9d7f09ff4b
模板是"默认就合规"的起点,所以它的每一处错误都会被复制到每个新仓。 ① 删掉 deploy:test 作业:它打印 TODO 然后返回成功 —— 网页上看起来部署过了,实际什么都没做。 那是假报成功,不是"还没做"。模板从此只做 CI,部署由项目另写独立 workflow。 ② gitea-prod-deploy.yml 里的两个 TODO 步骤同理,改成 exit 1 并在文件头写明"红的占位是设计"。 ③ 假绿四处: - gofmt -l 列出文件时退出码仍是 0 ⇒ 改成判输出是否为空; - 缺 package-lock.json 时回落 npm install ⇒ 改成直接失败(npm install 会重写锁文件); - GOFLAGS=-mod=mod ⇒ 改成 -mod=readonly,并加"编译前后比对依赖清单摘要"; - 识别不到任何构建入口时打印"跳过编译"然后成功 ⇒ 改成失败。 ④ 检出改成核对到**事件完整 SHA**(原来取的是"当时的分支 HEAD",事件之后又有人推一次就对不上了)。 ⑤ 预览加了三条断言:包里真的有 index.html、报告里每个地址都 200、不再产生 `.` 根成员 (原来会:通知发了、预览 404 而流水线显示成功)。 验证:模板 YAML 用 pyyaml 解析通过(build 模板 jobs=gate,build,无 deploy 作业; prod-deploy jobs=guard,publish 且只有 workflow_dispatch); new-project.sh 生成物里 grep '^ deploy' 计数为 0。
112 lines
5.0 KiB
YAML
112 lines
5.0 KiB
YAML
# <PROJECT> —— 生产发布(Gitea Actions,**只由人工发起**)
|
||
# 正本见 01-规范-Git与CI-CD(Gitea版).md §5
|
||
#
|
||
# ⚠️ 这个文件是"生产发布"的**唯一入口**。四条不许动的形状:
|
||
# 1. 触发**只有** workflow_dispatch —— 不存在任何自动触发路径(§4.1)
|
||
# 2. 必填 sha + confirm,第一步就校验,不合法直接红(§5.2 第二层)
|
||
# 3. concurrency.cancel-in-progress **必须** false —— 取消不是回滚,会留下换了一半的镜像(§5.4)
|
||
# 4. **不构建**。只搬运"测试验收过的那一份产物"(同 tar_sha256 / 同 image_id,§5.1)
|
||
#
|
||
# ⚠️ **占位步骤一律 `exit 1`,不许返回成功**(正本 §8)。
|
||
# 一个"打印 TODO 然后返回 0"的占位作业,会在网页上显示成一次成功的生产发布 ——
|
||
# 那比没有这个 workflow 更危险。所以本模板里未实现的步骤**故意是红的**:
|
||
# 你填完真实逻辑它才会绿。第一次手动发起时看到红,是设计,不是故障。
|
||
#
|
||
# 人工确认三层(§5.2):
|
||
# 第一层 平台:本 workflow 只能手动发起
|
||
# 第二层 参数:confirm 必须等于字面量 deploy-production
|
||
# 第三层 人:最后一步"切镜像"由本人在生产机执行(或本人确认脚本输出)
|
||
|
||
name: deploy-production
|
||
|
||
on:
|
||
workflow_dispatch:
|
||
inputs:
|
||
sha:
|
||
description: '要发布的短 sha(12 位,必须是已在测试环境验收过的那一份)'
|
||
required: true
|
||
confirm:
|
||
description: '确认执行生产发布,请输入字面量 deploy-production'
|
||
required: true
|
||
note:
|
||
description: '发布说明(对应 release issue / 验收记录链接)'
|
||
required: false
|
||
|
||
# ⚠️ 不要 cancel-in-progress
|
||
concurrency:
|
||
group: deploy-production
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
LANG: C.UTF-8
|
||
LC_ALL: C.UTF-8
|
||
|
||
jobs:
|
||
guard:
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 10
|
||
steps:
|
||
- name: 人工确认 + 参数校验
|
||
run: |
|
||
set -eu
|
||
SHA="${{ github.event.inputs.sha }}"
|
||
CONFIRM="${{ github.event.inputs.confirm }}"
|
||
echo "请求发布 sha=$SHA"
|
||
[ "$CONFIRM" = "deploy-production" ] || { echo "❌ confirm 不匹配,拒绝执行"; exit 1; }
|
||
printf '%s' "$SHA" | grep -Eq '^[0-9a-f]{12}$' || { echo "❌ sha 必须是 12 位小写十六进制"; exit 1; }
|
||
echo "$SHA" > /tmp/ci-sha12
|
||
echo "✅ 参数合法"
|
||
|
||
publish:
|
||
needs: [guard]
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 30
|
||
steps:
|
||
- name: 取产物并校验(不构建!)
|
||
run: |
|
||
set -eu
|
||
echo "❌ 本步骤尚未实现:占位不得返回成功(正本 §8)"
|
||
cat <<'EOF'
|
||
要填的真实逻辑(见 02-流程说明与Runbook.md §D):
|
||
① 从产物托管点取 release-<sha12>.json 与 <repo>-<sha12>.tar
|
||
② sha256sum <tar> 必须 == 清单 tar_sha256
|
||
③ 清单 sha12 必须 == 输入 sha
|
||
在填完之前,这条 workflow 会一直红 —— 这是**故意的**。
|
||
EOF
|
||
exit 1
|
||
|
||
- name: 交付到生产机(只传文件,不切镜像)
|
||
run: |
|
||
set -eu
|
||
echo "❌ 本步骤尚未实现:占位不得返回成功(正本 §8)"
|
||
cat <<'EOF'
|
||
要填的真实逻辑:
|
||
① scp 到 <PROD_HOST>:<INCOMING_DIR>/
|
||
② ⚠️ 凭据只走 stdin / 文件,绝不进命令行参数(会进 ps)
|
||
③ ⚠️ ssh 是非交互 shell,不读 /etc/profile:远端命令要显式补 PATH
|
||
在填完之前,这条 workflow 会一直红 —— 这是**故意的**。
|
||
EOF
|
||
exit 1
|
||
|
||
- name: 输出给本人的"下一步"(第三层确认在这里)
|
||
run: |
|
||
cat <<'EOF'
|
||
|
||
✅ 产物已送达生产机。**最后一步由本人在生产机执行**:
|
||
|
||
exec 9>/var/lock/dobotai-deploy.lock
|
||
flock -n 9 || { echo "另一个部署正在进行"; exit 1; }
|
||
sha256sum /opt/dobotai/container/incoming/<repo>-<sha12>.tar # == 清单 tar_sha256
|
||
docker tag <current> <repo>:rollback-<old12>-before-<sha12> # 回滚用
|
||
docker load -i /opt/dobotai/container/incoming/<repo>-<sha12>.tar
|
||
docker image inspect --format '{{.Id}}' <repo>:sha-<sha12> # == 清单 image_id
|
||
# 迁移(若有)必须在切镜像**之前**完成并读回(正本 §5.5)
|
||
cd /opt/dobotai/container && docker compose --env-file runtime.env -f compose.yaml \
|
||
up -d --no-deps --force-recreate <ctr>
|
||
curl -fsS http://127.0.0.1:8090/healthz # {"status":"ok"}
|
||
curl -fsS http://127.0.0.1:8090/version # commit_sha == <sha12>
|
||
|
||
最后的门:**本人在生产机器人上发一条真实消息**,再回读 turns.agent_id。
|
||
回滚:bash /opt/dobotai/container/ops/rollback_dobotai_prod.sh
|
||
⚠️「取消流水线」不是回滚。
|
||
EOF |