.gitlab-ci.yml文件定义 CI/CD 流水线
一个python程序的管道示范:
yaml
stages: # 定义流水线阶段列表,按顺序执行
- build # 第一个阶段:构建(build)
- deploy # 第二个阶段:部署(deploy)
# 默认变量,可通过 rules 覆盖 # 说明:下面是一些默认的 CI/CD 变量,必要时可在 job 中覆盖
variables: # 定义整个流水线通用的环境变量
# Telegram Bot # 注释:这几项是 Telegram 机器人通知相关配置
TELEGRAM_TOKEN: "test" # Telegram Bot 的 token(建议放到 GitLab CI 变量里)
TELEGRAM_CHAT_ID: "-4706391872" # 接收通知的 Telegram 群/频道 ID
IMAGE_NAME: "registry.gbox.one/ai-poker/video_talk_api" # 要构建和推送的 Docker 镜像完整名称
TEST_SERVER_HOST: "172.26.97.44" # 测试服务器的 IP 地址
TEST_SERVER_USER: "root" # 测试服务器的登录用户
.common_failure_notification: &common_failure_notification # 定义一个通用模板(锚点),用于“失败时通知”的 job 复用
after_script: # 在 job 主脚本执行结束后,无论成功失败都会执行这里的脚本
- | # 使用多行脚本块,交给 Shell 执行
if [[ "$CI_JOB_STATUS" != "success" ]]; then # 如果当前 job 状态不是 success(说明失败或被取消)
MSG=" # 定义要发送给 Telegram 的消息内容(多行字符串)
❌ *${CI_PROJECT_NAME} 打包失败*
📁 项目:*${CI_PROJECT_NAME}*
🔧 分支:*${CI_COMMIT_REF_NAME}*
🎯 Job:*${CI_JOB_NAME}*
❌ 状态:*${CI_JOB_STATUS}*
🪪 提交者:*${CI_COMMIT_AUTHOR}*
🔗 [查看详情](${CI_JOB_URL})
"
curl -s -X POST https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendPhoto \ # 调用 Telegram Bot API,发送带图片的通知
# 上一行的“\”表示命令续行
-d photo=https://raw.githubusercontent.com/coder-tye/media/refs/heads/main/banner.png \ # 指定要附带发送的图片 URL
# 继续上一行命令参数
-d chat_id=${TELEGRAM_CHAT_ID} \ # 指定发送到哪一个 chat(群/频道)
# 继续上一行命令参数
-d parse_mode=Markdown \ # 启用 Markdown 格式解析,支持加粗/链接等
# 继续上一行命令参数
-d caption="$MSG" # 把上面拼好的 MSG 作为图片说明文字发送
fi # 条件结束:只有失败时才会发送通知
.common_all_notification: &common_all_notification # 定义另一个通用模板,用于“部署成功或失败都通知”
after_script: # job 完成后统一发送部署结果到 Telegram
- | # 多行 Shell 脚本块
if [[ "$CI_JOB_STATUS" == "success" ]]; then # 如果 job 成功
MSG=" # 定义成功时的通知内容
🚀 *${CI_PROJECT_NAME} ${CI_ENV} 部署完成*
📁 项目:*${CI_PROJECT_NAME}*
🔧 分支:*${CI_COMMIT_REF_NAME}*
🔗 [访问部署环境](${TARGET_URL})
"
else # 否则(失败或被取消)
MSG=" # 定义失败时的通知内容
❌ *${CI_PROJECT_NAME} ${CI_ENV} 部署失败!*
📁 项目:*${CI_PROJECT_NAME}*
🛠️ 分支:*${CI_COMMIT_REF_NAME}*
🚨 Job:*${CI_JOB_NAME}*
🔗 [查看日志](${CI_JOB_URL})"
fi # 结束 if 判断
curl -s -X POST https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendPhoto \ # 无论成功失败,都会发送一条带图片的通知
-d photo=https://raw.githubusercontent.com/coder-tye/media/refs/heads/main/banner.png \ # 使用统一的 banner 图片
-d chat_id=${TELEGRAM_CHAT_ID} \ # 指定接收通知的 chat
-d parse_mode=Markdown \ # 启用 Markdown 渲染
-d caption="$MSG" # 发送上面构造的 MSG 作为说明文字
build-docker-image: # 定义一个名为 build-docker-image 的 job(构建 Docker 镜像)
stage: build # 这个 job 属于 build 阶段
script: # 该 job 的主要执行脚本
- echo "🐳 构建 Docker 镜像..." # 打印提示,说明正在构建 Docker 镜像
- | # 多行 Shell 命令块
docker buildx build -f Dockerfile.test \ # 使用 buildx 和 Dockerfile.test 构建镜像
-t "$IMAGE_NAME:latest" \ # 给镜像打一个 latest 标签
-t "$IMAGE_NAME:$CI_COMMIT_SHORT_SHA" \ # 再打一个短提交 SHA 作为版本标签
. # 构建上下文为当前目录
- docker push "$IMAGE_NAME:latest" # 推送 latest 标签的镜像到镜像仓库
- docker push "$IMAGE_NAME:$CI_COMMIT_SHORT_SHA" # 推送带提交哈希标签的镜像,便于回滚和追踪
extends: .common_failure_notification # 继承上面定义的“失败时 Telegram 通知”模板
deploy-to-test: # 定义部署到测试环境的 job
stage: deploy # 这个 job 属于 deploy 阶段
dependencies: [] # ❌ 不自动下载 artifacts # 显式禁止从其他 job 下载构建产物(此处只依赖镜像,不靠 artifacts)
when: on_success # 仅在之前阶段的 job 全部成功时才执行该 job
needs: ["build-docker-image"] # 声明依赖 build-docker-image,确保构建 job 成功后再执行部署
#only: # 注释掉的 only 规则
# - release # 如果启用,则表示只在 release 分支/tag 上执行这个部署 job
script: # 部署脚本
- | # 多行 Shell 命令块
echo "🚀 正在部署到测试服务器..." # 提示开始部署
ssh "$TEST_SERVER_USER@$TEST_SERVER_HOST" << EOF # 通过 SSH 连到测试服务器,并执行下面的 bash 脚本(heredoc)
docker pull $IMAGE_NAME:latest # 在测试服务器上拉取最新镜像
docker stop video-talk || true # 停掉旧的容器(如果不存在则忽略错误)
docker rm video-talk || true # 删除旧容器(如果不存在则忽略错误)
docker run -d \ # 以后台方式启动新容器
--restart unless-stopped \ # 设置容器自动重启策略
-v /data/video_talk/.env:/app/.env \ # 挂载服务器上的 .env 文件到容器内,提供配置
-v /data/video_talk/logs:/app/logs \ # 挂载日志目录,方便持久化日志
-p 8199:8000 \ # 映射宿主机 8199 端口到容器 8000 端口
--name video-talk \ # 指定容器名称为 video-talk
$IMAGE_NAME:latest # 使用刚刚构建并推送的 latest 镜像启动容器
EOF # 结束 heredoc,SSH 远程命令执行完毕
variables: # 仅在这个 job 中生效的变量
TARGET_URL: "https://tailk.masterpoker.pro" # 部署好的测试环境访问地址,用于通知消息中展示
CI_ENV: "测试环境" # 当前部署环境名称,用于通知(比如“测试环境”)
extends: .common_all_notification # 继承“成功/失败都通知”的通用模板
一个node.js管道示范:
yaml
stages: # 定义流水线的阶段列表,按顺序执行
- build # 第一阶段:build(打包构建)
- deploy # 第二阶段:deploy(部署)
variables: # 定义全局变量,所有 job 默认都能用
TELEGRAM_TOKEN: key # Telegram 机器人 token(示例,实际建议用 CI 变量存)
TELEGRAM_CHAT_ID: -4706391872 # Telegram 聊天/频道 ID,用于发送通知
# ================= 通用 build 脚本 =================
.default_build_script: &build_script # 定义一个锚点 build_script,后面 job 的 script 可以复用这段命令
- source ~/.nvm/nvm.sh # 加载 nvm 环境脚本,让当前 shell 支持 nvm 命令
- nvm use 20 # 切换到 Node.js 20 版本
- node --version # 打印当前 Node.js 版本,方便在 CI 日志中确认
- yarn install --frozen-lockfile # 安装依赖,严格按照 yarn.lock,不允许变更锁文件
- yarn run build # 执行前端项目的打包构建命令,生成 dist 等产物
# ================= 通用 deploy 脚本 =================
.default_deploy_script: &deploy_script | # 定义一个部署脚本锚点 deploy_script,作为整段 shell 脚本复用
set -e # 遇到任意命令出错则立即退出,保证部署过程出错不会悄悄继续
echo "🚀 部署开始..." # 输出部署开始提示
DEPLOY_TIMESTAMP=$(date +%Y%m%d-%H%M%S) # 生成当前时间戳,用于备份目录命名(如 20251203-153000)
DEPLOY_BACKUP="${DEPLOY_BACKUP_PATH}${CI_PROJECT_NAME}-$DEPLOY_TIMESTAMP" # 拼接备份目录完整路径
ssh "$HOSTNAME" << EOF # 通过 SSH 登录到远程服务器,执行下面的多行命令
if [[ -d "$DEPLOY_PATH" ]]; then # 判断目标部署目录是否存在
echo "🗃️ 备份旧版本到 $DEPLOY_BACKUP" # 提示要备份旧版本
mv "$DEPLOY_PATH" "$DEPLOY_BACKUP" # 把当前部署目录移动到备份目录
fi
mkdir -p "$DEPLOY_PATH" # 创建新的部署目录(如果不存在就创建)
EOF # 结束 SSH heredoc,多行远程命令结束
rsync -avz dist/ "$HOSTNAME:$DEPLOY_PATH/" # 使用 rsync 把本地 dist/ 目录同步到远程部署目录
echo "✅ 部署完成" # 输出部署完成提示
.common_failure_notification: &common_failure_notification # 定义一个“失败通知”模板锚点,供 job 继承
after_script: # 在 job 主脚本结束后执行,无论成功失败都会执行这里
- | # 多行 shell 脚本块
if [[ "$CI_JOB_STATUS" != "success" ]]; then # 如果当前 job 状态不是 success(说明失败或被取消)
MSG=" # 定义要发送到 Telegram 的消息内容(多行字符串)
❌ *${CI_PROJECT_NAME} 打包失败*
📁 项目:*${CI_PROJECT_NAME}*
🔧 分支:*${CI_COMMIT_REF_NAME}*
🎯 Job:*${CI_JOB_NAME}*
❌ 状态:*${CI_JOB_STATUS}*
🪪 提交者:*${CI_COMMIT_AUTHOR}*
🔗 [查看详情](${CI_JOB_URL})
"
curl -s -X POST https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendPhoto \ # 调用 Telegram Bot API 发送图片消息
-d photo=https://raw.githubusercontent.com/coder-tye/media/refs/heads/main/banner.png \ # 指定要附带的 banner 图片
-d chat_id=${TELEGRAM_CHAT_ID} \ # 指定推送到哪个聊天/频道
-d parse_mode=Markdown \ # 启用 Markdown 渲染,让加粗/链接生效
-d caption="$MSG" # 把上面构造的 MSG 作为图片说明文字发送
fi # 结束 if 条件判断
.common_all_notification: &common_all_notification # 定义“成功/失败都通知”的模板锚点
after_script: # job 结束后统一执行,用于部署相关结果通知
- | # 多行 shell 脚本块
if [[ "$CI_JOB_STATUS" == "success" ]]; then # 如果 job 成功
MSG=" # 定义成功时的通知内容
🚀 *${CI_PROJECT_NAME} ${CI_ENV} 部署完成*
📁 项目:*${CI_PROJECT_NAME}*
🔧 分支:*${CI_COMMIT_REF_NAME}*
🔗 [访问部署环境](${TARGET_URL})
"
else # 否则视为部署失败
MSG=" # 定义失败时的通知内容
❌ *${CI_PROJECT_NAME} ${CI_ENV} 部署失败!*
📁 项目:*${CI_PROJECT_NAME}*
🛠️ 分支:*${CI_COMMIT_REF_NAME}*
🚨 Job:*${CI_JOB_NAME}*
🔗 [查看日志](${CI_JOB_URL})"
fi # 结束成功/失败判断
curl -s -X POST https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendPhoto \ # 不论成功失败都会发送一条带图片的通知
-d photo=https://raw.githubusercontent.com/coder-tye/media/refs/heads/main/banner.png \ # 使用统一 banner 图片
-d chat_id=${TELEGRAM_CHAT_ID} \ # 发送到指定 Telegram 聊天/频道
-d parse_mode=Markdown \ # 使用 Markdown 解析
-d caption="$MSG" # 发送上面构建好的 MSG 内容
# ================= 打包 =================
# 专业版测试环境
build_pro_test: # 定义一个名为 build_pro_test 的 job,用于构建“专业版测试环境”的前端
stage: build # 这个 job 属于 build 阶段
script: *build_script # 使用前面定义的通用构建脚本锚点 build_script
artifacts: # 指定本 job 产生的构建产物,以便后续 job(如部署)使用
paths: # 要打包保存的文件/目录路径列表
- $DIST_FILE # 使用 DIST_FILE 变量指定的构建产物路径(例如 dist/ 或 zip 文件)
expire_in: 1 week # 构建产物在 GitLab 中保存 1 周后自动过期删除
extends: .common_failure_notification # 继承“失败时发送 Telegram 通知”的模板
# ================= 部署流程 =================
deploy_pro_test: # 定义一个名为 deploy_pro_test 的 job,用于部署到测试环境
stage: deploy # 这个 job 属于 deploy 阶段
when: on_success # 仅当前面依赖的 job 全部成功时才会执行本 job
needs: ["build_pro_test"] # 声明依赖 build_pro_test job,以确保构建完成后再部署(并可并行优化流水线)
variables: # 仅在这个部署 job 中生效的变量
HOSTNAME: "root@172.26.97.44" # SSH 目标地址(用户名 + 服务器 IP)
DEPLOY_PATH: "/data/video_talk/html/" # 部署的目标目录(新版本会同步到这里)
DEPLOY_BACKUP_PATH: "/data/video_talk/html_bak/" # 旧版本备份目录前缀
TARGET_URL: "https://talk.masterpoker.pro" # 部署完成后对外访问的 URL,用于通知里给链接
CI_ENV: "测试环境" # 当前部署环境名称,通知中显示“测试环境”
script: *deploy_script # 使用通用部署脚本锚点 deploy_script(上面那一大段 SSH+rsync)
extends: .common_all_notification # 继承“成功/失败都发送 Telegram 通知”的模板