Jenkinsfile 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // note-manager Jenkins Pipeline
  2. // 项目:Go/Gin + SQLite(CGO),部署端口 9090
  3. pipeline {
  4. agent any
  5. environment {
  6. // ─── 基础配置 ───
  7. APP_NAME = 'note-manager'
  8. GO_VERSION = '1.21'
  9. GIT_REPO = 'https://git.cnbugs.com/AI-Agent/note-manager.git'
  10. GIT_BRANCH = 'main'
  11. // ─── 部署配置(敏感信息用 Jenkins Credentials)───
  12. DEPLOY_HOST = credentials('deploy-host')
  13. DEPLOY_USER = credentials('deploy-user')
  14. DEPLOY_PATH = '/vol1/Project/note-manager'
  15. SERVICE_PORT = '9090'
  16. // ─── 构建产物 ───
  17. BUILD_DIR = 'dist'
  18. }
  19. options {
  20. timestamps()
  21. timeout(time: 15, unit: 'MINUTES')
  22. disableConcurrentBuilds()
  23. buildDiscarder(logRotator(numToKeepStr: '10'))
  24. }
  25. stages {
  26. // ━━━━━━━━ 拉取代码 ━━━━━━━━
  27. stage('Checkout') {
  28. steps {
  29. git branch: "${GIT_BRANCH}",
  30. url: "${GIT_REPO}",
  31. credentialsId: 'git-cnbugs-credentials'
  32. sh 'echo "分支: ${GIT_BRANCH} | 提交: $(git rev-parse --short HEAD)"'
  33. }
  34. }
  35. // ━━━━━━━━ 代码检查 ━━━━━━━━
  36. stage('Lint') {
  37. steps {
  38. sh '''
  39. docker run --rm \
  40. -v "$PWD":/app \
  41. -w /app \
  42. golang:${GO_VERSION}-alpine \
  43. sh -c "go fmt ./... && go vet ./..."
  44. '''
  45. }
  46. }
  47. // ━━━━━━━━ 单元测试 ━━━━━━━━
  48. stage('Test') {
  49. steps {
  50. sh '''
  51. docker run --rm \
  52. -v "$PWD":/app \
  53. -w /app \
  54. -e CGO_ENABLED=1 \
  55. golang:${GO_VERSION} \
  56. go test -v -race -coverprofile=coverage.out ./...
  57. '''
  58. }
  59. post {
  60. always {
  61. archiveArtifacts artifacts: 'coverage.out', allowEmptyArchive: true
  62. }
  63. }
  64. }
  65. // ━━━━━━━━ 编译构建 ━━━━━━━━
  66. stage('Build') {
  67. steps {
  68. sh '''
  69. rm -rf ${BUILD_DIR} && mkdir -p ${BUILD_DIR}
  70. VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
  71. BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
  72. COMMIT=$(git rev-parse --short HEAD)
  73. echo "━━━ 构建信息 ━━━"
  74. echo "版本: ${VERSION}"
  75. echo "提交: ${COMMIT}"
  76. echo "时间: ${BUILD_TIME}"
  77. # go-sqlite3 需要 CGO_ENABLED=1,用完整 golang 镜像确保 gcc 可用
  78. docker run --rm \
  79. -v "$PWD":/app \
  80. -w /app \
  81. -e CGO_ENABLED=1 \
  82. -e GOOS=linux \
  83. -e GOARCH=amd64 \
  84. golang:${GO_VERSION} \
  85. go build \
  86. -ldflags="-s -w \
  87. -X main.Version=${VERSION} \
  88. -X main.BuildTime=${BUILD_TIME} \
  89. -X main.Commit=${COMMIT}" \
  90. -o ${BUILD_DIR}/${APP_NAME} \
  91. ./...
  92. file ${BUILD_DIR}/${APP_NAME}
  93. ls -lh ${BUILD_DIR}/${APP_NAME}
  94. '''
  95. }
  96. }
  97. // ━━━━━━━━ 打包发布 ━━━━━━━━
  98. stage('Package') {
  99. steps {
  100. sh '''
  101. VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
  102. mkdir -p ${BUILD_DIR}/package
  103. # 复制运行所需文件
  104. cp ${BUILD_DIR}/${APP_NAME} ${BUILD_DIR}/package/
  105. cp -r config ${BUILD_DIR}/package/
  106. cp -r web ${BUILD_DIR}/package/
  107. # 生成启动脚本
  108. cat > ${BUILD_DIR}/package/start.sh << 'STARTSCRIPT'
  109. #!/bin/bash
  110. APP_DIR="$(cd "$(dirname "$0")" && pwd)"
  111. cd "$APP_DIR"
  112. export PORT=${SERVICE_PORT}
  113. export DB_PATH=data/notes.db
  114. export UPLOAD_DIR=uploads
  115. export ADMIN_PASS=${ADMIN_PASS:-admin123}
  116. mkdir -p data uploads
  117. echo "启动 note-manager ..."
  118. ./note-manager
  119. STARTSCRIPT
  120. chmod +x ${BUILD_DIR}/package/start.sh
  121. # 打 tar 包
  122. cd ${BUILD_DIR}/package
  123. tar czf ${WORKSPACE}/${APP_NAME}-${VERSION}-linux-amd64.tar.gz .
  124. cd ${WORKSPACE}
  125. echo "━━━ 发布包 ━━━"
  126. ls -lh ${APP_NAME}-${VERSION}-linux-amd64.tar.gz
  127. '''
  128. }
  129. post {
  130. success {
  131. archiveArtifacts artifacts: "${APP_NAME}-*-linux-amd64.tar.gz", fingerprint: true
  132. }
  133. }
  134. }
  135. // ━━━━━━━━ 部署(需手动确认)━━━━━━━━
  136. stage('Deploy') {
  137. when {
  138. branch 'main'
  139. }
  140. input {
  141. message '确认部署到生产环境?'
  142. ok '部署'
  143. }
  144. steps {
  145. sh '''
  146. VERSION=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)
  147. PACKAGE="${APP_NAME}-${VERSION}-linux-amd64.tar.gz"
  148. echo "━━━ 部署到 ${DEPLOY_HOST} ━━━"
  149. # 上传发布包
  150. scp -o StrictHostKeyChecking=no \
  151. ${PACKAGE} \
  152. ${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/${PACKAGE}
  153. # 远程执行部署
  154. ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} "bash -s" << REMOTE_DEPLOY
  155. set -e
  156. echo "停止旧服务..."
  157. pkill -f 'note-manager' || true
  158. sleep 1
  159. echo "备份旧版本..."
  160. if [ -f ${DEPLOY_PATH}/note-manager ]; then
  161. cp ${DEPLOY_PATH}/note-manager ${DEPLOY_PATH}/note-manager.bak
  162. fi
  163. echo "解压新版本..."
  164. mkdir -p ${DEPLOY_PATH}
  165. cd ${DEPLOY_PATH}
  166. tar xzf /tmp/${PACKAGE}
  167. rm -f /tmp/${PACKAGE}
  168. echo "启动服务..."
  169. cd ${DEPLOY_PATH}
  170. PORT=${SERVICE_PORT} nohup ./note-manager > ${DEPLOY_PATH}/app.log 2>&1 &
  171. sleep 2
  172. if curl -sf http://localhost:${SERVICE_PORT}/ > /dev/null 2>&1; then
  173. echo "✅ 服务启动成功!端口: ${SERVICE_PORT}"
  174. else
  175. echo "❌ 服务启动失败,查看日志:"
  176. tail -20 ${DEPLOY_PATH}/app.log
  177. exit 1
  178. fi
  179. REMOTE_DEPLOY
  180. echo "━━━ 部署完成 ━━━"
  181. '''
  182. }
  183. }
  184. }
  185. post {
  186. success {
  187. echo '✅ Pipeline 执行成功'
  188. }
  189. failure {
  190. echo '❌ Pipeline 执行失败'
  191. }
  192. always {
  193. cleanWs()
  194. }
  195. }
  196. }