ci: separate master serial and PR jobs

Skipped matrix jobs never expand matrix display values, so let GitHub use stable job identifiers. Bind optimized jobs to pull requests and the three serial references to master pushes.
This commit is contained in:
Tianyi Cui
2026-07-22 18:17:06 +08:00
parent 41609c4ce4
commit da5a1fc811
4 changed files with 25 additions and 29 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-21-serial-cross-platform-ci-reference.md: e2cb9dbc5f8778b5a28236cb135f6dcbb979d38b
2026-07-21-serial-cross-platform-ci-reference.zh.md: 85c3403f1acb78e60e3a811503b5b264f84242c7
2026-07-21-serial-cross-platform-ci-reference.md: ffc1fd5b37bc6c9e3427ee55a55300f93a1292f3
2026-07-21-serial-cross-platform-ci-reference.zh.md: d7f87916865b83973abe6b0708203618cf536c8e

View File

@@ -6,7 +6,7 @@ English | [中文](2026-07-21-serial-cross-platform-ci-reference.zh.md)
## Problem
The pull-request workflow reaches its latency targets by partitioning static checks, lint, coverage, snapshot replay, and artifact validation across explicit GitHub jobs. Those partitions are exhaustively checked in code, but the optimized workflow still should not be its own only completeness oracle: a defect shared by shard selection and its inventory test could omit work while every optimized lane stays green.
The pull-request workflow reaches its latency targets by scheduling the complete primary Node inventory concurrently inside one larger runner. The optimized scheduler still should not be its own only completeness oracle: a defect in its gate inventory or dependency graph could omit work while the optimized job stays green.
Encoding the one-minute non-Windows target and three-minute Windows target as job timeouts creates a separate failure mode. Hosted-runner startup and performance vary, so a correct gate can be cancelled at the target boundary before it emits useful diagnostics. The performance objective needs measurement against GitHub timestamps, while correctness needs enough time to finish.
@@ -14,25 +14,25 @@ Reviewers also need a direct answer to a simpler question: what happens when the
## Decision
[CI](../../../../.github/workflows/ci.yml) accepts `workflow_dispatch` in addition to its normal push and pull-request events. A manual dispatch skips the optimized and compatibility jobs and exposes three explicit jobs named `serial / linux`, `serial / macos`, and `serial / windows`. They intentionally duplicate their short checkout, runtime setup, and immutable install sequences instead of hiding the operating systems behind a matrix or reusable workflow.
[CI](../../../../.github/workflows/ci.yml) gives pull-request and master-push events complementary responsibilities. Pull requests run only the optimized larger-runner and compatibility jobs. A push to `master` skips those jobs and runs three explicit references named `serial / linux`, `serial / macos`, and `serial / windows`. They intentionally duplicate their short checkout, runtime setup, and immutable install sequences instead of hiding the operating systems behind a matrix or reusable workflow. `workflow_dispatch` is reserved for runner benchmarks.
Each reference job runs `pnpm run check:ci` without any shard selector. `DSH_GATE_CONCURRENCY=1` makes the top-level aggregate execute one ready gate at a time; coverage, snapshot replay, built-bin smoke, and publication validation also receive worker counts of one. The three operating-system jobs may run beside one another, but each host's repository gates are serial and complete. Linux installs bubblewrap before replaying snapshots, and Windows enables Developer Mode before installing the symlinked workspace.
Manual reference jobs are diagnostic and do not participate in the required `all checks passed` result. Pull-request and push events continue to run only the optimized lanes. The one-minute non-Windows and three-minute Windows objectives are evaluated from completed hosted-job timestamps and reported as measurements; they are not `timeout-minutes` values.
Master reference jobs are diagnostic and do not participate in the pull request's required `all checks passed` result. A pull request runs only the optimized jobs; a master push runs only the three serial references. The one-minute non-Windows and three-minute Windows objectives are evaluated from completed hosted-job timestamps and reported as measurements; they are not `timeout-minutes` values.
The portable reference uses GitHub's standard `ubuntu-latest`, `macos-latest`, and `windows-2025` labels. A higher-core hosted runner remains a possible future benchmark, but it is not the default: larger runners require organization-owned labels and provisioning, while a reference oracle should remain runnable without repository-external runner configuration. Provisioning one later can change the performance experiment without changing this correctness baseline.
## Alternatives considered
- **Set each timeout equal to its latency target** - rejected because scheduling variance would cancel correct work and suppress the evidence needed to diagnose a regression.
- **Trust only the optimized shard inventory** - rejected because selection and validation share implementation assumptions; an unsharded aggregate is an independent completeness check.
- **Trust only the concurrent primary inventory** - rejected because scheduling and validation share implementation assumptions; a serial aggregate is an independent completeness check.
- **Run the serial references on every pull request** - rejected because they deliberately trade wall time and runner consumption for simplicity and are not needed in the fast feedback loop.
- **Use one operating-system matrix** - rejected because three named jobs make the reference surface visible without another selection mechanism.
- **Move the fast workflow to larger runners now** - rejected as the portable default because it would couple ordinary CI to organization-specific runner capacity. It remains an opt-in experiment after such capacity has an owned label and budget.
- **Run the serial reference on larger runners** - rejected because the reference is the portable fallback for the organization-specific pull-request topology. The fast pull-request path uses provisioned larger runners; the serial master path keeps standard labels.
## Consequences
The workflow contains duplicated setup steps and a manual reference run can take much longer than the optimized pull-request path. That duplication is deliberate: reviewers can inspect each operating system's complete command without resolving a matrix or shard inventory.
The workflow contains duplicated setup steps and a master reference run can take much longer than the optimized pull-request path. That duplication is deliberate: reviewers can inspect each operating system's complete command without resolving a matrix or concurrent scheduler.
The reference may expose platform failures that the optimized blocking set does not yet claim to support, especially on Windows. Such a failure is evidence about current cross-platform behavior rather than a reason to weaken or silently skip the aggregate.

