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
40 changes: 32 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,13 @@ jobs:
name: Phase 1 real-authority conformance
runs-on: macos-15
needs: [web, changes]
if: needs.changes.outputs.docs_only != 'true'
# macOS bills at ten times the Linux rate and this job is the longest one
# here, so it runs on main and on a pull request that asks for it by
# carrying the `ci:full` label.
if: >-
needs.changes.outputs.docs_only != 'true'
&& ((github.event_name == 'push' && github.ref == 'refs/heads/main')
|| contains(github.event.pull_request.labels.*.name, 'ci:full'))
timeout-minutes: 120
permissions:
contents: read
Expand Down Expand Up @@ -458,7 +464,15 @@ jobs:
needs: changes
# macOS runners bill at ten times the Linux rate. Spending that on a branch
# that changed only prose is the least defensible minute in this file.
if: needs.changes.outputs.docs_only != 'true'
#
# It cannot move to Linux: it cross-builds the Windows supervisor from an
# arm64 Homebrew mingw-w64 bottle, pinned by digest below. So it runs on
# main, and on a pull request that carries the `ci:full` label -- which any
# branch touching src-tauri or the supervisors should.
if: >-
needs.changes.outputs.docs_only != 'true'
&& ((github.event_name == 'push' && github.ref == 'refs/heads/main')
|| contains(github.event.pull_request.labels.*.name, 'ci:full'))
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
Expand Down Expand Up @@ -589,12 +603,16 @@ jobs:
timeout-minutes: 15
strategy:
fail-fast: false
# linux-x64 runs everywhere. darwin-arm64 is a macOS runner, so it
# joins on main or under the `ci:full` label. A matrix entry cannot
# carry its own condition, so the include list is selected here.
matrix:
include:
- platform: linux-x64
runner: ubuntu-24.04
- platform: darwin-arm64
runner: macos-14
include: >-
${{ fromJSON(
((github.event_name == 'push' && github.ref == 'refs/heads/main')
|| contains(github.event.pull_request.labels.*.name, 'ci:full'))
&& '[{"platform":"linux-x64","runner":"ubuntu-24.04"},{"platform":"darwin-arm64","runner":"macos-14"}]'
|| '[{"platform":"linux-x64","runner":"ubuntu-24.04"}]') }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
Expand All @@ -606,7 +624,13 @@ jobs:
name: Windows supervisor behavior
runs-on: windows-2025
needs: [changes, rust]
if: always() && needs.rust.result == 'success' && needs.changes.outputs.docs_only != 'true'
# Windows bills at twice the Linux rate, and this job needs the artifact
# `rust` produces, so it follows the same gate.
if: >-
always() && needs.rust.result == 'success'
&& needs.changes.outputs.docs_only != 'true'
&& ((github.event_name == 'push' && github.ref == 'refs/heads/main')
|| contains(github.event.pull_request.labels.*.name, 'ci:full'))
timeout-minutes: 20
permissions:
contents: read
Expand Down
4 changes: 2 additions & 2 deletions phase1-conformance.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@
},
{
"path": ".github/workflows/ci.yml",
"blob": "bd10a0a97f7d450086319e9e644591337c574e7b",
"sha256": "62fe688a15e0ca7d8a244af51f3a6c87a2624e2ded8e312c2bb3590542ca6dc2"
"blob": "5dee6b72205f7098b832a0da6c996afad3f5874e",
"sha256": "c3659b47808dcde989c10c7b37efe0845c743e18b25381182748329ce1e6cc71"
},
{
"path": ".github/workflows/client-v1-conformance.yml",
Expand Down
9 changes: 7 additions & 2 deletions src/client-v1-conformance-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2239,8 +2239,13 @@ describe('Chat-local protected Windows conformance workflow', () => {
const workflow = readFileSync(resolve(projectRoot, '.github', 'workflows', 'ci.yml'), 'utf8');
const job = workflowJob(workflow, 'unix-supervisor');
expect(job).toContain('runs-on: $' + '{{ matrix.runner }}');
expect(job).toContain('runner: ubuntu-24.04');
expect(job).toContain('runner: macos-14');
// The include list is selected by expression -- linux-x64 on every branch,
// darwin-arm64 on main or under the `ci:full` label -- because a matrix
// entry cannot carry its own condition. Both platforms must still be
// reachable, and the macOS one must be the gated half.
expect(job).toContain('"platform":"linux-x64","runner":"ubuntu-24.04"');
expect(job).toContain('"platform":"darwin-arm64","runner":"macos-14"');
expect(job).toContain("contains(github.event.pull_request.labels.*.name, 'ci:full')");
expect(job).toContain('bash scripts/unix-producer-supervisor.test.sh');

const runtimeTest = readFileSync(
Expand Down
32 changes: 29 additions & 3 deletions src/specification-guards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,11 +1042,37 @@ describe('Phase 1 specification guards', () => {
expect(workflow).toMatch(/^ {2}changes:$/m);
expect(workflow).toContain('docs_only: $' + '{{ steps.classify.outputs.docs_only }}');

const gatedJobs = [...workflowJobs(workflow)]
.filter(([, job]) => /^ {4}if: needs\.changes\.outputs\.docs_only != 'true'$/m.test(job))
const jobs = new Map(workflowJobs(workflow));
const gatedJobs = [...jobs]
.filter(([, job]) => job.includes("needs.changes.outputs.docs_only != 'true'"))
.map(([name]) => name);

expect(gatedJobs).toEqual(['e2e', 'phase1-conformance', 'desktop', 'rust', 'unix-supervisor']);
expect(gatedJobs).toEqual([
'e2e',
'phase1-conformance',
'desktop',
'rust',
'unix-supervisor',
'windows-supervisor-behavior',
]);

// Prose is not the only minute worth not spending. macOS bills at ten
// times the Linux rate and Windows at twice it, so those runners also wait
// for main -- or for a pull request that asks for them by carrying the
// `ci:full` label, which any branch touching src-tauri or the supervisors
// should. `unix-supervisor` keeps linux-x64 on every branch and admits
// darwin-arm64 under the same gate, so it carries the expression in its
// matrix rather than in an `if`.
for (const name of [
'phase1-conformance',
'rust',
'unix-supervisor',
'windows-supervisor-behavior',
]) {
const job = jobs.get(name) ?? '';
expect(job).toContain("github.event_name == 'push' && github.ref == 'refs/heads/main'");
expect(job).toContain("contains(github.event.pull_request.labels.*.name, 'ci:full')");
}

// The classification has to fail towards running everything. A wrong guess
// that way wastes a few minutes; the other way merges untested code.
Expand Down
Loading