Kaynağa Gözat

feat: add Jenkins CI/CD pipeline

AI Agent 4 gün önce
ebeveyn
işleme
f0eb822e68
1 değiştirilmiş dosya ile 227 ekleme ve 0 silme
  1. 227 0
      Jenkinsfile

+ 227 - 0
Jenkinsfile

@@ -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()
+        }
+    }
+}