Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/scripts/test_maintenance_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,21 @@ def test_each_workflow_uses_only_approved_action_majors(self) -> None:
)
self.assert_action_matrix(self.stale_workflow, {"actions/stale"})

def test_node24_actions_stay_on_github_hosted_runners(self) -> None:
for workflow in (
self.workflow,
self.release_workflow,
self.stale_workflow,
):
self.assertNotIn("self-hosted", workflow)
runs_on = {
line.strip()
for line in workflow.splitlines()
if line.strip().startswith("runs-on:")
}
self.assertGreater(len(runs_on), 0)
self.assertEqual({"runs-on: ubuntu-latest"}, runs_on)

def test_all_pull_requests_and_master_pushes_trigger(self) -> None:
self.assertIn("pull_request:", self.workflow)
self.assertIn("push:", self.workflow)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
Android 确实使用 `--no-build-cache` 编译源码。
- [x] 6.3 核对替代 Pull Request 没有触发 `Release Build`,没有读取正式签名 Secrets、推送
tag、创建草稿 Release 或改变 stale 实际对象;任何失败均保持 #73 打开并停止合并。
- [ ] 6.4 人工评审通过后合并替代 Pull Request,重新读取 `origin/master` SHA、合并提交检查、
- [x] 6.4 人工评审通过后合并替代 Pull Request,重新读取 `origin/master` SHA、合并提交检查、
三个工作流和 OpenSpec 状态;只有读回结果完整时才以替代 Pull Request 链接关闭 #73。
- [ ] 6.5 按验证与归档流程同步主规范、归档 `upgrade-github-actions-v6`,在归档提交和合并后的
- [x] 6.5 按验证与归档流程同步主规范、归档 `upgrade-github-actions-v6`,在归档提交和合并后的
`master` 上再次严格校验,并只按精确 ref/SHA 清理已合并临时分支;不触发正式发布。
53 changes: 53 additions & 0 deletions openspec/specs/maintenance-quality-baseline/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,56 @@ Pull Request 和 `master` 的适用验证 MUST 执行 Android 单元测试、And
- **WHEN** 某项建议修复需要提高最低 SDK、改变持久化格式或突破明确固定的依赖版本
- **THEN** 系统不得把该修改作为机械 warning 清理合并
- **AND** 必须将其延期或建立独立兼容性变更

### Requirement: 工作流依赖运行时和关键安全合同必须失败即停止
Pull Request、`master`、发布和 stale 工作流使用的 JavaScript Action MUST 与执行 runner
兼容;本次目标 Action 运行于 Node 24,runner 版本 MUST 不低于 `2.327.1`。仓库契约 MUST
精确覆盖三个工作流中的目标 Action 大版本,并继续覆盖 CodeQL v4、Android CodeQL
`--no-build-cache`、JDK 17、Node.js 22、pnpm 9.15.9、只读 Web 构建、正式签名 Secrets
隔离和稳定聚合门禁。任一版本或安全边界漂移 MUST 使仓库检查失败,不能以部分工作成功代替。

#### Scenario: 目标 Action 版本与契约不一致
- **WHEN** 任一工作流使用的目标 Action 大版本与批准组合不一致
- **THEN** 仓库契约检查失败并指出发生漂移的工作流和 Action
- **AND** 稳定聚合门禁报告失败

#### Scenario: runner 不满足 Node 24 下限
- **WHEN** 工作流在低于 `2.327.1` 的 runner 上执行 Node 24 Action
- **THEN** 对应检查必须失败或被明确阻止
- **AND** 系统不得把未执行的后续构建描述为通过

#### Scenario: 维护契约真实 RED 被同步修复
- **WHEN** Action 大版本升级使旧版本精确断言失败
- **THEN** 系统只将断言更新为逐工作流批准的新版本并补齐缺失覆盖
- **AND** 不删除 JDK、Node.js、pnpm、CodeQL、缓存、签名隔离或聚合门禁断言

#### Scenario: 工作流实现文件发生变化
- **WHEN** Pull Request 修改测试、发布、stale 工作流或其仓库契约
- **THEN** 变更范围识别必须触发 Android、Web、CodeQL、OpenSpec 和仓库验证的完整适用门禁
- **AND** 每个实际运行的失败均阻止稳定聚合门禁通过