View File

@@ -6,7 +6,7 @@ Status: implemented
## 问题
拉取请求工作流通过把静态检查、lint、覆盖率、快照回放和产物验证拆分到显式的 GitHub 作业中来达到延迟目标。这些分区由代码穷举校验,但优化工作流仍不应成为自身唯一的完整性判定基准:如果分片选择逻辑及其清单测试存在同一缺陷,即使所有优化通道都保持绿灯,也可能漏掉部分工作。
拉取请求工作流通过在一台更大型运行器内并发调度完整的主 Node 门禁清单来达到延迟目标。优化调度器仍不应成为自身唯一的完整性判定基准:如果其门禁清单或依赖图存在缺陷,即使优化作业保持绿灯,也可能漏掉部分工作。
将非 Windows 作业的 1 分钟目标和 Windows 作业的 3 分钟目标写成作业超时,会引入另一种失败模式。托管运行器的启动时间和性能会波动,因此即使门禁本身正确,也可能在到达目标时间边界时被取消,来不及输出有用的诊断信息。性能目标需要根据 GitHub 时间戳衡量,而正确性验证需要给门禁留足完成时间。
@@ -14,25 +14,25 @@ Status: implemented
## 决策
[CI](../../../../.github/workflows/ci.yml) 除正常的 push 和拉取请求事件外,也接受 `workflow_dispatch`。手动触发时会跳过优化作业和兼容性作业,只提供三个名`serial / linux``serial / macos``serial / windows` 的显式作业。这些作业有意分别重复简短的代码检出、运行时设置和依赖锁定的安装步骤,不用矩阵或可复用工作流把操作系统差异隐藏起来。
[CI](../../../../.github/workflows/ci.yml) 为拉取请求事件与 master 推送事件赋予互补的职责。拉取请求只运行使用更大型运行器的优化作业和兼容性作业。向 `master` 推送时会跳过这些作业,改为运行三个显式参考作业,名称分别`serial / linux``serial / macos``serial / windows`。这些作业有意分别重复简短的代码检出、运行时设置和依赖锁定的安装步骤,不用矩阵或可复用工作流把操作系统差异隐藏起来。`workflow_dispatch` 仅用于运行器基准测试。
每个参考作业均在不设置任何分片选择器的情况下运行 `pnpm run check:ci``DSH_GATE_CONCURRENCY=1` 使顶层聚合每次只执行一个已经就绪的门禁覆盖率、快照回放、built-bin 冒烟测试和发布验证的并发数也设为 1。三种操作系统的作业可以彼此并行但每台主机上的仓库门禁都串行运行且完整执行。Linux 在回放快照前安装 bubblewrapWindows 则在安装采用符号链接的工作区前启用开发人员模式。
手动参考作业仅用于诊断,不参与必需`all checks passed` 结果。拉取请求和 push 事件仍只运行优化通道。系统根据已完成托管作业的时间戳评估非 Windows 作业的 1 分钟目标和 Windows 作业的 3 分钟目标,并将其报告为测量结果,而不是写成 `timeout-minutes` 值。
master 分支的参考作业仅用于诊断,不参与拉取请求所要求`all checks passed` 结果。拉取请求只运行优化作业;向 master 推送时只运行三个串行参考作业。系统根据已完成托管作业的时间戳评估非 Windows 作业的 1 分钟目标和 Windows 作业的 3 分钟目标,并将其报告为测量结果,而不是写成 `timeout-minutes` 值。
可移植的参考流程使用 GitHub 标准的 `ubuntu-latest``macos-latest``windows-2025` 标签。仍可将更高核心数的托管运行器作为未来的基准测试,但不将其设为默认选择:更大型运行器需要组织自有的标签和预配,而参考判定基准应无需仓库外部的运行器配置即可运行。日后完成这类预配,可以改变性能实验而无需改变该正确性基线。
## 曾考虑的替代方案
- **将每个超时值设为相应延迟目标**:不予采纳,因为调度波动会中止原本正确的执行,并使诊断回归所需的证据无法产生。
- **仅信任优化分片清单**:不予采纳,因为选择逻辑与校验逻辑共享实现假设;无分片的聚合流程是一项独立的完整性检查。
- **仅信任并发执行的主门禁清单**:不予采纳,因为调度逻辑与校验逻辑共享实现假设;串行聚合流程是一项独立的完整性检查。
- **在每个拉取请求上运行串行参考作业**:不予采纳,因为这些作业有意以更长的总耗时和更多运行器用量换取简单性,快速反馈循环不需要它们。
- **使用一个操作系统矩阵**:不予采纳,因为三个具名作业无需另一套选择机制,就能让参考流程的构成清晰可见。
- **立即把快速工作流迁移到更大型运行器**:不作为可移植的默认方案,因为这会让常规 CI 与特定组织的运行器容量耦合。等到这类容量拥有明确归属的标签和预算后,仍可将其作为一项可选实验
- **在更大型运行器上运行串行参考流程**:不予采纳,因为该参考流程是特定组织拉取请求拓扑的可移植后备方案。快速拉取请求路径使用已预配的更大型运行器;串行 master 路径保留标准标签
## 后果
工作流包含重复的设置步骤,手动参考运行也可能比优化后的拉取请求路径耗时长得多。这些重复是有意保留的:评审人无需解析矩阵或分片清单,就能直接检查每种操作系统执行的完整命令。
工作流包含重复的设置步骤,master 参考运行也可能比优化后的拉取请求路径耗时长得多。这些重复是有意保留的:评审人无需解析矩阵或并发调度器,就能直接检查每种操作系统执行的完整命令。
参考流程可能暴露某些平台上的故障而优化后的阻塞门禁集合尚未声明支持这些平台Windows 尤其如此。这类失败反映了当前的跨平台行为,不应成为削弱或静默跳过该聚合流程的理由。

