Skip to content

Commit 7f0f381

Browse files
committed
🤖 ci: use paths-filter for selective test execution
Replace coverage-based test selection with simpler paths-filter approach. Changes: - Add 'changes' job using dorny/paths-filter@v3 to detect file categories - Categories: docs-only, browser-only, node-backend, ci-config - Jobs check should-run and complete successfully (not skipped) when not needed - This keeps required CI checks passing while saving compute Skip logic: - Docs-only: skip unit, integration, e2e, storybook, smoke tests - Browser-only: skip integration tests (no backend impact) - CI config changes: always run all tests (safety) - Manual test_filter: run requested tests only Jobs complete with notice and summary instead of being marked 'skipped', so required checks still pass. Removed: - scripts/selective-tests/ (coverage map generation) - .github/workflows/coverage-map.yml _Generated with `mux`_
1 parent 129554e commit 7f0f381

File tree

6 files changed

+189
-1039
lines changed

6 files changed

+189
-1039
lines changed

.github/workflows/ci.yml

Lines changed: 189 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,49 @@ concurrency:
1818
cancel-in-progress: true
1919

2020
jobs:
21+
# Detect what files changed to enable selective test execution
22+
changes:
23+
name: Detect Changes
24+
runs-on: ubuntu-latest
25+
outputs:
26+
docs-only: ${{ steps.filter.outputs.docs-only }}
27+
browser-only: ${{ steps.filter.outputs.browser-only }}
28+
node-backend: ${{ steps.filter.outputs.node-backend }}
29+
ci-config: ${{ steps.filter.outputs.ci-config }}
30+
steps:
31+
- name: Checkout code
32+
uses: actions/checkout@v4
33+
34+
- name: Detect file changes
35+
uses: dorny/paths-filter@v3
36+
id: filter
37+
with:
38+
filters: |
39+
docs-only:
40+
- 'docs/**'
41+
- '*.md'
42+
- 'LICENSE'
43+
- '.vscode/**'
44+
browser-only:
45+
- 'src/browser/**'
46+
- 'src/styles/**'
47+
- 'src/components/**'
48+
- 'src/hooks/**'
49+
- '*.stories.tsx'
50+
- '.storybook/**'
51+
node-backend:
52+
- 'src/node/**'
53+
- 'src/cli/**'
54+
- 'src/desktop/**'
55+
- 'tests/integration/**'
56+
ci-config:
57+
- '.github/**'
58+
- 'jest.config.cjs'
59+
- 'babel.config.cjs'
60+
- 'package.json'
61+
- 'bun.lockb'
62+
- 'tsconfig*.json'
63+
2164
static-check:
2265
name: Static Checks (lint + typecheck + fmt)
2366
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
@@ -69,22 +112,34 @@ jobs:
69112

70113
test:
71114
name: Unit Tests
115+
needs: [changes]
72116
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
73117
steps:
74118
- name: Checkout code
75119
uses: actions/checkout@v4
76120
with:
77121
fetch-depth: 0 # Required for git describe to find tags
78122

123+
- name: Skip (docs-only changes)
124+
if: needs.changes.outputs.docs-only == 'true' && needs.changes.outputs.node-backend != 'true' && needs.changes.outputs.browser-only != 'true' && needs.changes.outputs.ci-config != 'true'
125+
run: |
126+
echo "::notice::Skipping unit tests - docs-only changes"
127+
echo "## Unit Tests Skipped" >> $GITHUB_STEP_SUMMARY
128+
echo "Only documentation files changed." >> $GITHUB_STEP_SUMMARY
129+
79130
- uses: ./.github/actions/setup-mux
131+
if: needs.changes.outputs.docs-only != 'true' || needs.changes.outputs.node-backend == 'true' || needs.changes.outputs.browser-only == 'true' || needs.changes.outputs.ci-config == 'true'
80132

81133
- name: Build worker files
134+
if: needs.changes.outputs.docs-only != 'true' || needs.changes.outputs.node-backend == 'true' || needs.changes.outputs.browser-only == 'true' || needs.changes.outputs.ci-config == 'true'
82135
run: make build-main
83136

84137
- name: Run tests with coverage
138+
if: needs.changes.outputs.docs-only != 'true' || needs.changes.outputs.node-backend == 'true' || needs.changes.outputs.browser-only == 'true' || needs.changes.outputs.ci-config == 'true'
85139
run: bun test --coverage --coverage-reporter=lcov ${{ github.event.inputs.test_filter || 'src' }}
86140

87141
- name: Upload coverage to Codecov
142+
if: needs.changes.outputs.docs-only != 'true' || needs.changes.outputs.node-backend == 'true' || needs.changes.outputs.browser-only == 'true' || needs.changes.outputs.ci-config == 'true'
88143
uses: codecov/codecov-action@v5
89144
with:
90145
token: ${{ secrets.CODECOV_TOKEN }}
@@ -94,80 +149,68 @@ jobs:
94149