### Requirement: Gradle Action 缓存升级必须显式选择开放边界并保留可审计失败
使用 `gradle/actions/setup-gradle@v6` 的工作流 MUST 显式选择 100% MIT 的 `basic` 缓存
provider,不得加载默认的 enhanced 商业缓存组件,并将升级后的首次缓存未命中视为预期冷启动
而非构建失败。缓存保存或恢复异常 MUST 在作业摘要或日志中可见,且 Android CodeQL 构建
MUST 继续使用 `--no-build-cache`,不得用缓存命中替代 Java/Kotlin 源码编译证据。

#### Scenario: 缓存 provider 配置漂移
- **WHEN** `setup-gradle@v6` 没有显式选择 `basic` provider 或改为 enhanced provider
- **THEN** 仓库契约检查失败
- **AND** 变更不得在缺少新的独立评审时合并

#### Scenario: 升级后首次运行缓存未命中
- **WHEN** 新缓存协议使既有 Gradle 缓存失效
- **THEN** 工作流重新填充缓存并继续执行真实构建
- **AND** 不将预期缓存未命中报告为应用兼容性回归

#### Scenario: CodeQL Android 任务执行
- **WHEN** CodeQL 分析 Java/Kotlin 源码
- **THEN** Gradle 构建禁用 build cache 并实际编译目标源码
- **AND** 缓存恢复成功不得代替 CodeQL 可处理源码的证据

#### Scenario: 缓存操作失败
- **WHEN** Gradle 缓存保存或恢复失败
- **THEN** 工作流日志或摘要必须暴露该状态
- **AND** 不得伪造缓存成功或隐藏后续真实构建结果
41 changes: 41 additions & 0 deletions openspec/specs/release-verification/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,44 @@
#### Scenario: 任一完成条件缺失
- **WHEN** 发布、回验、任务证据、严格验证或分支清理任一条件尚未完成
- **THEN** 系统不得宣称一次性治理已经完成

### Requirement: 发布工作流内部 artifact 传递必须保持唯一且完整
发布工作流 MUST 将唯一普通正式 APK 以唯一名称上传,并在后续作业按该名称下载到确定路径;
下载结果 MUST 恰好包含预期文件名的一个 APK。artifact 服务报告的摘要或下载内容摘要不一致
时 MUST 失败,不得创建 tag、草稿 Release 或其他发布对象。Action 升级不得改变 APK 名称、
路径、保留期、包名、签名身份或 Release 唯一资产约束。

#### Scenario: 唯一 APK 完整传递
- **WHEN** 构建作业产生唯一预期普通正式 APK 并完成 artifact 上传
- **THEN** 后续作业按唯一 artifact 名称下载到预期目录
- **AND** 下载后只存在文件名、大小和摘要均符合预期的一个 APK

#### Scenario: artifact 摘要不匹配
- **WHEN** 下载内容摘要与 artifact 服务提供的预期摘要不一致
- **THEN** 发布工作流立即失败
- **AND** 不创建 tag、草稿 Release 或替代资产

#### Scenario: artifact 数量或路径漂移
- **WHEN** 下载目录没有 APK、包含多个 APK 或唯一 APK 不在预期路径
- **THEN** 发布工作流立即失败
- **AND** 不通过搜索其他目录或重命名未知文件继续发布

### Requirement: 发布 Action 升级验证不得产生发布副作用
系统 MUST 在不读取正式签名 Secrets、不运行正式构建、不推送 tag、不创建或修改 Release 的
前提下验证发布工作流的触发、权限、目标 Action 版本、唯一 APK 传递和失败即停止合同。只有
替代变更合并后另行取得明确发布授权,才可手动触发正式发布工作流。

#### Scenario: Pull Request 验证发布工作流
- **WHEN** Pull Request 修改发布工作流或其合同
- **THEN** 系统通过静态语法检查和仓库合同验证发布边界
- **AND** 不读取签名 Secrets、不构建正式 APK 且不创建任何发布对象