View File

@@ -2,20 +2,18 @@ name: CI
on:
push:
branches: [main, master]
branches: [master]
pull_request:
workflow_dispatch:
inputs:
suite:
description: Manual CI suite to run
required: true
default: serial-reference
default: larger-runner-benchmark
type: choice
options:
- serial-reference
- larger-runner-benchmark
- consolidated-runner-benchmark
- optimized-larger-runners
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -33,7 +31,7 @@ jobs:
# overlaps the complete unsharded primary Node inventory. Build starts eagerly;
# only consumers of emitted output wait for it.
node-24:
if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
if: github.event_name == 'pull_request'
runs-on: dsh-ubuntu-24-04-96core
name: node 24 / complete
env:
@@ -88,7 +86,7 @@ jobs:
node-compat:
if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
if: github.event_name == 'pull_request'
# Distinct larger-runner pools avoid both standard-runner setup outliers and
# delayed allocation when independent environment contracts share one pool.
runs-on: ${{ matrix.runner }}
@@ -135,7 +133,7 @@ jobs:
run: pnpm run check:node-compat
python-sdk:
if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
if: github.event_name == 'pull_request'
runs-on: dsh-ubuntu-24-04-8core
name: python 3.10 / keyless SDK
steps:
@@ -156,7 +154,7 @@ jobs:
# complete observational portability inventory. run-gates reports failures
# from observational gates without allowing them to fail the required job.
windows:
if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
if: github.event_name == 'pull_request'
runs-on: dsh-windows-2025-32core
name: windows node 24 / complete
env:
@@ -208,12 +206,12 @@ jobs:
shell: pwsh
run: pnpm run check:ci:windows-complete
# Manual reference runs deliberately avoid the optimized jobs above.
# Master pushes run only the serial reference jobs below.
# Each host executes the complete, unsharded primary Node aggregate with one
# gate worker, giving reviewers a simple cross-platform oracle for completeness
# and timing.
serial-linux:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / linux
runs-on: ubuntu-latest
steps:
@@ -242,7 +240,7 @@ jobs:
run: pnpm run check:ci
serial-macos:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / macos
runs-on: macos-latest
steps:
@@ -268,7 +266,7 @@ jobs:
run: pnpm run check:ci
serial-windows:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: serial / windows
runs-on: windows-2025
steps:
@@ -306,7 +304,6 @@ jobs:
# The named pools are restricted at the organization level to this repository.
larger-runner-benchmark:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'larger-runner-benchmark'
name: larger runner / ${{ matrix.platform }} / ${{ matrix.cores }} cores / ${{ matrix.workload }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
@@ -407,7 +404,6 @@ jobs:
# Windows runs both blocking build surfaces concurrently through run-gates.
consolidated-runner-benchmark:
if: github.event_name == 'workflow_dispatch' && inputs.suite == 'consolidated-runner-benchmark'
name: consolidated / ${{ matrix.platform }} / ${{ matrix.cores }} cores
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
@@ -556,7 +552,7 @@ jobs:
name: all checks passed
runs-on: ubuntu-latest
needs: [node-24, node-compat, python-sdk, windows]
if: always() && (github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners')
if: always() && github.event_name == 'pull_request'
steps:
- name: Fail if any needed job did not succeed
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')