@@ -5,9 +5,8 @@ description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/C
# Slark CI/CD runbook
# Slark CI/CD runbook
驱动 Slark 从改代码到上线的端到端流程。环境身份与发布门禁 见仓库文档
驱动 Slark 从改代码到上线的端到端流程。权威流程与完整细节 见仓库文档
`docs/cicd-staging-production.md` ;当前 governed staging 候选入口以仓库脚本契约和历史成功回执为准 。
`docs/cicd-staging-production.md` (本技能命令与之一致);本文件是可直接照做的分阶段清单 。
该文档中直接运行 `deploy-staging.sh` 的示例尚未列出当前脚本要求的不可变候选材料。
仓库:`qiudl/qiu-slark` (本机克隆通常在 `/Users/donglinlai/coding/slark` )。所有命令在**仓库根**执行。
仓库:`qiudl/qiu-slark` (本机克隆通常在 `/Users/donglinlai/coding/slark` )。所有命令在**仓库根**执行。
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
`ai-proj` 相关命令加 `GODEBUG=netdns=go` 前缀。
@@ -22,8 +21,8 @@ description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/C
**每次都要人审 ** ;命中审批闸就**等人批**,绝不绕过、绝不把这些 key 加进 auto-approve 白名单。
**每次都要人审 ** ;命中审批闸就**等人批**,绝不绕过、绝不把这些 key 加进 auto-approve 白名单。
- staging 与生产硬隔离:staging 脚本带生产 IP 拒运行护栏、不 push origin、不反向 rsync。
- staging 与生产硬隔离:staging 脚本带生产 IP 拒运行护栏、不 push origin、不反向 rsync。
- 当前 Slark staging 权威目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`
- 当前 Slark staging 权威目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e`
( 文档记录 Name=`slark-staging` ; 2026-10-23 只读 核验公网 IP `15.135.112.181` )。公网 IP 自动分配,发布前必须按
( Name=`slark-staging` , 2026-08-18 核验公网 IP `15.135.112.181` )。公网 IP 自动分配,发布前必须按
实例 ID 从 AWS 控制面 重查并确认 Name/状态/IP ;不得凭历史 IP 或 SSH 可达性判断发布 目标。
实例 ID 重查并确认 Name/状态;不得凭历史 IP 判断 目标。
- `82.157.141.202` 是 ai-proj 预生产宿主机。其 `/opt/slark-staging` cohost 是隔离的 Slark 兼容验收栈,
- `82.157.141.202` 是 ai-proj 预生产宿主机。其 `/opt/slark-staging` cohost 是隔离的 Slark 兼容验收栈,
但不是 Slark 独立预生产;只有任务明确指定 cohost 时才能使用,不得作为普通 Slark staging 的默认目标。
但不是 Slark 独立预生产;只有任务明确指定 cohost 时才能使用,不得作为普通 Slark staging 的默认目标。
- staging 发布若影响 `packages/daemon` 、`packages/runtime` 、Server/daemon 公共协议或 Desktop 公共运行链,
- staging 发布若影响 `packages/daemon` 、`packages/runtime` 、Server/daemon 公共协议或 Desktop 公共运行链,
@@ -76,8 +75,7 @@ git branch -D <feature-branch> # 删本地(先切走)
### 3.1 当前权威路径:Slark 独立 staging
### 3.1 当前权威路径:Slark 独立 staging
稳定目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e` ( Name=`slark-staging` )。它使用自动分配
稳定目标是 AWS `ap-southeast-2` 实例 `i-013e5ca3fdfddc13e` ( Name=`slark-staging` )。它使用自动分配
公网 IP,因此每次部署前先查询当前 IP。 `15.135.112.181` 是 2026-10-23 的只读核验值,
公网 IP,因此每次部署前先查询当前 IP; 2026-08-18 核验值为 `15.135.112.181` :
不是可直接用于将来发布的固定地址:
``` bash
``` bash
aws ec2 describe-instances --region ap-southeast-2 \
aws ec2 describe-instances --region ap-southeast-2 \
@@ -85,93 +83,20 @@ aws ec2 describe-instances --region ap-southeast-2 \
--query 'Reservations[0].Instances[0].{Name:Tags[?Key==`Name`]|[0].Value,State:State.Name,PublicIp:PublicIpAddress}' \
--query 'Reservations[0].Instances[0].{Name:Tags[?Key==`Name`]|[0].Value,State:State.Name,PublicIp:PublicIpAddress}' \
--output table
--output table
# 只使用上一步 AWS 返回且已核对 Name=slark-staging、State=running 的公网 IP。
export STAGING_IP = '<verified-public-ip>'
export STAGING_SSH_KEY = ~/.ssh/cloud-server-syd.pem
export STAGING_SSH_KEY = ~/.ssh/cloud-server-syd.pem
export STAGING_REMOTE = " ubuntu@ $STAGING_IP "
export STAGING_REMOTE = ubuntu@15.135.112.181
export STAGING_REMOTE_IP = " $STAGING_IP "
export STAGING_REMOTE_IP = 15.135.112.181
export STAGING_BASE = " http:// $STAGING_IP "
export STAGING_BASE = http://15.135.112.181
# 使用 Slark 应用内的 project ID,不是 ai-proj 的数字项目/需求/任务 ID。
export SLARK_RELEASE_PROJECT_ID = '<verified-slark-app-project-id>'
export SLARK_RELEASE_TASK_ID = 'REQ-...'
export SLARK_RELEASE_ID = 'release-staging-<40-sha>-<unique-attempt>'
# 普通发布还需从既有受信 provider 提供 mirror token;不要打印或写进日志。
# 先确认候选 SHA 在 canonical GitHub 上可获取,且包含当前 main 的全部提交。
scripts/deploy-staging.sh --migrate-dry-run
bash scripts/release -staging-governed.sh '<40-sha>'
scripts/deploy -staging.sh
STAGING_TOKEN = $( cat /tmp/.staging_token) scripts/staging-seed.sh
scripts/staging-verify-isolation.sh
scripts/staging-verify-isolation.sh
# 使用上一步发布输出的同一 release receipt;观察窗口未结束时保留原 receipt 重试。
scripts/complete-staging-verification.sh '<release-receipt-file>'
```
```
这是历史 Slark 发布实际采用的 governed 入口:控制器从 canonical GitHub
获取不可变候选、持有本地锁,内层 `deploy-staging.sh` 执行远端构建、数据库迁移、
切换和健康检查。需要先单独演练迁移时,按仓库发布文档生成同一精确 SHA 的
`staging-candidate` manifest/archive/source,再向 `deploy-staging.sh --migrate-dry-run`
传入 `STAGING_CANDIDATE_MANIFEST` 、`STAGING_CANDIDATE_ARCHIVE` 和
`STAGING_SOURCE_DIR` ;当前 `deploy-staging.sh` 不接受缺少这些候选材料的直接调用。
这条演练在隔离数据库副本执行 pending up,不重启 Server;正式 governed 发布则在
live staging 数据库执行 migrate up。历史 2026-10-22 成功回执见
`acceptance/REQ-20261010-0007-rejection-execution-20261022/staging-deploy-r2.log`
与 `staging-verify-r2.log` 。历史脚本里为特定 DSH UI 发布加入的配对、手动切换及回退步骤
不属于普通 Slark Server/daemon 发布命令,不能直接照搬。
候选若包含 canonical main 尚未收录的 migration,或修改发布控制面,staging admission
只允许在隔离 PDV 中验证,不会切换共享 staging。隔离验证通过后,先按 PR 门禁将改动并入
canonical main,再用新的 main 精确 SHA 重新准备候选和执行共享 staging 发布;不要把
`isolated_required` 的成功回执写成共享 staging 已发布。
若镜像 token 确实不可读,仓库另有 `deploy-staging.sh --no-mirror --reason '<具体原因>'`
审计路径。仅在已按官方 `staging-candidate.mjs` 生成并核验同一精确 SHA 的 manifest、archive、
source 和 daemon seed revision 列表,且已满足正常 CI、环境身份和 release admission 门禁时使用;
该路径不读取 Bitwarden、不切换 `staging-latest` 镜像频道。历史先例见
`docs/reports/REQ-20261012-0006-delivery-status.md` 。使用后应明确记录“共享 staging 已发布,
镜像频道未更新”,不能把 no-mirror 写成镜像发布成功。
不得把查询到的 Name 非 `slark-staging` 的机器当作目标。四件套细节与环境隔离约束见
不得把查询到的 Name 非 `slark-staging` 的机器当作目标。四件套细节与环境隔离约束见
`docs/req-20260811-0059-staging-environment-design.md` 。
`docs/req-20260811-0059-staging-environment-design.md` 。
**AWS 凭证不可用时的只读排查(2026-10-23 记录):**先查 Slark 自己的历史发布记录,
例如 `acceptance/REQ-20260930-0015/release-staging-main-history-window.sh` ;其中记录的
staging IP 是 `15.135.112.181` , Slark 应用 release project ID 是
`tpqvorREPTj2kx9poKJWH` 。这些值只是定位线索,使用前须重新核对。可用已有
`cloud-server-syd.pem` 、`StrictHostKeyChecking=yes` 只读 SSH 到历史 IP,并核对远端
`/sys/devices/virtual/dmi/id/board_asset_tag` 、IMDSv2 `instance-id` 与
`public-ipv4` ; 2026-10-23 三项分别核对为 `i-013e5ca3fdfddc13e` 、
`i-013e5ca3fdfddc13e` 、`15.135.112.181` ,当时 `slark-server` 和
`slark-daemon-staging` 均在运行。不要输出 IMDS token 或应用凭证。
只读身份核验示例(历史 IP 仅用于定位;任何一项不匹配就停止):
``` bash
ssh -i ~/.ssh/cloud-server-syd.pem -o BatchMode = yes \
-o StrictHostKeyChecking = yes -o ConnectTimeout = 5 \
ubuntu@15.135.112.181 'bash -s' <<'STAGING_IDENTITY'
set -euo pipefail
tag="$(cat /sys/devices/virtual/dmi/id/board_asset_tag)"
token="$(curl -fsS --noproxy '*' --max-time 3 -X PUT \
-H 'X-aws-ec2-metadata-token-ttl-seconds: 60' \
http://169.254.169.254/latest/api/token)"
id="$(curl -fsS --noproxy '*' --max-time 3 \
-H "X-aws-ec2-metadata-token: $token" \
http://169.254.169.254/latest/meta-data/instance-id)"
ip="$(curl -fsS --noproxy '*' --max-time 3 \
-H "X-aws-ec2-metadata-token: $token" \
http://169.254.169.254/latest/meta-data/public-ipv4)"
test "$tag" = i-013e5ca3fdfddc13e
test "$id" = "$tag"
test "$ip" = 15.135.112.181
printf 'instance=%s public_ip=%s\n' "$id" "$ip"
STAGING_IDENTITY
```
这条 SSH/DMI/IMDS 路径只证明所连主机的实例身份,**不提供 AWS Name 标签的当次证明,
不能代替上面的发布前控制面核验**。`docs/runbooks/REQ-20260922-0005-staging-watchdog-emergency-recovery.md`
中的替代路径仅授权给那次特定事故,不能移用于普通 staging 发布。不要借用其他项目
的 AWS profile;若缺少 Slark AWS 控制面凭证,停在只读排查并记录缺口。2026-10-23
实例自带角色 `dsh-identity-staging-ec2` 调用 `DescribeInstances` 返回
`UnauthorizedOperation` ,不能靠这个角色补齐 Name 核验。
### 3.2 非权威路径:ai-proj cohost(仅明确指定时)
### 3.2 非权威路径:ai-proj cohost(仅明确指定时)
ai-proj cohost 位于 `ubuntu@82.157.141.202:/opt/slark-staging` 。全程使用隔离数据、数据库、密钥和状态目录,
ai-proj cohost 位于 `ubuntu@82.157.141.202:/opt/slark-staging` 。全程使用隔离数据、数据库、密钥和状态目录,