@@ -8,6 +8,37 @@ permissions:
88 contents : read
99
1010jobs :
11+ # Which checks a pull request's changes can affect (.github/scripts/ci-changes.sh). A job is
12+ # skipped only when the script prints `false` for its flag; an unset flag, and every push or
13+ # dispatch (the deploy chain's checks), runs it. Every gated job needs this one, so its failure
14+ # skips them and fails `ci` below.
15+ changes :
16+ name : changes
17+ runs-on : &runner-2vcpu ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-2vcpu-ubuntu-2404' || 'ubuntu-latest' }}
18+ timeout-minutes : 5
19+ outputs :
20+ full : ${{ github.event_name != 'pull_request' || steps.classify.outputs.full != 'false' }}
21+ test : ${{ github.event_name != 'pull_request' || steps.classify.outputs.test != 'false' }}
22+ build : ${{ github.event_name != 'pull_request' || steps.classify.outputs.build != 'false' }}
23+ desktop_live : ${{ github.event_name != 'pull_request' || steps.classify.outputs.desktop_live != 'false' }}
24+ steps :
25+ - name : Checkout code (mirror)
26+ if : github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
27+ uses : useblacksmith/checkout@25227e61ff9dafe400e22fa487b673eac4e4409a # v1.8.1
28+ with :
29+ fetch-depth : 2
30+ - name : Checkout code
31+ if : github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
32+ uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
33+ with :
34+ fetch-depth : 2
35+ - name : Classify the pull request's changes
36+ id : classify
37+ if : github.event_name == 'pull_request'
38+ env :
39+ BASE : ${{ github.event.pull_request.base.sha }}
40+ run : bash .github/scripts/ci-changes.sh "$BASE" >> "$GITHUB_OUTPUT"
41+
1142 integration :
1243 # Runs the real-infrastructure test layer: every `*.integration.ts` in packages/db and
1344 # apps/sim, discovered by glob (`vitest run --mode integration`), against the database each
1950 # shard runs its files one at a time against its own database. Files run serially, so a shard
2051 # barely uses more than one core: 4 vCPU is enough.
2152 name : integration (${{ matrix.provision }}, ${{ matrix.shard }}/4)
53+ needs : changes
54+ if : needs.changes.outputs.full == 'true'
2255 runs-on : &runner-4vcpu ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
2356 timeout-minutes : 20
2457 strategy :
@@ -143,6 +176,8 @@ jobs:
143176 # self-hosted suites fit on 4.
144177 e2e :
145178 name : e2e (${{ matrix.group }})
179+ needs : changes
180+ if : needs.changes.outputs.full == 'true'
146181 runs-on : ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && matrix.runner || 'ubuntu-latest' }}
147182 timeout-minutes : ${{ matrix.group == 'mobile' && 40 || 20 }}
148183 strategy :
@@ -217,39 +252,12 @@ jobs:
217252 if-no-files-found : ignore
218253 retention-days : 7
219254
220- # Pull requests skip the live desktop suite only when every change is clearly unrelated to the
221- # app it drives (docs, other apps, published content). Anything else, and any failure to work
222- # out the diff, runs it: a pull request that skipped it wrongly would first fail on staging.
223- desktop-changes :
224- name : desktop-changes
225- runs-on : &runner-2vcpu ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-2vcpu-ubuntu-2404' || 'ubuntu-latest' }}
226- timeout-minutes : 5
227- outputs :
228- changed : ${{ github.event_name != 'pull_request' || steps.diff.outputs.changed != 'false' }}
229- steps :
230- - name : Checkout code (mirror)
231- if : github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
232- uses : useblacksmith/checkout@25227e61ff9dafe400e22fa487b673eac4e4409a # v1.8.1
233- with :
234- fetch-depth : 2
235- - name : Checkout code
236- if : github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork
237- uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
238- with :
239- fetch-depth : 2
240- - name : Diff against the pull request's base
241- id : diff
242- if : github.event_name == 'pull_request'
243- env :
244- BASE : ${{ github.event.pull_request.base.sha }}
245- run : bash .github/scripts/desktop-live-changes.sh "$BASE" >> "$GITHUB_OUTPUT"
246-
247255 # Desktop tools in the real Electron app against a local app, on its own runner: the
248256 # Electron app, the dev app and its realtime server together outgrow the e2e runner.
249257 desktop-live :
250258 name : desktop-live
251- needs : desktop- changes
252- if : needs.desktop- changes.outputs.changed == 'true'
259+ needs : changes
260+ if : needs.changes.outputs.desktop_live == 'true'
253261 runs-on : ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-latest' }}
254262 timeout-minutes : 30
255263 services :
@@ -592,6 +600,8 @@ jobs:
592600 # task hash, so a shard only ever replays its own result.
593601 test :
594602 name : test (${{ matrix.shard }}/2)
603+ needs : changes
604+ if : needs.changes.outputs.test == 'true'
595605 runs-on : ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}
596606 timeout-minutes : 15
597607 strategy :
@@ -660,6 +670,8 @@ jobs:
660670 # not the native Turbopack workers that dominate, so it cannot prevent this.
661671 build :
662672 name : build
673+ needs : changes
674+ if : needs.changes.outputs.build == 'true'
663675 runs-on : ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-16vcpu-ubuntu-2404' || 'linux-x64-8-core' }}
664676 # Build durations crossed 15 minutes as the app grew (10m02 on Jul 29 AM,
665677 # 14m44 after the folders/desktop/library merges, then two straight
@@ -713,25 +725,22 @@ jobs:
713725 run : bunx turbo run build --filter=@sim/app
714726
715727 # One status for every check above: the single check to require on a branch ruleset, so adding,
716- # sharding or renaming a job never means editing the ruleset. Skipped is a pass (the desktop live
717- # suite skips on pull requests that cannot affect it); a failure or a cancellation is not.
728+ # sharding or renaming a job never means editing the ruleset. .github/scripts/ci-gate.sh decides:
729+ # a skip passes only for a gated job whose `changes` flag is exactly `false`; any other skip (a
730+ # failed or skipped `changes`, a lost output, an ungated job) fails, as does a failure or a
731+ # cancellation. A failed checkout fails the gate too.
718732 # `always()`, not `!cancelled()`: a skipped job satisfies a required check, so a gate that skips
719733 # on a cancelled run would report a cancelled head commit as passing.
720734 ci :
721735 name : ci
722- needs : [integration, e2e, desktop-changes , desktop-live, lint, test, build]
736+ needs : [changes, integration, e2e , desktop-live, lint, test, build]
723737 if : ${{ always() }}
724738 runs-on : *runner-2vcpu
725739 timeout-minutes : 5
726740 steps :
741+ - *checkout-mirror
742+ - *checkout-plain
727743 - name : Require every check to pass
728744 env :
729- RESULTS : ${{ toJSON(needs) }}
730- run : |
731- failed="$(jq -r 'to_entries[] | select(.value.result != "success" and .value.result != "skipped") | "\(.key): \(.value.result)"' <<< "$RESULTS")"
732- if [ -n "$failed" ]; then
733- echo "::error::Checks did not pass:"
734- echo "$failed"
735- exit 1
736- fi
737- echo "All checks passed."
745+ NEEDS : ${{ toJSON(needs) }}
746+ run : bash .github/scripts/ci-gate.sh
0 commit comments