#### Scenario: 验证需要真实 artifact 闭环
- **WHEN** 静态合同不足以证明升级后的上传下载兼容性
- **THEN** 系统使用无签名、无发布权限的测试夹具执行上传下载闭环
- **AND** 夹具不得被当作 APK、Release 资产或正式发布证据

#### Scenario: 工作流升级已经合并
- **WHEN** 替代变更已合并且维护者尚未明确授权新一轮正式发布
- **THEN** 系统保持发布工作流未触发
- **AND** 不以 CI 绿色推断签名、设备或公开 Release 门禁已经通过
45 changes: 45 additions & 0 deletions openspec/specs/repository-maintenance-governance/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,48 @@
- **WHEN** 自动同步工作流仍引用已撤空仓库或不存在的分支
- **THEN** 系统移除该工作流
- **AND** 不把失败目标机械改成未经批准的新上游

### Requirement: GitHub Actions 大版本升级必须逐项审查并替代失效队列
系统 MUST 从最新 `master` 评估 GitHub Actions 大版本升级,并为目标版本的运行时、runner
下限、凭据、缓存、artifact、权限和事件筛选语义保存可验证的判断。目标组合 MUST 为
`actions/checkout@v7`、`actions/setup-java@v6`、`gradle/actions/setup-gradle@v6`、
`actions/upload-artifact@v7`、`actions/download-artifact@v8`、`actions/stale@v11`、
`pnpm/action-setup@v6` 和 `actions/setup-node@v7`;CodeQL、JDK、Node.js、pnpm、Gradle、
AGP 和应用依赖不得借此变更升级。落后当前 `master` 且无法通过现行仓库契约的机器人 Pull
Request MUST NOT 直接合并,只能在替代 Pull Request 完成评审与全部必需检查后按可追踪的
替代关系关闭。

#### Scenario: 候选分支落后且契约失败
- **WHEN** GitHub Actions 更新 Pull Request 已落后当前 `master`,并因仍要求旧版本的精确
仓库契约而失败
- **THEN** 系统保留该失败作为真实兼容性 RED,并从最新 `master` 建立替代变更
- **AND** 不通过删除、跳过或模糊契约断言使原 Pull Request 可合并

#### Scenario: 任一大版本缺少兼容证据
- **WHEN** 目标 Action 的运行时、runner 下限、权限、缓存、凭据或 artifact 行为尚未完成审查
- **THEN** 系统不得批准或合并该升级
- **AND** 已完成审查的其他 Action 不得被用于推断该目标兼容

#### Scenario: 替代 Pull Request 通过完整门禁
- **WHEN** 从最新 `master` 创建的替代 Pull Request 已通过人工评审和全部必需检查
- **THEN** 系统可以关闭被替代的机器人 Pull Request,并在关闭记录中关联替代 Pull Request
- **AND** 该动作不得被解释为允许普通依赖自动合并

### Requirement: Action 升级必须保持 stale 的问题范围和最小权限
升级后的 stale 工作流 MUST 继续只处理带 `needs-info` 标签且超过 30 天未补充资料的 issue,
MUST 禁止对 Pull Request 执行 stale 或 close,并 MUST 继续豁免崩溃、数据损坏和安全 issue。
工作流权限 MUST 限定为读取仓库内容和写入 issue,不得新增 Pull Request、Release、Actions
或其他写权限。

#### Scenario: Pull Request 长期无活动
- **WHEN** 任意 Pull Request 超过 stale 时间阈值且没有新活动
- **THEN** stale 工作流不得标记或关闭该 Pull Request

#### Scenario: 严重 issue 带有豁免标签
- **WHEN** issue 带有 `crash`、`data-loss` 或 `security` 标签
- **THEN** stale 工作流不得仅因时间流逝标记或关闭该 issue

#### Scenario: needs-info issue 长期未补充
- **WHEN** issue 带有 `needs-info` 标签、没有豁免标签且超过 30 天未补充资料
- **THEN** stale 工作流可以按既有消息和恢复说明关闭该 issue
- **AND** 执行期间只使用 issue 写权限和内容读权限
Loading