// 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() } } }