feat: add Jenkins CI/CD pipeline
This commit is contained in:
Vendored
+227
@@ -0,0 +1,227 @@
|
||||
// note-manager Jenkins Pipeline
|
||||
// 项目:Go/Gin + SQLite(CGO),部署端口 9090
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
// ─── 基础配置 ───
|
||||
APP_NAME = 'note-manager'
|
||||
GO_VERSION = '1.21'
|
||||
GIT_REPO = 'https://git.cnbugs.com/AI-Agent/note-manager.git'
|
||||
GIT_BRANCH = 'main'
|
||||
// ─── 部署配置(敏感信息用 Jenkins Credentials)───
|
||||
DEPLOY_HOST = credentials('deploy-host')
|
||||
DEPLOY_USER = credentials('deploy-user')
|
||||
DEPLOY_PATH = '/vol1/Project/note-manager'
|
||||
SERVICE_PORT = '9090'
|
||||
// ─── 构建产物 ───
|
||||
BUILD_DIR = 'dist'
|
||||
}
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
timeout(time: 15, unit: 'MINUTES')
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
// ━━━━━━━━ 拉取代码 ━━━━━━━━
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
git branch: "${GIT_BRANCH}",
|
||||
url: "${GIT_REPO}",
|
||||
credentialsId: 'git-cnbugs-credentials'
|
||||
sh 'echo "分支: ${GIT_BRANCH} | 提交: $(git rev-parse --short HEAD)"'
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━━━━━━ 代码检查 ━━━━━━━━
|
||||
stage('Lint') {
|
||||
steps {
|
||||
sh '''
|
||||
docker run --rm \
|
||||
-v "$PWD":/app \
|
||||
-w /app \
|
||||
golang:${GO_VERSION}-alpine \
|
||||
sh -c "go fmt ./... && go vet ./..."
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━━━━━━ 单元测试 ━━━━━━━━
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh '''
|
||||
docker run --rm \
|
||||
-v "$PWD":/app \
|
||||
-w /app \
|
||||
-e CGO_ENABLED=1 \
|
||||
golang:${GO_VERSION} \
|
||||
go test -v -race -coverprofile=coverage.out ./...
|
||||
'''
|
||||
}
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts artifacts: 'coverage.out', allowEmptyArchive: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━━━━━━ 编译构建 ━━━━━━━━
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh '''
|
||||
rm -rf ${BUILD_DIR} && mkdir -p ${BUILD_DIR}
|
||||
|
||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
|
||||
BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
|
||||
echo "━━━ 构建信息 ━━━"
|
||||
echo "版本: ${VERSION}"
|
||||
echo "提交: ${COMMIT}"
|
||||
echo "时间: ${BUILD_TIME}"
|
||||
|
||||
# go-sqlite3 需要 CGO_ENABLED=1,用完整 golang 镜像确保 gcc 可用
|
||||
docker run --rm \
|
||||
-v "$PWD":/app \
|
||||
-w /app \
|
||||
-e CGO_ENABLED=1 \
|
||||
-e GOOS=linux \
|
||||
-e GOARCH=amd64 \
|
||||
golang:${GO_VERSION} \
|
||||
go build \
|
||||
-ldflags="-s -w \
|
||||
-X main.Version=${VERSION} \
|
||||
-X main.BuildTime=${BUILD_TIME} \
|
||||
-X main.Commit=${COMMIT}" \
|
||||
-o ${BUILD_DIR}/${APP_NAME} \
|
||||
./...
|
||||
|
||||
file ${BUILD_DIR}/${APP_NAME}
|
||||
ls -lh ${BUILD_DIR}/${APP_NAME}
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━━━━━━ 打包发布 ━━━━━━━━
|
||||
stage('Package') {
|
||||
steps {
|
||||
sh '''
|
||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
|
||||
|
||||
mkdir -p ${BUILD_DIR}/package
|
||||
|
||||
# 复制运行所需文件
|
||||
cp ${BUILD_DIR}/${APP_NAME} ${BUILD_DIR}/package/
|
||||
cp -r config ${BUILD_DIR}/package/
|
||||
cp -r web ${BUILD_DIR}/package/
|
||||
|
||||
# 生成启动脚本
|
||||
cat > ${BUILD_DIR}/package/start.sh << 'STARTSCRIPT'
|
||||
#!/bin/bash
|
||||
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$APP_DIR"
|
||||
|
||||
export PORT=${SERVICE_PORT}
|
||||
export DB_PATH=data/notes.db
|
||||
export UPLOAD_DIR=uploads
|
||||
export ADMIN_PASS=${ADMIN_PASS:-admin123}
|
||||
|
||||
mkdir -p data uploads
|
||||
|
||||
echo "启动 note-manager ..."
|
||||
./note-manager
|
||||
STARTSCRIPT
|
||||
chmod +x ${BUILD_DIR}/package/start.sh
|
||||
|
||||
# 打 tar 包
|
||||
cd ${BUILD_DIR}/package
|
||||
tar czf ${WORKSPACE}/${APP_NAME}-${VERSION}-linux-amd64.tar.gz .
|
||||
cd ${WORKSPACE}
|
||||
|
||||
echo "━━━ 发布包 ━━━"
|
||||
ls -lh ${APP_NAME}-${VERSION}-linux-amd64.tar.gz
|
||||
'''
|
||||
}
|
||||
post {
|
||||
success {
|
||||
archiveArtifacts artifacts: "${APP_NAME}-*-linux-amd64.tar.gz", fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━━━━━━ 部署(需手动确认)━━━━━━━━
|
||||
stage('Deploy') {
|
||||
when {
|
||||
branch 'main'
|
||||
}
|
||||
input {
|
||||
message '确认部署到生产环境?'
|
||||
ok '部署'
|
||||
}
|
||||
steps {
|
||||
sh '''
|
||||
VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
|
||||
PACKAGE="${APP_NAME}-${VERSION}-linux-amd64.tar.gz"
|
||||
|
||||
echo "━━━ 部署到 ${DEPLOY_HOST} ━━━"
|
||||
|
||||
# 上传发布包
|
||||
scp -o StrictHostKeyChecking=no \
|
||||
${PACKAGE} \
|
||||
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/${PACKAGE}
|
||||
|
||||
# 远程执行部署
|
||||
ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} "bash -s" << REMOTE_DEPLOY
|
||||
set -e
|
||||
echo "停止旧服务..."
|
||||
pkill -f 'note-manager' || true
|
||||
sleep 1
|
||||
|
||||
echo "备份旧版本..."
|
||||
if [ -f ${DEPLOY_PATH}/note-manager ]; then
|
||||
cp ${DEPLOY_PATH}/note-manager ${DEPLOY_PATH}/note-manager.bak
|
||||
fi
|
||||
|
||||
echo "解压新版本..."
|
||||
mkdir -p ${DEPLOY_PATH}
|
||||
cd ${DEPLOY_PATH}
|
||||
tar xzf /tmp/${PACKAGE}
|
||||
rm -f /tmp/${PACKAGE}
|
||||
|
||||
echo "启动服务..."
|
||||
cd ${DEPLOY_PATH}
|
||||
PORT=${SERVICE_PORT} nohup ./note-manager > ${DEPLOY_PATH}/app.log 2>&1 &
|
||||
|
||||
sleep 2
|
||||
if curl -sf http://localhost:${SERVICE_PORT}/ > /dev/null 2>&1; then
|
||||
echo "✅ 服务启动成功!端口: ${SERVICE_PORT}"
|
||||
else
|
||||
echo "❌ 服务启动失败,查看日志:"
|
||||
tail -20 ${DEPLOY_PATH}/app.log
|
||||
exit 1
|
||||
fi
|
||||
REMOTE_DEPLOY
|
||||
|
||||
echo "━━━ 部署完成 ━━━"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
echo '✅ Pipeline 执行成功'
|
||||
}
|
||||
failure {
|
||||
echo '❌ Pipeline 执行失败'
|
||||
}
|
||||
always {
|
||||
cleanWs()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user