Files
姚章浩 9d7f09ff4b fix(template): 通用 CI 模板改成 CI-only;去掉假成功占位;修四处会造成假绿的写法
模板是"默认就合规"的起点,所以它的每一处错误都会被复制到每个新仓。

① 删掉 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。
2026-10-12 00:11:40 +08:00

112 lines
5.0 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# <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