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