Files
2026-05-26 17:36:34 +08:00

228 行
7.8 KiB
Groovy

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