26 KiB
发布手册(预发 / 正式)
发布只走一条受控链路:GitHub Actions 的 Release workflow 负责版本分配、封板、构建、签名和下游发布;Homebrew Formula 在不可变 Release 资产及 checksum 通过校验后,由同一 workflow 直接写入 main,不再创建二次 PR。本地 dws-release 仍是兼容入口,但不再要求某一台固定电脑承担打包;不要直接运行 goreleaser release,也不要手工补打、移动或复用 tag。
发布前必须完成平台治理:目标 GitHub 仓库已启用 immutable releases,main 精确要求 CI workflow 的九个 context:Lint、Test、Coverage、Policy、Edition、Interface Integrity、AI Behavior、CLI Smoke、Mock MCP。云端入口会在封 tag 前检查 immutable releases、当前 SHA 的全部九个 context、Environment 保护规则和在途 Release;v* tag ruleset 仍需仓库管理员预先配置。
推荐入口:GitHub 云端发布
入口页面是 GitHub Actions → Release。在仓库页面依次点击 Actions → Release → Run workflow 即可操作;不需要通过 Agent 或本地机器触发。
只有得到该仓库明确授权、最终权限为 write、maintain 或 admin 的协作者可以运行发布操作,workflow 还会对发起人和重新运行者做同样的权限复核。没有仓库写权限的外部贡献者以及仅有 read / triage 权限的成员不能规划或发布版本。两个渠道的授权边界如下:
- beta:上述任一内部成员都可以直接规划和发布,不需要人工审批。
- stable:上述任一内部成员都可以规划并发起发布;完成只读规划和治理检查后,workflow 会在
release-stableEnvironment 等待另一名仓库管理员通过Review deployments签收。申请人不能批准自己的请求,批准后原 run 自动继续,无需重新触发。
基于当时最新的 main 发起发布:
- 在上述
Release页面选择Run workflow,分支必须是默认分支main。 release_operation=plan,选择release_channel=beta|stable;仅在开始新 beta 线时选择release_bump=patch|minor|major。- workflow summary 会给出唯一的下一版本。运行
prepare-changelog.sh将已合入的 release fragments 汇总成对应的精确CHANGELOG.md章节,并通过唯一的 release-seal PR 合入main。 - 再次运行,改为
release_operation=publish。beta 会直接进入自动化发布;stable 会在封 tag 前等待管理员签收。
plan 是纯只读操作,不创建 tag、预留版本号或生成包。CHANGELOG 合入期间若另一个发布先占用了该版本,publish 会重新分配并因 CHANGELOG 章节不匹配而拒绝,需要重新 plan。publish 会先再次确认 dispatch SHA 仍是当前 main、Code Admission 和平台治理均通过,再由唯一的 write job 使用 GitHub API 原子创建 annotated tag;同一次 run 随即进入既有的跨平台构建、GitHub/npm、可选 OSS/Gitee 发布和 Homebrew 直交付 DAG。内置 GITHUB_TOKEN 创建的 tag 不依赖第二条 workflow 被再次触发。
为缩短封板前后的关键路径,publish 的只读版本规划会与平台治理检查并行,seal 仍严格等待二者成功;plan 在 candidate annotated tag 上验证过的 contract 和 stable/beta baseline 会绑定进 seal,并由 seal 后的 tag authority 检查复用。Code Admission 状态与 immutable-releases 治理仍会在 seal 后再次读取,避免 preflight 与发布之间的状态变化被忽略。随后三类只读门禁(release automation、CLI 与 Schema 兼容性、multi-profile E2E)与 GoReleaser 构建并行;Node/archive 等仅供后处理使用的工具也延后到构建完成后安装。并行和已验证结果复用只改变调度,不降低发布门禁:任何一条验证失败都会阻止 GitHub Release、npm、镜像和 Homebrew 发布,delivery proof 也要求三条验证 job 全部成功。
OSS 镜像默认不参与发布 DAG,适用于尚未创建 Bucket 的仓库。云端封板会把当时的仓库变量 ENABLE_OSS_MIRROR=true 记录为不可变 tag 元数据 OSS-Mirror: enabled,否则记录为 deferred;后续发布和撤回只读取该 sealed policy,不读取变量的当前值。enabled 继续对缺失凭据、无效 Bucket、上传、pointer 和撤回失败保持 fail-closed;deferred 明确跳过不存在的渠道。为避免补发后撤回遗漏,deferred 版本暂不接受 repair_oss_version,启用 OSS 只影响后续新 tag,直到补齐可审计的不可变 repair 证明。
自动版本规则
- beta:如果存在尚未封正式版的最高版本线,自动取
beta.N+1;否则从最新已分配正式版按所选 patch/minor/major 开新线并取beta.1。 - stable:先锁定最高开放版本线上的最新已分配 beta,再要求它已成功交付且未撤回;不会跳过失败/撤回的最新 beta 去选择更早版本。正式版 core 与该 beta 完全相同。
vX.Y.Z、vX.Y.Z-beta.N一经分配就永久占用。撤回时创建withdrawn/v...墓碑,原编号永不复用。- 例如撤回
v1.0.53-beta.5后,下一 beta 是v1.0.53-beta.6;撤回正式版v1.0.53后,下一 patch 修复线是v1.0.54-beta.1,验证后再发布v1.0.54。 - 如果最新 beta 已撤回,禁止直接用更早 beta 晋级正式版;必须先构建下一个 beta。
全平台撤回与回滚
已公开版本出现问题时,在 GitHub Actions 运行 Withdraw release,分支必须选择当前默认分支 main,并填写:
version:精确版本,例如v1.0.53或v1.0.53-beta.5。reason:8–300 字符的单行公开原因。confirmation:精确输入WITHDRAW <version>,例如WITHDRAW v1.0.53。
该 workflow 使用与发布相同的串行 publication lock,并进入受保护的 release-withdrawal environment。它只接受已经由 Release workflow 完整交付的 public immutable release,自动选择同一渠道中最新的、更早且未撤回的完整版本作为回退目标,然后按以下顺序执行:
- 先创建永久 annotated tag
withdrawn/<version>,记录原 tag object、commit、原因、申请人和 workflow run。这个墓碑是版本号永久占用记录,永不移动、永不删除。 - 先验证 Homebrew Formula;若它仍指向问题版本,先创建回退 PR,再继续其他渠道撤回。这样 PR 创建失败时只留下可安全续跑的墓碑,不会先造成渠道分裂。若 Formula 尚未指向问题版本或已经处于安全版本,则直接校验。
- GitHub Release 先标记为 withdrawn;npm 精确版本执行
deprecate,并把latest/betadist-tag 回退;只有目标 tag 封存了OSS-Mirror: enabled时,OSS 才会先补齐回退版本资产,再移动latest.txt/beta.txt并删除问题版本目录;启用 Gitee 时同样先补齐回退 Release,再删除问题 Release 和 tag。 - npm 以及目标 tag 启用或发布时配置的镜像渠道均已验证安全后,删除 GitHub 上的问题 Release 和原
v...tag,并验证/releases/latest对正式版回到安全版本。若本次创建了 Homebrew PR,run 最后故意保持失败,直到另一名维护者审核合入;合入后,从新的main使用完全相同的 version、reason 和 confirmation 重跑并完成。永久withdrawn/v...墓碑始终保留。
GitHub、npm、OSS、Gitee 和 Homebrew 的“回滚”指新的安装、升级和渠道解析不再拿到问题版本。已经装到用户电脑上的二进制无法被服务端强制降级;用户必须重新安装回退版本、安装后续修复版,或使用 CLI 自带的本地 rollback 能力。npm 不执行 unpublish:问题版本保留明确的弃用警告,但 latest / beta 不再指向它;即使 registry 允许删除,已发布过的版本号也不会重新使用。
撤回前必须存在同一渠道中更早、完整交付且未撤回的安全版本;若目标是该渠道第一个版本、没有安全候选,workflow 会在创建墓碑或修改任何渠道前 fail closed,需要先决定明确的替代策略。CLI 本地 rollback 也只有在本机仍保留上一次升级备份时可用。
撤回以“精确版本”为单位,不会因为正式版曾由某个 beta 晋级就隐式级联修改另一个渠道。若同一缺陷同时存在于正式版及其 beta,应先撤回正式版,再撤回对应 beta,并分别使用各自的精确确认串;每次都只会把该渠道回退到自己的安全候选。
撤回正式版 v1.0.53 后,v1.0.53 仍被墓碑视为已分配。下一次 patch 发布从 v1.0.54-beta.1 开始,验证后晋级 v1.0.54。撤回 v1.0.53-beta.5 后,同一开放版本线继续为 v1.0.53-beta.6;不会退回或复用 beta.5。
兼容入口:本地发布
安装发布 Skill 后直接运行:
dws-release
零参数会进入引导模式。仓库内的等价入口是 ./scripts/release/dws-release.sh。第一次使用只需配置一次生产发布远端,命令会把远端名及其规范化仓库身份一起保存在当前 Git 仓库中:
dws-release config --remote origin
之后命令按仓库状态自动走到正确步骤:缺少精确 CHANGELOG 章节时只生成模板并停止;补全、提交并合入 main 后,再运行同一条命令就会安全快进本地 main 并执行完整预检。若同名 remote 后续被改指向其他仓库会直接拒绝。官方仓库不再接受本地 --publish,命令会直接提示上述 Actions 页面;本地入口不能绕过 beta/stable 的统一授权。
Release workflow 不再监听新建的 v* tag;直接推 tag 不会发布 GitHub Release、npm 或镜像渠道。所有新 beta/stable 都必须从云端页面进入统一授权和审计链路;历史失败版本仍可通过受保护的 recovery 兼容处理。
发布模型
main 上的候选代码 + beta CHANGELOG
→ vX.Y.Z-beta.N(预发验证)
→ 补正式 CHANGELOG;允许继续通过 PR 合入新 commit
→ vX.Y.Z(正式发布,封板提交必须包含该 beta 提交)
云端入口自动选择本次最新、已交付且未撤回的 beta;本地入口必须显式指定。流水线要求该 beta 已成功交付、未撤回,且 beta 提交必须位于正式发布封板提交的历史中——不能跳过 beta 直接发正式版,但允许在 beta 之后把经过 review 合入 main 的 commit 一起发布。
预发发布
运行统一入口:
dws-release v1.2.3-beta.1
如果 CHANGELOG 尚不存在,该命令会从 .changes/*.md 生成 beta 章节并归档已消费的
fragments,然后停止。审阅生成内容并通过唯一的 release-seal PR 合入 main;然后重新运行完全相同的命令,它会执行完整预检:
dws-release v1.2.3-beta.1
预检包含测试、策略检查、旧正式版 CLI 与 Schema 双基线兼容检查、全平台打包、npm 安装验证,以及 macOS 环境下的 Homebrew 安装验证。它还会从默认分支触发一次无发布权限的 Release governance preflight,用正式流水线相同的身份检查该精确 commit 的九个 Code Admission context 和 immutable releases。通过后回到上述 Actions 页面选择 beta 和 release_operation=publish;云端会重新绑定当前 main,然后直接进入 beta 自动发布,不需要人工审批或输入确认短语。
正式发布
beta 验证通过后,运行正式版入口:
dws-release v1.2.3 --from-beta v1.2.3-beta.1
首次运行只生成正式版 CHANGELOG 并停止。补全内容、删除 TODO,提交后通过 PR 合入 main;重新运行同一条命令做完整预检:
dws-release v1.2.3 --from-beta v1.2.3-beta.1
预检通过后,在 Actions 页面选择 stable 和 release_operation=publish。云端入口会按上述规则唯一选择 beta,并把它写入 stable annotated tag 的 From-Beta 元数据;在创建 tag 前必须由另一名仓库管理员签收。
CHANGELOG 契约
每个 tag 必须有唯一、非空且不含 TODO/TBD 的精确章节:
## [1.2.3-beta.1] - 2026-07-11
### Changed
- 本次 beta 验证的用户可见变化。
正式版使用 ## [1.2.3] - YYYY-MM-DD。该章节会直接成为 GitHub Release Notes。
Release fragments
普通 PR 不修改 CHANGELOG.md 的 Unreleased 区域。需要面向用户发布说明的改动在
.changes/<unique-name>.md 中增加一个独立 fragment;格式和允许的分类见
.changes/README.md。预发封板时
scripts/release/prepare-changelog.sh prerelease <version> 会稳定排序并汇总所有未归档
fragment,写入唯一版本章节后移动到 .changes/released/<version>/。如果 beta 发布后又有
带 fragment 的 PR 合入,而维护者决定直接发布 stable,
scripts/release/prepare-changelog.sh stable <version> --from-beta <tag> 会保留 beta 晋级摘要
模板,并把这些 post-beta fragments 写到明确的 Changes since <beta> 边界之后,再移动到
.changes/released/<stable-version>/。没有 active fragment 时,stable 仍只生成原有晋级摘要
模板。因此并发 PR 不会争用 CHANGELOG.md;唯一的 release-seal PR 同时提交生成的章节与
归档移动,供审计复核。
CI/CD 保证
- 只接受
vX.Y.Z-beta.N和vX.Y.Z,且新版本必须高于上一正式版。这里的“上一正式版”必须同时具备公开非草稿 GitHub Release 和同 tag/commit 的成功 Release workflow;只有 tag、没有交付成功的孤儿版本会阻断后续发布,要求走机器核验恢复补齐。云端 tag 会固定Release-Run、requester、commit 和版本分配指纹,交付验证按该精确 run/attempt 及完整 job graph 取证,不接受任意workflow_dispatch。历史版本若曾通过专用 recovery workflow 完成交付,只能使用仓库内delivered-stable-recoveries.json中精确到 tag、commit、run、workflow SHA 与 attempt 的 reviewed 证据。 - tag 必须由云端 seal job 创建为 annotated tag;封板提交必须已通过 PR 合入并包含在远端
main历史中。流水线允许其后main继续前进,但始终要求封板提交位于main历史中。 - 日常 CI 和发布前都会对比“最新已交付正式版”的完整 CLI 与 Schema 契约;若长时间预检期间该 baseline 发生变化,会针对新的 baseline 重新比较。
- GoReleaser 只构建;Darwin 重签、checksums 重算和 npm 安装验证通过后,才统一上传 GitHub Release 的最终产物。
- 六个平台归档会逐个解包并核验二进制内嵌版本;公开资产集合、checksums 集合和 npm tarball integrity 都必须精确一致。npm tarball 固定由 npm
10.9.2打包,避免重跑时因 runner 自带 npm 漂移产生不同字节。 - stable 发布到 npm
latest;prerelease 发布到 npmbeta。启用ENABLE_OSS_MIRROR=true后,stable 同步 OSSlatest.txt和共享安装脚本,prerelease 只同步 OSSbeta.txt,不会覆盖稳定入口。 - Release workflow 使用一个最多容纳 100 个 pending run 的串行 publication queue;版本规划、云端封板、发布、恢复、修复和撤回共享同一发布锁。
- 云端 seal 创建远端 tag 后,后续发布归同一 run 所有;发布中途失败时先重跑同一 run 的失败 jobs,必须跨 run 时走机器核验恢复,禁止改 tag 指向或复用版本号。只有已经公开版本经过受保护的全渠道撤回并留下永久
withdrawn/...墓碑后,撤回 workflow 才会在最后一步删除原 tag。
npm 补发只允许从默认分支触发 Release workflow 的 repair_npm_version。它只支持启用 immutable releases 后、由本流水线成功产出的公开 immutable release:目标必须是 main 历史中的 annotated tag,并且同 commit 的 Build immutable GitHub Release job 已成功。即使后续 npm 分发失败,这个独立的产物封存边界仍可作为补发依据。补发会用目标 commit 的 npm 模板重组包,逐平台核验资产和二进制版本,再发布到隔离的 backfill dist-tag,不会回滚 latest / beta。历史 mutable release 不进入自动补发路径,避免把可被替换的资产带入 npm。
已启用的 OSS 或 Gitee 分发失败且 GitHub immutable Release、npm 已交付时,从受保护的默认分支触发
Release workflow,并且只填写 repair_oss_version 或 repair_gitee_version 之一。channel
repair 会精确绑定失败 tag run 的最新 attempt,且 OSS repair 要求 tag 的 sealed policy 为 enabled;contract、构建、Developer ID 签名、
immutable GitHub 发布和 npm delivery 必须全部成功,且只能有一个 OSS/Gitee 下游失败,
随后才会下载并重新校验原始资产、修复所选镜像。OSS repair 必须匹配失败的 OSS step;
Gitee repair 还允许其 job 因该 OSS 失败而 skipped,此时只代表 Gitee backfill 成功,
不会把仍未修复的 OSS 标成成功。该证据不能用于 beta → stable 或
stable baseline,后两者仍要求整条 Release 成功或受保护 recovery 成功。不要重跑旧
attempt 的单个 failed job,以免在 attempts 之间拼接交付证据。独立 Gitee release
workflow 和本地直发脚本已停用,避免绕开 publication queue 或用重新构建的不同字节覆盖镜像。
既有 tag 的紧急恢复
云端封板或本地 tag push 已成功、但 Release workflow 失败且 GitHub Release 尚未公开时,不要新建临时 workflow、移动 tag 或跳过门禁。在最新且干净的 main worktree 运行:
dws-release recover v1.2.3-beta.1
命令会自动解析 annotated tag object、peeled commit,以及 tag 绑定的失败云端 run 或最近一次匹配的失败 tag-push run;也可以用 --failed-run <run-id> 精确指定。确认完整版本号后,它从默认分支触发受保护的恢复模式并等待完成。恢复模式必须满足:
- 输入精确绑定原 annotated tag object、commit 和失败的 sealed
Releaserun;云端 run 还必须与 tag 内的 run ID、attempt、requester 完全一致,commit 必须仍在main历史中。 - 目标只允许不存在 GitHub Release 或仍为 Draft;已经公开的版本不能全量重建:单个下游故障走对应的 channel repair,版本本身有问题则走受保护的全平台 withdrawal。
- 恢复不再进入人工审批 environment。workflow 会机器核验 tag object、commit、原失败 run/attempt、请求人、完整 seal metadata、
main祖先关系以及 Release 状态;任一事实不一致都会在构建前 fail closed。 - 恢复复用正常的 contract、构建、Developer ID 签名、资产校验、immutable 发布、Homebrew、npm,以及已启用的 OSS jobs,不存在 recovery 专用 publisher 或门禁跳过。
- 如果 GitHub Release 已在 recovery 中封存、后续 Homebrew/npm 校验发生瞬时失败,只重跑该 run 的 failed jobs;流水线仅在隐藏 run marker、tag object、commit 和 finalized artifact 字节全部精确一致时复用公开 Release。
成功的默认分支恢复 run 会成为后续 beta → stable 和 stable baseline 验证的可审计交付证据;历史临时分支恢复仍只接受 reviewed manifest 中的固定证据。
seal job 写入 tag 后如果只因 GitHub API 瞬时 404/429/5xx 或后续 job 失败,可直接使用 GitHub 的 “Re-run failed jobs”。同一 run 会精确复用原 release-plan;seal 只在 version、tag object、commit、channel、beta 来源、OSS policy、请求人、run ID 和完整 message 全部匹配且原 attempt 不大于当前 attempt 时认领已有 tag。不同 run 或任一字段不匹配时不会认领。GitHub Release 尚未公开且必须跨 run 重建时走上述机器核验 recovery;已经公开且仅 npm/OSS/Gitee 某一渠道失败时走对应 repair;版本内容本身有问题时走 withdrawal。
OSS 的 latest.txt / beta.txt 是镜像频道元数据;当前仓库安装器仍主要从 GitHub/Gitee 解析版本。启用 OSS 后,发布和撤回把它作为受控分发渠道处理,保证一旦外部消费者接入该 pointer,也不会继续解析到已撤回版本;未启用时两条流程都明确跳过不存在的 OSS 渠道。
Release workflow 会生成 Darwin/Linux 双架构 Formula,并在不可变资产逐个校验后,由 HOMEBREW_PR_TOKEN 所属的受控发布身份只提交对应 stable 或 beta Formula 文件到 main,不再创建二次发布 PR;并发 main 更新会以全新 clone 最多重试三次,绝不 force push。该身份的提交不会依赖另一轮 CI 来补齐证明:workflow 只在确认该 commit 单父、唯一改动为目标 Formula、内容与本次已验证产物逐字节一致,且父 commit 九项 Code Admission 全绿后,直接为 Formula-only commit 封存同名九项成功 checks,避免下一次发布因缺失 contexts 被卡住。撤回 workflow 暂时仍使用相同模板和回退版本 checksums 打开反向 PR;问题 GitHub Release 会先被移除以阻止新安装,永久墓碑和 workflow 日志承担审计/续跑依据。
平台治理前置
仓库管理员还需要在 GitHub 平台配置以下不可由脚本替代的规则:
main必须精确要求Lint、Test、Coverage、Policy、Edition、Interface Integrity、AI Behavior、CLI Smoke、Mock MCP九个 Code Admission context;Release workflow 也会通过 Checks API 再确认该封板 SHA 上九项全部成功。- 必须启用 immutable releases;它只保护启用后发布的 release,因此应在第一次使用新流水线前配置。为
v*增加 tag ruleset,限制创建权限,并在 release 发布前保护 tag 的短暂窗口。 - tag ruleset 还必须覆盖
withdrawn/v*:只允许受保护的撤回 workflow 创建墓碑,禁止更新或删除墓碑;同时应允许 Release workflow 创建新的v*,允许撤回 workflow 在全部渠道回退后删除精确的问题v*。若组织级规则阻止这两个 workflow 的预期动作,发布或撤回会 fail closed,不能靠手工移动 tag 绕过。 - 配置
RELEASE_GOVERNANCE_TOKENActions secret,只授予目标仓库Administration: read;内置GITHUB_TOKEN不具备 immutable-releases API 所需的仓库治理权限。每次本地预检和云端发布都使用这一个身份进行 fail-closed 验证。 - 配置
APPLE_CERTIFICATE_P12_BASE64、APPLE_CERTIFICATE_PASSWORD和具备发布权限的NPM_TOKEN;撤回还要求该 npm 身份能够执行deprecate和修改 dist-tag。 - 启用 OSS 镜像时,先创建有效 Bucket,再设置仓库变量
ENABLE_OSS_MIRROR=true,并配置OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET、OSS_ENDPOINT、OSS_BUCKET,按需配置OSS_PREFIX。启用后发布保持 fail-closed;撤回身份必须能够补齐安全版本资产、写latest.txt/beta.txt并删除问题版本前缀。尚未 provision Bucket 时保持该变量未设置或不等于true,新 tag 会封存OSS-Mirror: deferred并跳过 OSS;该版本不能通过现有 repair 流程事后改成启用。 - 若启用 Gitee fallback,设置
ENABLE_GITEE_UPLOAD_FALLBACK=true,并配置GITEE_TOKEN、GITEE_USER、GITEE_REPO;该身份必须能够创建和删除目标仓库的 Release 与 tag。 - 正常 Homebrew 发布使用现有的
HOMEBREW_PR_TOKEN直接提交 Formula-only commit,不再创建 Homebrew PR,也不跑权限 canary。GitHub 不允许内置 Actions App 作为当前仓库 ruleset 的 bypass actor,因此两个默认分支 ruleset 都只给该 token 所属的指定发布管理员用户alwaysbypass;仓库脚本仍会限制提交路径、校验 Ruby、禁止 force push,并在并发更新时重新基于最新main。 HOMEBREW_PR_TOKEN应保持仓库范围的Contents: write与Pull requests: write权限;后者仅供撤回流程创建回退 PR。不要与RELEASE_GOVERNANCE_TOKEN复用,并定期审计 token owner 与 ruleset bypass actor 一致。- 创建
release-betaenvironment,只允许受保护分支且不配置 required reviewer;仓库内部write、maintain、admin成员的 beta 发布会直接通过该边界。 - 创建
release-stableenvironment,只允许受保护分支,以仓库管理员为 required reviewer,禁止申请人自审并关闭管理员绕过。内部成员可以发起 stable,但必须由另一名管理员签收后才能封 tag 和写入任何发布渠道。 - Release workflow 会在封 tag 前回读并验证上述两套 Environment 规则;规则缺失、stable reviewer 不再是仓库管理员、或 beta 被误加人工审批时都会 fail closed。
- 创建
release-withdrawalenvironment,只允许受保护分支,设置至少一名 required reviewer、禁止申请人自审并关闭管理员绕过。撤回 workflow 会通过 API 复核这些规则;任何一项缺失都会在触碰 npm、OSS、Gitee、Homebrew 或 GitHub Release 前失败。 - 仓库或组织的 Actions 策略必须允许
Release与Withdraw releaseworkflow 的GITHUB_TOKEN获得各 job 声明的权限;正常发布由HOMEBREW_PR_TOKEN更新两个受控 Formula 路径,内置 token 只承担 workflow 自身声明的封板与校验写入。若撤回凭证采用 environment secret,确认release-withdrawal审批完成后能够读取撤回所需的 npm、OSS、Gitee 和 Homebrew 凭证。
immutable releases,或任一 Code Admission context 缺失、未成功时,发布脚本会自动拒绝封 tag。tag ruleset 可能来自组织层,脚本不自动推断其最终作用范围;管理员确认不能省略,脚本约定也不能替代平台强制。