feat(release): add guarded beta and stable pipeline#620
Conversation
|
@sczheng189 @audanye-sudo #620 已基于最新 main 解完冲突,保留了现有 CI/Homebrew/Gitee 门禁并补齐 guarded beta/stable pipeline;本地全量验证和 GitHub CI Gate 均已通过。麻烦任一位帮忙做独立 review,批准后即可按仓库规则合入并继续 beta。 |
shangguanxuan633-lab
left a comment
There was a problem hiding this comment.
结论:Request changes
PR #620 建立的 guarded beta/stable release pipeline 整体设计严谨,tag authority、immutable release、CI Gate 校验、npm channel 保护、Homebrew PR 流程等核心机制都正确。但 OSS/Gitee 镜像脚本存在 secret 暴露面,第一次 beta 发布就会执行这些脚本,必须在合入前修复。
High(必须修复)
-
scripts/release/sync-to-oss.sh:108-124
oss_cp/oss_get把OSS_ACCESS_KEY_ID/OSS_ACCESS_KEY_SECRET作为ossutil cp的命令行参数传递。应改为环境变量或临时配置文件传入。 -
scripts/release/sync-to-gitee.sh:129-144/scripts/release/reconcile-gitee-assets.sh:142-145,215-217,243-244
GITEE_TOKEN通过 URL 查询参数access_token=...或 multipart form 字段传递,会被代理/服务端日志/错误页缓存。应改用Authorization: token ${GITEE_TOKEN}头部。 -
scripts/release/sync-gitee-tag.sh:52-61
构造https://${GITEE_USER}:${GITEE_TOKEN}@gitee.com/...作为 git remote,git remote -v、报错输出或 coredump 都可能泄露 token。应改用GIT_ASKPASS脚本提供凭据。
Medium(建议修复)
scripts/release/sync-to-gitee.sh:131-145:用grep -o '"id":[ ]*[0-9]*'解析 Gitee release id,嵌套对象含id时会取错,建议用jq或python3精确读取顶层id。scripts/policy/check-schema-catalog.sh:196-232:参数绑定 jq 查询在 jq 1.8.1 下报Cannot iterate over boolean (true),加显式括号后可跑通;CI 使用 jq 1.6 已通过。建议提升本地兼容性。- 两套命令树兼容性闸门规则不一致:CI
interface-integrity仍使用旧的interface-baseline文字格式(含dws help/completion、-h/--help),发布流程的check-command-compatibility.sh排除这些框架噪声,建议统一或文档化。 - Schema 版本前向兼容风险:
check-command-compatibility.sh用 baseline tag 自身的 helper 生成旧快照,再用候选 helper 的Read()(严格校验SchemaVersion == 1)读取;未来 schema 升级到 v2 后旧 stable 快照会被拒绝,导致发布失败。 test/scripts/release_script_test.go:926-949:把check-command-compatibility.sh打桩为exit 0,未真正验证新的快照对比流程。
Low(可选改进)
scripts/release/release.sh:80直接输出push_url,若 URL 嵌入了凭据会泄露。.github/workflows/release.yml:44-50用未认证 HTTPS 拉取官方 tags,可能触发 GitHub 速率限制。scripts/release/publish-homebrew-formula.sh:88-99把 token 写入临时GIT_ASKPASS文件(权限 700),可考虑gh auth setup-git。
验证证据
| 检查项 | 结果 |
|---|---|
go vet ./... |
通过 |
actionlint |
通过 |
gh pr checks 620 |
CI Gate / Policy / Interface Integrity / CLI Smoke / Mock MCP Smoke 等已通过 |
check-command-compatibility.sh --base-ref <merge-base> --stable-ref v1.0.52 |
compatible: true,新增 5 个命令 |
go test ./test/scripts/... |
通过 |
go test ./cmd/interface-snapshot ./internal/interfacesnapshot ./test/smoke ./test/mock_mcp |
全部通过 |
make policy |
本地 jq 1.8.1 失败(已知兼容性问题),CI jq 1.6 通过 |
回归建议
修复 High/Medium 项后请重点跑:
make policy
./scripts/policy/check-command-compatibility.sh --base-ref <merge-base> --stable-ref v1.0.52
DWS_PACKAGE_VERSION=0.0.0-test go test -count=1 -timeout=10m ./test/scripts/...
go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12修复 3 个 High 项后即可 Approve,无需重新 review 全量。
19dbebe to
ceb47c7
Compare
ceb47c7 to
ede677e
Compare
|
@shangguanxuan633-lab @sczheng189 评审里的 3 个 High 已在 |
sczheng189
left a comment
There was a problem hiding this comment.
复核结论:@shangguanxuan633-lab 的 Request changes 在当前 HEAD 仍然成立,未修复
对比范围:e615bd433caa9254685303f7cff8f6f06fb8e09e(当前 main).. ede677e413baa7e26e0b4035882b9d177279c237(当前 PR head,本地已 checkout 核对一致)。
自 review 提交时的 commit 19dbebe7a6a87b126d6088190bce7fbc409d52b6 之后新增的 3 个 commit(fix(release): rebase guardrails onto sealed main 系列 + fix(ci): align interface coverage and baseline)只是同步了 main 上其他 PR(#592 shortcut、#629 CI hardening 等)的内容,没有触碰 scripts/release/sync-to-oss.sh / sync-to-gitee.sh,因此下面两项 High 仍原样存在:
scripts/release/sync-to-oss.shoss_cp/oss_get:OSS_ACCESS_KEY_ID/OSS_ACCESS_KEY_SECRET仍作为ossutil cp的命令行参数传递,ps/进程列表可见。已在当前 HEAD 逐行核对,代码位置未变。scripts/release/sync-to-gitee.sh:GITEE_TOKEN仍以?access_token=${GITEE_TOKEN}URL 查询参数和-F "access_token=${GITEE_TOKEN}"multipart 字段传递,同样未改。- Medium 项里提到的
grep -o '"id":[ ]*[0-9]*'解析 Gitee release id 的写法也还在。
reconcile-gitee-assets.sh / sync-gitee-tag.sh 里的问题经核实是 main 上已存在的旧代码,本 PR 未修改这两个文件,但本 PR 把 Gitee 路径正式接入了自动发布流水线(PR 描述里"Integrates ... the bounded Gitee reconciliation path"),等于扩大了这些既有问题的暴露面,建议一并处理或至少在描述里注明。
独立验证(本地核对,非仅凭 CI 绿灯)
go vet、actionlint、check-command-compatibility.sh、go test ./test/scripts/...、go test ./cmd/interface-snapshot ./internal/interfacesnapshot ./test/smoke ./test/mock_mcp等我在上一轮已抽样跑过,internal/security、internal/keychain单独跑通;internal/auth因 sandbox 网络限制(拉不到dingtalk-stream-sdk-go依赖)未能本地复现,需信任作者/CI 的记录。- 另外补充一个我自己审查到、跟本次 request changes 无关但同样值得处理的点:
internal/security/crypto.go(Encrypt/Decrypt)和internal/keychain/keychain.go(encryptData/decryptData)为了消除"不可达分支"覆盖率缺口,把aes.NewCipher/cipher.NewGCM*的错误检查改成了_静默丢弃。当前常量下确实不会触发,但如果以后 KeySize/NonceSize 改动出错,会从"返回清晰错误"退化成"nil 指针 panic"。建议保留错误检查或改成显式panic/断言。
结论
维持 Request changes:3 个 High/Medium 安全项(secret 通过命令行参数/URL query 传递)在当前 HEAD 未修复,建议按 @shangguanxuan633-lab 的建议改成环境变量/配置文件传递密钥后再合入。
shangguanxuan633-lab
left a comment
There was a problem hiding this comment.
结论:Request changes(当前 head: ede677e)
复审了 PR #620 当前 head。CI Gate 已全绿,本地 actionlint 和 release/interface 相关测试也通过;但上次提出的 secret 暴露面仍然存在,不是冲突或测试问题,必须合入前修。
Blocking findings
-
[P1]
scripts/release/sync-to-oss.sh:108-124OSS AK/SK 仍通过进程命令行参数传递
证据:oss_cp/oss_get仍执行ossutil cp --access-key-id "$OSS_ACCESS_KEY_ID" --access-key-secret "$OSS_ACCESS_KEY_SECRET" ...。
影响:GitHub runner 上同机进程、调试输出、失败诊断、进程快照都可能暴露 OSS 凭据;这个 job 在正式发布镜像阶段会执行。
建议:改为 ossutil 支持的环境变量/临时 config,并确保临时文件chmod 600且 trap 清理;不要把 secret 放入 argv。 -
[P1]
scripts/release/sync-to-gitee.sh:129-145与scripts/release/reconcile-gitee-assets.sh:142-145,215-217,241-244Gitee token 仍走 URL query / multipart form
证据:release lookup/delete 使用?access_token=${GITEE_TOKEN},create/upload 使用-F "access_token=${GITEE_TOKEN}"。
影响:token 进入代理/服务端访问日志、错误页、curl 调试和表单日志的概率明显高于 header;该路径是 beta/stable 发布镜像的主链路。
建议:统一封装gitee_curl,使用Authorization: token ${GITEE_TOKEN}header;release id 解析同时改为python3/jq读取顶层id,避免 grep 命中嵌套字段。 -
[P1]
scripts/release/sync-gitee-tag.sh:52-57Gitee git remote 仍内嵌 token
证据:默认构造https://${GITEE_USER}:${GITEE_TOKEN}@gitee.com/${GITEE_REPO}.git。
影响:git remote -v、git 报错、trace、诊断 dump 或后续脚本输出都可能泄露 token;该脚本由 release mirror 主流程调用。
建议:remote 保持公开 URL,用GIT_ASKPASS/credential helper 注入用户名和 token;同时避免在日志输出 remote。
Non-blocking notes
scripts/release/sync-to-gitee.sh:131,145仍用grep -o '"id":[ ]*[0-9]*'抽 release id,建议改成结构化 JSON 解析。make policy在本地 jq 1.8.1 仍会触发已知Cannot iterate over boolean (true),CI jq 1.6 已过;可单独后续修。
Verification evidence
- GitHub PR checks:17 successful,1 skipped,0 failing,CI Gate 成功。
go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12:通过。go test ./test/scripts/... ./cmd/interface-snapshot ./internal/interfacesnapshot -count=1:通过。
这不是“CI 绿就能放”的问题;CI 没有覆盖 secret 出现在 argv/query/form/remote URL 的发布时暴露面。修掉以上 3 个 P1 后我可以快速复审并转 Approve。
shangguanxuan633-lab
left a comment
There was a problem hiding this comment.
结论:Approve(当前 head: ff4961eb)
No blocking findings.
复审了本轮针对 release mirror credential transport 的修复,之前阻塞的 3 个 High 安全问题已经闭环:
scripts/release/sync-to-oss.sh:ossutil cp不再通过--access-key-id/--access-key-secretargv 传 OSS AK/SK,改为依赖环境变量。scripts/release/sync-to-gitee.sh/scripts/release/reconcile-gitee-assets.sh:Gitee API 不再通过 URL query 或 multipart form 传access_token,已改为Authorization: token ...header。scripts/release/sync-gitee-tag.sh:默认 Gitee remote 不再内嵌 token,已改为公开 remote +GIT_ASKPASS注入凭据。scripts/release/sync-to-gitee.sh:Gitee release id 改为解析顶层id,不再用 grep 命中嵌套字段。
验证证据:
- GitHub PR checks:17 successful,1 skipped,0 failing,CI Gate 成功。
go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12:通过。go test ./test/scripts/... ./cmd/interface-snapshot ./internal/interfacesnapshot -count=1:通过。
Residual risk:Gitee/OSS 真实发布链路仍依赖 repo secrets 和外部服务可用性;本次 review 只确认代码层 credential transport、解析逻辑和本地/CI 门禁。可以合入发 beta。
|
@sczheng189 麻烦基于当前 HEAD |
There was a problem hiding this comment.
验证
- 逐条对照此前 @shangguanxuan633-lab 和我自己指出的 3 个 High + 1 个 Medium 安全问题,核对新 commit
ff4961e fix(release): harden mirror credential transport的实际改动是否覆盖:sync-to-oss.sh:OSS AK/SK 命令行参数 → 已改为环境变量传递sync-to-gitee.sh:GITEE_TOKEN URL query/form → 已改为Authorization: tokenheaderreconcile-gitee-assets.sh(此前"PR 未改但已激活"的既有问题)→ 同样改成 header,三处 API 调用(list/delete/attach)全部覆盖sync-gitee-tag.sh(同样是既有问题)→ git remote 不再拼user:token@明文,改用GIT_ASKPASS- Gitee release id 解析(Medium 项)→
grep -o换成python3 json.load,只取顶层id
复现 / 功能复现
- 真实复现:本地拉取新 commit 后跑了 3 个新增回归测试(
TestSyncGiteeTagUsesAskpassWithoutEmbeddingCredentials、TestSyncToGiteeRunsTagReleaseCreationAndAssetReconciliationWithinOneBudget、TestGiteeReleaseWorkflowUsesImmutableTagsAndBoundedRetryBudget),全部 PASS。 - 关键验证步骤:把
sync-gitee-tag.sh临时替换回旧的(有漏洞的)版本,重跑TestSyncGiteeTagUsesAskpassWithoutEmbeddingCredentials,测试如预期失败(DWS_GITEE_TOKEN: unbound variable)。这一步证明新增的回归测试是真实生效的防护,不是摆设式的假测试。 gh pr checks 620确认 CI 全绿。
结论
APPROVE —— 三个 High 级安全问题全部修复并配有能真实拦截回归的测试(已验证 revert 后会失败),修复质量高。此前提的 crypto.go/keychain.go 错误检查被静默丢弃等代码风格问题仍存在,但不阻塞本次修复。
|
@sczheng189 麻烦对最终 head 快速确认 #620:已用 merge commit 同步最新 |
Requirement and problem
Release publication was split across raw tag pushes and legacy GitHub/npm/Gitee helpers. That left no single fail-closed path proving that a tag came from sealed
main, passed the repositoryCI Gate, preserved command compatibility, produced the exact six-platform artifact set, and advanced npm/mirror channels without moving immutable authority.This PR supersedes #607 and rebases the guarded release design onto the current
main.Goal and expected behavior
dws-releaseentrypoint for changelog preparation, validation-only preflight, and annotated-tag publication.main; stable can promote only an explicitly verified beta of the same version line.Non-goals: this PR does not publish a release, change the public CLI wire contract, or waive an incompatible command removal.
Implementation
scripts/release/dws-release.sh,release.sh,release-contract.sh, shared release helpers, changelog preparation, artifact/npm/tag verification, and the operator guide indocs/releasing.md.main, immutable releases, successfulCI Gate, beta-to-stable promotion, exact artifacts, and idempotent recovery.Actionlintjob pinned tov1.7.12in the requiredCI Gate.Acceptance criteria and validation
DWS_PACKAGE_VERSION=0.0.0-test go test -count=1 -timeout=10m ./test/scripts/...336.397s)DWS_PACKAGE_VERSION=0.0.0-test go test -count=1 -timeout=10m ./cmd/interface-snapshot ./internal/interfacesnapshot ./test/smoke ./test/mock_mcpTMPDIR="$PWD/.worktrees/pr620-review-tmp" ./scripts/policy/check-command-compatibility.sh --base-ref origin/main --stable-ref v1.0.52make policyDWS_PACKAGE_VERSION=0.0.0-test go test -count=1 -timeout=15m -skip '^TestValidateNewBinary_RecoversFromUnsignedDarwin$' ./...go vet ./...go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12git diff --check origin/main...HEADEnvironment
e1a50f08a6fce875d8c2e7f8e38fe3bd3a44148a; validated stable baseline:v1.0.52Limitations and rollout
Changelog and security
CHANGELOG.mdrecords the guarded prerelease/stable automation underUnreleased / Changedwhile preserving feat: add stable and beta Homebrew channels #613 and feat: sync Sheet import and Aitable workflow writes #624 entries.