95150
integration-test:
96151
name: Integration Tests
152+
needs: [changes]
97153
timeout-minutes: 10
98154
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
99155
steps:
100156
- name: Checkout code
101157
uses: actions/checkout@v4
102158
with:
103-
fetch-depth: 0 # Required for git describe to find tags and diff
104-
105-
- uses: ./.github/actions/setup-mux
106-
107-
- name: Build worker files
108-
run: make build-main
109-
110-
# Try to restore coverage map from cache for selective testing
111-
- name: Restore coverage map
112-
if: github.event.inputs.test_filter == ''
113-
id: coverage-map-cache
114-
uses: actions/cache/restore@v4
115-
with:
116-
path: coverage-map.json
117-
key: coverage-map-${{ github.sha }}
118-
restore-keys: |
119-
coverage-map-latest-
120-
coverage-map-
159+
fetch-depth: 0 # Required for git describe to find tags
121160

122-
- name: Select affected tests
123-
if: github.event.inputs.test_filter == '' && steps.coverage-map-cache.outputs.cache-hit != ''
124-
id: select-tests
161+
# Skip conditions: docs-only OR browser-only (no backend changes)
162+
- name: Check if tests should run
163+
id: should-run
125164
run: |
126-
set +e
127-
SELECTED=$(bun scripts/selective-tests/select-affected-tests.ts \
128-
--map coverage-map.json \
129-
--base origin/${{ github.base_ref || 'main' }} \
130-
--head ${{ github.sha }} \
131-
--output jest \
132-
--verbose 2>&1)
133-
EXIT_CODE=$?
134-
set -e
135-
136-
# Extract just the test list (last line of output)
137-
TEST_LIST=$(echo "$SELECTED" | tail -1)
138-
139-
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
140-
if [[ $EXIT_CODE -eq 0 ]]; then
141-
echo "selected_tests=$TEST_LIST" >> $GITHUB_OUTPUT
142-
echo "run_selective=true" >> $GITHUB_OUTPUT
165+
DOCS_ONLY="${{ needs.changes.outputs.docs-only }}"
166+
BROWSER_ONLY="${{ needs.changes.outputs.browser-only }}"
167+
NODE_BACKEND="${{ needs.changes.outputs.node-backend }}"
168+
CI_CONFIG="${{ needs.changes.outputs.ci-config }}"
169+
170+
# Run if: manual filter provided, OR node-backend changed, OR ci-config changed
171+
# Skip if: docs-only OR (browser-only AND NOT node-backend AND NOT ci-config)
172+
if [[ -n "${{ github.event.inputs.test_filter }}" ]]; then
173+
echo "run=true" >> $GITHUB_OUTPUT
174+
echo "reason=Manual test filter provided" >> $GITHUB_OUTPUT
175+
elif [[ "$NODE_BACKEND" == "true" ]] || [[ "$CI_CONFIG" == "true" ]]; then
176+
echo "run=true" >> $GITHUB_OUTPUT
177+
echo "reason=Backend or CI config changes detected" >> $GITHUB_OUTPUT
178+
elif [[ "$DOCS_ONLY" == "true" ]]; then
179+
echo "run=false" >> $GITHUB_OUTPUT
180+
echo "reason=Docs-only changes" >> $GITHUB_OUTPUT
181+
elif [[ "$BROWSER_ONLY" == "true" ]]; then
182+
echo "run=false" >> $GITHUB_OUTPUT
183+
echo "reason=Browser-only changes (no backend impact)" >> $GITHUB_OUTPUT
143184
else
144-
echo "run_selective=false" >> $GITHUB_OUTPUT
185+
# Default: run tests (safety fallback)
186+
echo "run=true" >> $GITHUB_OUTPUT
187+
echo "reason=Default - running all tests" >> $GITHUB_OUTPUT
145188
fi
146189
147-
- name: Run selective integration tests
148-
if: steps.select-tests.outputs.run_selective == 'true' && steps.select-tests.outputs.selected_tests != '--testPathPattern=^$'
190+
- name: Skip integration tests
191+
if: steps.should-run.outputs.run != 'true'
149192
run: |
150-
echo "Running selective tests: ${{ steps.select-tests.outputs.selected_tests }}"
151-
TEST_INTEGRATION=1 bun x jest --coverage --maxWorkers=100% --silent ${{ steps.select-tests.outputs.selected_tests }}
152-
env:
153-
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
154-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
193+
echo "::notice::Skipping integration tests - ${{ steps.should-run.outputs.reason }}"
194+
echo "## Integration Tests Skipped" >> $GITHUB_STEP_SUMMARY
195+
echo "${{ steps.should-run.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
155196
156-
- name: Skip tests (no affected tests)
157-
if: steps.select-tests.outputs.run_selective == 'true' && steps.select-tests.outputs.selected_tests == '--testPathPattern=^$'
158-
run: |
159-
echo "::notice::No integration tests affected by changes - skipping"
160-
echo "No integration tests were affected by the changes in this PR." >> $GITHUB_STEP_SUMMARY
197+
- uses: ./.github/actions/setup-mux
198+
if: steps.should-run.outputs.run == 'true'
161199

