Skip to content

Commit e0ba985

Browse files
committed
Gate preflight jobs by affected changes
Use the preflight dependency graph to decide which CI jobs need to run so pull requests skip unrelated checks.
1 parent 4814cbe commit e0ba985

3 files changed

Lines changed: 279 additions & 15 deletions

File tree

.github/workflows/preflight.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,54 @@ permissions:
88
contents: read
99

1010
jobs:
11+
detect:
12+
name: Detect Affected Jobs
13+
runs-on: ubuntu-latest
14+
outputs:
15+
run_biome_format: ${{ steps.detect.outputs.run_biome_format }}
16+
run_typescript_packages: ${{ steps.detect.outputs.run_typescript_packages }}
17+
run_extension: ${{ steps.detect.outputs.run_extension }}
18+
run_docs: ${{ steps.detect.outputs.run_docs }}
19+
run_landing: ${{ steps.detect.outputs.run_landing }}
20+
run_demo: ${{ steps.detect.outputs.run_demo }}
21+
run_playground: ${{ steps.detect.outputs.run_playground }}
22+
run_desktop_macos: ${{ steps.detect.outputs.run_desktop_macos }}
23+
run_python_sdk: ${{ steps.detect.outputs.run_python_sdk }}
24+
run_rust_sdk: ${{ steps.detect.outputs.run_rust_sdk }}
25+
run_go_sdk: ${{ steps.detect.outputs.run_go_sdk }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
with:
29+
fetch-depth: 0
30+
31+
- name: Setup Bun
32+
uses: oven-sh/setup-bun@v2
33+
34+
- name: Detect affected preflight jobs
35+
id: detect
36+
run: bun run scripts/preflight.ts --list --since "${{ github.event.pull_request.base.sha }}" --github-output "$GITHUB_OUTPUT"
37+
38+
biome-format:
39+
name: Biome Format Check
40+
needs: detect
41+
if: needs.detect.outputs.run_biome_format == 'true'
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@v4
45+
46+
- name: Setup Bun
47+
uses: oven-sh/setup-bun@v2
48+
49+
- name: Install workspace dependencies
50+
run: bun install --frozen-lockfile
51+
52+
- name: Check TypeScript formatting
53+
run: bun run format:check
54+
1155
typescript-packages:
1256
name: TypeScript Packages
57+
needs: detect
58+
if: needs.detect.outputs.run_typescript_packages == 'true'
1359
runs-on: ubuntu-latest
1460
steps:
1561
- uses: actions/checkout@v4
@@ -28,6 +74,8 @@ jobs:
2874

2975
extension:
3076
name: Chrome Extension
77+
needs: detect
78+
if: needs.detect.outputs.run_extension == 'true'
3179
runs-on: ubuntu-latest
3280
steps:
3381
- uses: actions/checkout@v4
@@ -48,6 +96,8 @@ jobs:
4896

4997
docs:
5098
name: Docs
99+
needs: detect
100+
if: needs.detect.outputs.run_docs == 'true'
51101
runs-on: ubuntu-latest
52102
steps:
53103
- uses: actions/checkout@v4
@@ -74,6 +124,8 @@ jobs:
74124

75125
landing:
76126
name: Landing Site
127+
needs: detect
128+
if: needs.detect.outputs.run_landing == 'true'
77129
runs-on: ubuntu-latest
78130
steps:
79131
- uses: actions/checkout@v4
@@ -96,6 +148,8 @@ jobs:
96148

97149
demo:
98150
name: Demo Site
151+
needs: detect
152+
if: needs.detect.outputs.run_demo == 'true'
99153
runs-on: ubuntu-latest
100154
steps:
101155
- uses: actions/checkout@v4
@@ -115,6 +169,8 @@ jobs:
115169

116170
playground:
117171
name: Playground
172+
needs: detect
173+
if: needs.detect.outputs.run_playground == 'true'
118174
runs-on: ubuntu-latest
119175
steps:
120176
- uses: actions/checkout@v4
@@ -134,6 +190,8 @@ jobs:
134190

135191
desktop-macos:
136192
name: Desktop macOS Smoke Build
193+
needs: detect
194+
if: needs.detect.outputs.run_desktop_macos == 'true'
137195
runs-on: macos-latest
138196
steps:
139197
- uses: actions/checkout@v4
@@ -157,6 +215,8 @@ jobs:
157215

158216
python-sdk:
159217
name: Python SDK
218+
needs: detect
219+
if: needs.detect.outputs.run_python_sdk == 'true'
160220
runs-on: ubuntu-latest
161221
steps:
162222
- uses: actions/checkout@v4
@@ -174,6 +234,8 @@ jobs:
174234

175235
rust-sdk:
176236
name: Rust SDK
237+
needs: detect
238+
if: needs.detect.outputs.run_rust_sdk == 'true'
177239
runs-on: ubuntu-latest
178240
steps:
179241
- uses: actions/checkout@v4
@@ -187,6 +249,8 @@ jobs:
187249

188250
go-sdk:
189251
name: Go SDK
252+
needs: detect
253+
if: needs.detect.outputs.run_go_sdk == 'true'
190254
runs-on: ubuntu-latest
191255
steps:
192256
- uses: actions/checkout@v4

scripts/preflight.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { describe, expect, test } from "bun:test";
2+
3+
import { buildComponents, computeAffected, computeCiJobPlan } from "./preflight";
4+
5+
const components = buildComponents();
6+
7+
function getCiJobPlan(changedFiles: string[]) {
8+
const { ordered, repoWide } = computeAffected(components, changedFiles, false);
9+
return computeCiJobPlan(ordered, changedFiles, repoWide);
10+
}
11+
12+
describe("preflight CI detection", () => {
13+
test("root Bun dependency changes only trigger Bun-backed jobs", () => {
14+
const plan = getCiJobPlan(["package.json"]);
15+
16+
expect(plan.run_biome_format).toBe(true);
17+
expect(plan.run_typescript_packages).toBe(true);
18+
expect(plan.run_extension).toBe(true);
19+
expect(plan.run_docs).toBe(true);
20+
expect(plan.run_landing).toBe(true);
21+
expect(plan.run_demo).toBe(true);
22+
expect(plan.run_playground).toBe(true);
23+
expect(plan.run_desktop_macos).toBe(true);
24+
expect(plan.run_python_sdk).toBe(false);
25+
expect(plan.run_rust_sdk).toBe(false);
26+
expect(plan.run_go_sdk).toBe(false);
27+
});
28+
29+
test("Rust SDK changes propagate to desktop but not other language jobs", () => {
30+
const plan = getCiJobPlan(["packages/rust/slop-ai/Cargo.toml"]);
31+
32+
expect(plan.run_biome_format).toBe(false);
33+
expect(plan.run_typescript_packages).toBe(false);
34+
expect(plan.run_extension).toBe(false);
35+
expect(plan.run_docs).toBe(false);
36+
expect(plan.run_landing).toBe(false);
37+
expect(plan.run_demo).toBe(false);
38+
expect(plan.run_playground).toBe(false);
39+
expect(plan.run_desktop_macos).toBe(true);
40+
expect(plan.run_python_sdk).toBe(false);
41+
expect(plan.run_rust_sdk).toBe(true);
42+
expect(plan.run_go_sdk).toBe(false);
43+
});
44+
45+
test("Biome config changes only trigger the formatting job", () => {
46+
const plan = getCiJobPlan(["biome.json"]);
47+
48+
expect(plan.run_biome_format).toBe(true);
49+
expect(plan.run_typescript_packages).toBe(false);
50+
expect(plan.run_extension).toBe(false);
51+
expect(plan.run_docs).toBe(false);
52+
expect(plan.run_landing).toBe(false);
53+
expect(plan.run_demo).toBe(false);
54+
expect(plan.run_playground).toBe(false);
55+
expect(plan.run_desktop_macos).toBe(false);
56+
expect(plan.run_python_sdk).toBe(false);
57+
expect(plan.run_rust_sdk).toBe(false);
58+
expect(plan.run_go_sdk).toBe(false);
59+
});
60+
61+
test("root TypeScript build script changes trigger only jobs that use it", () => {
62+
const plan = getCiJobPlan(["scripts/build-typescript-packages.ts"]);
63+
64+
expect(plan.run_biome_format).toBe(true);
65+
expect(plan.run_typescript_packages).toBe(true);
66+
expect(plan.run_extension).toBe(false);
67+
expect(plan.run_docs).toBe(false);
68+
expect(plan.run_landing).toBe(false);
69+
expect(plan.run_demo).toBe(true);
70+
expect(plan.run_playground).toBe(true);
71+
expect(plan.run_desktop_macos).toBe(false);
72+
expect(plan.run_python_sdk).toBe(false);
73+
expect(plan.run_rust_sdk).toBe(false);
74+
expect(plan.run_go_sdk).toBe(false);
75+
});
76+
77+
test("workflow changes force every preflight job to run", () => {
78+
const plan = getCiJobPlan([".github/workflows/preflight.yml"]);
79+
80+
for (const shouldRun of Object.values(plan)) {
81+
expect(shouldRun).toBe(true);
82+
}
83+
});
84+
});

0 commit comments

Comments
 (0)