Skip to content

Commit 2da697e

Browse files
committed
ci: Run unit tests per workspace in parallel
Rework the test workflow to use a dynamic matrix that discovers all workspaces with a unit script automatically. Each workspace runs as its own job, making failures immediately visible per package in the GitHub Actions UI. A single gate job (CI) aggregates the results for use as the required status check in branch protection.
1 parent 5ce9640 commit 2da697e

1 file changed

Lines changed: 50 additions & 4 deletions

File tree

.github/workflows/test.yml

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,50 @@ permissions:
1212
contents: read
1313

1414
jobs:
15-
test:
16-
name: Unit and Integration
15+
discover:
16+
name: Discover workspaces
17+
runs-on: ubuntu-24.04
18+
outputs:
19+
workspaces: ${{ steps.find.outputs.workspaces }}
20+
steps:
21+
22+
- uses: actions/checkout@v6
23+
24+
- name: Find workspaces with unit tests
25+
id: find
26+
run: |
27+
workspaces=$(node -e "
28+
const {readFileSync, readdirSync, existsSync} = require('fs');
29+
const {join} = require('path');
30+
const root = JSON.parse(readFileSync('package.json', 'utf8'));
31+
const dirs = root.workspaces.flatMap(pattern => {
32+
const base = pattern.replace('/*', '');
33+
if (!existsSync(base)) return [];
34+
return readdirSync(base, {withFileTypes: true})
35+
.filter(d => d.isDirectory())
36+
.map(d => join(base, d.name));
37+
});
38+
const names = dirs
39+
.filter(d => {
40+
const p = join(d, 'package.json');
41+
if (!existsSync(p)) return false;
42+
const pkg = JSON.parse(readFileSync(p, 'utf8'));
43+
return Boolean(pkg.scripts && pkg.scripts.unit);
44+
})
45+
.map(d => JSON.parse(readFileSync(join(d, 'package.json'), 'utf8')).name);
46+
console.log(JSON.stringify(names));
47+
")
48+
echo "workspaces=$workspaces" >> "$GITHUB_OUTPUT"
49+
50+
unit:
51+
name: "${{ matrix.workspace }} (Node ${{ matrix.version }}, ${{ matrix.os }})"
52+
needs: discover
1753
strategy:
18-
fail-fast: false # Do not stop other jobs if one fails
54+
fail-fast: false
1955
matrix:
2056
version: [22, 24]
2157
os: [ubuntu-24.04, windows-2025, macos-15]
58+
workspace: ${{ fromJSON(needs.discover.outputs.workspaces) }}
2259
runs-on: ${{ matrix.os }}
2360
steps:
2461

@@ -38,4 +75,13 @@ jobs:
3875
run: npm ci
3976

4077
- name: Run unit tests
41-
run: npm run unit
78+
run: npm run unit --workspace=${{ matrix.workspace }}
79+
80+
ci:
81+
name: CI
82+
needs: [unit]
83+
if: always()
84+
runs-on: ubuntu-24.04
85+
steps:
86+
- run: exit 1
87+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')

0 commit comments

Comments
 (0)