162-
- name: Run all integration tests (fallback or manual filter)
163-
if: steps.select-tests.outputs.run_selective != 'true'
200+
- name: Build worker files
201+
if: steps.should-run.outputs.run == 'true'
202+
run: make build-main
203+
204+
- name: Run integration tests
205+
if: steps.should-run.outputs.run == 'true'
164206
# --silent suppresses per-test output (17+ test files × workers = overwhelming logs)
165207
run: TEST_INTEGRATION=1 bun x jest --coverage --maxWorkers=100% --silent ${{ github.event.inputs.test_filter || 'tests' }}
166208
env:
167209
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
168210
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
169211

170212
- name: Upload coverage to Codecov
213+
if: steps.should-run.outputs.run == 'true'
171214
uses: codecov/codecov-action@v5
172215
with:
173216
token: ${{ secrets.CODECOV_TOKEN }}
@@ -177,32 +220,64 @@ jobs:
177220

178221
storybook-test:
179222
name: Storybook Interaction Tests
223+
needs: [changes]
180224
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
181-
if: github.event.inputs.test_filter == ''
182225
steps:
183226
- name: Checkout code
184227
uses: actions/checkout@v4
185228
with:
186229
fetch-depth: 0 # Required for git describe to find tags
187230

231+
- name: Check if tests should run
232+
id: should-run
233+
run: |
234+
DOCS_ONLY="${{ needs.changes.outputs.docs-only }}"
235+
BROWSER_ONLY="${{ needs.changes.outputs.browser-only }}"
236+
NODE_BACKEND="${{ needs.changes.outputs.node-backend }}"
237+
CI_CONFIG="${{ needs.changes.outputs.ci-config }}"
238+
MANUAL_FILTER="${{ github.event.inputs.test_filter }}"
239+
240+
# Skip if manual filter (user wants specific tests) or docs-only
241+
if [[ -n "$MANUAL_FILTER" ]]; then
242+
echo "run=false" >> $GITHUB_OUTPUT
243+
echo "reason=Manual test filter provided" >> $GITHUB_OUTPUT
244+
elif [[ "$DOCS_ONLY" == "true" ]] && [[ "$BROWSER_ONLY" != "true" ]] && [[ "$NODE_BACKEND" != "true" ]] && [[ "$CI_CONFIG" != "true" ]]; then
245+
echo "run=false" >> $GITHUB_OUTPUT
246+
echo "reason=Docs-only changes" >> $GITHUB_OUTPUT
247+
else
248+
echo "run=true" >> $GITHUB_OUTPUT
249+
fi
250+
251+
- name: Skip storybook tests
252+
if: steps.should-run.outputs.run != 'true'
253+
run: |
254+
echo "::notice::Skipping storybook tests - ${{ steps.should-run.outputs.reason }}"
255+
echo "## Storybook Tests Skipped" >> $GITHUB_STEP_SUMMARY
256+
echo "${{ steps.should-run.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
257+
188258
- uses: ./.github/actions/setup-mux
259+
if: steps.should-run.outputs.run == 'true'
189260

190261
- uses: ./.github/actions/setup-playwright
262+
if: steps.should-run.outputs.run == 'true'
191263

192264
- name: Build Storybook
265+
if: steps.should-run.outputs.run == 'true'
193266
run: make storybook-build
194267

195268
- name: Serve Storybook
269+
if: steps.should-run.outputs.run == 'true'
196270
run: |
197271
bun x http-server storybook-static -p 6006 &
198272
sleep 5
199273
200274
- name: Run Storybook tests
275+
if: steps.should-run.outputs.run == 'true'
201276
run: make test-storybook
202277

203278
e2e-test:
204279
name: E2E Tests (${{ matrix.os }})
205-
if: github.event.inputs.test_filter == ''
280+
needs: [changes]
206281
strategy:
207282
fail-fast: false
208283
matrix:
@@ -222,24 +297,53 @@ jobs:
222297
with:
223298
fetch-depth: 0 # Required for git describe to find tags
224299

