228 lines
7.8 KiB
Groovy
228 lines
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()
|
||
}
|
||
}
|
||
}
|