300+
- name: Check if tests should run
301+
id: should-run
302+
run: |
303+
DOCS_ONLY="${{ needs.changes.outputs.docs-only }}"
304+
BROWSER_ONLY="${{ needs.changes.outputs.browser-only }}"
305+
NODE_BACKEND="${{ needs.changes.outputs.node-backend }}"
306+
CI_CONFIG="${{ needs.changes.outputs.ci-config }}"
307+
MANUAL_FILTER="${{ github.event.inputs.test_filter }}"
308+
309+
# Skip if manual filter or docs-only
310+
if [[ -n "$MANUAL_FILTER" ]]; then
311+
echo "run=false" >> $GITHUB_OUTPUT
312+
echo "reason=Manual test filter provided" >> $GITHUB_OUTPUT
313+
elif [[ "$DOCS_ONLY" == "true" ]] && [[ "$BROWSER_ONLY" != "true" ]] && [[ "$NODE_BACKEND" != "true" ]] && [[ "$CI_CONFIG" != "true" ]]; then
314+
echo "run=false" >> $GITHUB_OUTPUT
315+
echo "reason=Docs-only changes" >> $GITHUB_OUTPUT
316+
else
317+
echo "run=true" >> $GITHUB_OUTPUT
318+
fi
319+
320+
- name: Skip E2E tests
321+
if: steps.should-run.outputs.run != 'true'
322+
run: |
323+
echo "::notice::Skipping E2E tests - ${{ steps.should-run.outputs.reason }}"
324+
echo "## E2E Tests Skipped" >> $GITHUB_STEP_SUMMARY
325+
echo "${{ steps.should-run.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
326+
225327
- uses: ./.github/actions/setup-mux
328+
if: steps.should-run.outputs.run == 'true'
226329

227330
- name: Install xvfb (Linux)
228-
if: matrix.os == 'linux'
331+
if: steps.should-run.outputs.run == 'true' && matrix.os == 'linux'
229332
run: |
230333
sudo apt-get update
231334
sudo apt-get install -y xvfb
232335
233336
- uses: ./.github/actions/setup-playwright
337+
if: steps.should-run.outputs.run == 'true'
234338

235339
- name: Run comprehensive e2e tests (Linux)
236-
if: matrix.os == 'linux'
340+
if: steps.should-run.outputs.run == 'true' && matrix.os == 'linux'
237341
run: xvfb-run -a make test-e2e
238342
env:
239343
ELECTRON_DISABLE_SANDBOX: 1
240344

241345
- name: Run window lifecycle e2e tests (macOS)
242-
if: matrix.os == 'macos'
346+
if: steps.should-run.outputs.run == 'true' && matrix.os == 'macos'
243347
run: make test-e2e PLAYWRIGHT_ARGS="tests/e2e/scenarios/windowLifecycle.spec.ts"
244348

245349
docker-smoke-test:
@@ -311,23 +415,49 @@ jobs:
311415

312416
mux-server-smoke-test:
313417
name: Mux Server Smoke Test
418+
needs: [changes]
314419
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
315-
# Tests oRPC/WebSocket flows that catch MockBrowserWindow bugs
316420
steps:
317421
- name: Checkout code
318422
uses: actions/checkout@v4
319423
with:
320424
fetch-depth: 0 # Required for git describe to find tags
321425

426+
- name: Check if tests should run
427+
id: should-run
428+
run: |
429+
DOCS_ONLY="${{ needs.changes.outputs.docs-only }}"
430+
NODE_BACKEND="${{ needs.changes.outputs.node-backend }}"
431+
CI_CONFIG="${{ needs.changes.outputs.ci-config }}"
432+
433+
# Skip if docs-only, run if backend or CI changes
434+
if [[ "$DOCS_ONLY" == "true" ]] && [[ "$NODE_BACKEND" != "true" ]] && [[ "$CI_CONFIG" != "true" ]]; then
435+
echo "run=false" >> $GITHUB_OUTPUT
436+
echo "reason=Docs-only changes" >> $GITHUB_OUTPUT
437+
else
438+
echo "run=true" >> $GITHUB_OUTPUT
439+
fi
440+
441+
- name: Skip smoke test
442+
if: steps.should-run.outputs.run != 'true'
443+
run: |
444+
echo "::notice::Skipping mux server smoke test - ${{ steps.should-run.outputs.reason }}"
445+
echo "## Mux Server Smoke Test Skipped" >> $GITHUB_STEP_SUMMARY
446+
echo "${{ steps.should-run.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
447+
322448
- uses: ./.github/actions/setup-mux
449+
if: steps.should-run.outputs.run == 'true'
323450

324451
- name: Build application
452+
if: steps.should-run.outputs.run == 'true'
325453
run: make build
326454

327455
- name: Pack npm package
456+
if: steps.should-run.outputs.run == 'true'
328457
run: npm pack
329458

330459
- name: Run smoke test
460+
if: steps.should-run.outputs.run == 'true'
331461
env:
332462
SERVER_PORT: 3001
333463
SERVER_HOST: localhost

0 commit comments

Comments
 (0)