Skip to content

Commit 61eaea8

Browse files
lkdvosclaude
andcommitted
Add TestGroups reusable workflow with auto-discovery of test groups
Introduces TestGroups.yml: a reusable workflow that auto-discovers subdirectories under test/ and runs each as a parallel job across a configurable Julia version × OS matrix. Includes a stable ci-success gate job for branch protection rules, fast-path support for draft PRs, and coverage restricted to a single OS/version to avoid redundant uploads. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0766707 commit 61eaea8

2 files changed

Lines changed: 248 additions & 2 deletions

File tree

.github/workflows/TestGroups.yml

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
name: "Reusable Test Groups Workflow"
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
exclude:
7+
description: "JSON array of test/ subdirectories to exclude from auto-discovery"
8+
default: '["setup", "gpu"]'
9+
required: false
10+
type: string
11+
julia-version:
12+
description: "JSON array of Julia versions to test (e.g. '[\"min\", \"1\"]')"
13+
default: '["min", "1"]'
14+
required: false
15+
type: string
16+
os:
17+
description: "JSON array of runner operating systems"
18+
default: '["ubuntu-latest", "macos-latest", "windows-latest"]'
19+
required: false
20+
type: string
21+
fast:
22+
description: "Collapse matrix to ubuntu-latest + julia 1, and append --fast to test args"
23+
default: false
24+
required: false
25+
type: boolean
26+
nthreads:
27+
description: "Number of Julia threads"
28+
default: 2
29+
required: false
30+
type: number
31+
timeout-minutes:
32+
description: "Maximum time per job in minutes"
33+
default: 60
34+
required: false
35+
type: number
36+
localregistry:
37+
description: "Newline-separated list of local registry URLs to add before building"
38+
default: ""
39+
required: false
40+
type: string
41+
cache:
42+
description: "Enable julia-actions/cache"
43+
default: true
44+
required: false
45+
type: boolean
46+
buildpkg:
47+
description: "Enable julia-actions/julia-buildpkg"
48+
default: true
49+
required: false
50+
type: boolean
51+
coverage:
52+
description: "Collect and upload coverage (restricted to ubuntu-latest + julia 1)"
53+
default: true
54+
required: false
55+
type: boolean
56+
coverage-directories:
57+
description: "Comma-separated directories for julia-processcoverage"
58+
default: "src,ext"
59+
required: false
60+
type: string
61+
secrets:
62+
CODECOV_TOKEN:
63+
required: false
64+
65+
jobs:
66+
setup:
67+
runs-on: ubuntu-latest
68+
outputs:
69+
groups: ${{ steps.discover.outputs.groups }}
70+
versions: ${{ steps.discover.outputs.versions }}
71+
systems: ${{ steps.discover.outputs.systems }}
72+
steps:
73+
- uses: actions/checkout@v4
74+
75+
- name: Discover test groups
76+
id: discover
77+
shell: bash
78+
run: |
79+
GROUPS=$(find test -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \
80+
| sort \
81+
| jq -R -s -c --argjson exc '${{ inputs.exclude }}' \
82+
'split("\n")[:-1] | map(select(. as $g | $exc | index($g) | not))')
83+
echo "groups=$GROUPS" >> "$GITHUB_OUTPUT"
84+
85+
if [[ "${{ inputs.fast }}" == "true" ]]; then
86+
echo 'versions=["1"]' >> "$GITHUB_OUTPUT"
87+
echo 'systems=["ubuntu-latest"]' >> "$GITHUB_OUTPUT"
88+
else
89+
echo 'versions=${{ inputs.julia-version }}' >> "$GITHUB_OUTPUT"
90+
echo 'systems=${{ inputs.os }}' >> "$GITHUB_OUTPUT"
91+
fi
92+
93+
test:
94+
needs: setup
95+
name: "Test (${{ matrix.version }}, ${{ matrix.os }}, ${{ matrix.group }})"
96+
runs-on: ${{ matrix.os }}
97+
timeout-minutes: ${{ inputs.timeout-minutes }}
98+
strategy:
99+
fail-fast: false
100+
matrix:
101+
group: ${{ fromJSON(needs.setup.outputs.groups) }}
102+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
103+
os: ${{ fromJSON(needs.setup.outputs.systems) }}
104+
steps:
105+
- uses: actions/checkout@v4
106+
107+
- name: "Setup Julia ${{ matrix.version }}"
108+
uses: julia-actions/setup-julia@v2
109+
with:
110+
version: "${{ matrix.version }}"
111+
112+
- uses: julia-actions/cache@v2
113+
if: inputs.cache
114+
with:
115+
token: ${{ secrets.GITHUB_TOKEN }}
116+
117+
- uses: julia-actions/julia-buildpkg@v1
118+
if: inputs.buildpkg
119+
with:
120+
localregistry: "${{ inputs.localregistry }}"
121+
122+
- name: "Run tests"
123+
uses: julia-actions/julia-runtest@v1
124+
with:
125+
test_args: "${{ matrix.group }}${{ inputs.fast && ' --fast' || '' }}"
126+
coverage: "${{ inputs.coverage && matrix.os == 'ubuntu-latest' && matrix.version == '1' }}"
127+
env:
128+
JULIA_NUM_THREADS: "${{ inputs.nthreads }}"
129+
130+
- uses: julia-actions/julia-processcoverage@v1
131+
if: inputs.coverage && matrix.os == 'ubuntu-latest' && matrix.version == '1'
132+
with:
133+
directories: "${{ inputs.coverage-directories }}"
134+
135+
- name: "Report coverage with Codecov"
136+
uses: codecov/codecov-action@v5
137+
if: inputs.coverage && matrix.os == 'ubuntu-latest' && matrix.version == '1'
138+
with:
139+
files: lcov.info
140+
token: ${{ secrets.CODECOV_TOKEN }}
141+
fail_ci_if_error: false
142+
143+
ci-success:
144+
name: ci-success
145+
needs: test
146+
if: always()
147+
runs-on: ubuntu-latest
148+
steps:
149+
- name: Check all tests passed
150+
run: |
151+
if [[ "${{ contains(needs.test.result, 'failure') || contains(needs.test.result, 'cancelled') }}" == "true" ]]; then
152+
exit 1
153+
fi

README.md

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,99 @@
1-
# ITensorActions
1+
# QuantumKitHubActions
22

3-
Shared workflows for the ITensors Julia packages
3+
Shared workflows for the QuantumKitHub Julia packages
4+
5+
## Test Groups
6+
7+
The TestGroups workflow auto-discovers test groups from subdirectories of `test/` and runs them in parallel across a matrix of Julia versions and operating systems. Each subdirectory becomes a separate parallel job; groups are passed to the test suite via `test_args` and dispatched by [ParallelTestRunner.jl](https://github.com/QuantumKitHub/ParallelTestRunner.jl).
8+
9+
### Test directory structure
10+
11+
Organize your `test/` directory with one subfolder per test group. Place shared setup code in a `test/setup/` directory (excluded by default):
12+
13+
```
14+
test/
15+
├── setup/ # shared utilities, excluded from test groups
16+
├── core/
17+
│ └── runtests.jl
18+
├── extensions/
19+
│ └── runtests.jl
20+
└── runtests.jl # ParallelTestRunner.jl entry point
21+
```
22+
23+
A minimal `test/runtests.jl` using ParallelTestRunner.jl:
24+
25+
```julia
26+
using ParallelTestRunner
27+
ParallelTestRunner.runtests()
28+
```
29+
30+
### Example workflow
31+
32+
```yaml
33+
name: "Tests"
34+
35+
on:
36+
push:
37+
branches:
38+
- main
39+
tags: '*'
40+
pull_request:
41+
workflow_dispatch:
42+
43+
concurrency:
44+
group: ${{ github.workflow }}-${{ github.ref }}
45+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
46+
47+
jobs:
48+
tests:
49+
uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/TestGroups.yml@main"
50+
with:
51+
fast: ${{ github.event.pull_request.draft == true }}
52+
secrets:
53+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
54+
```
55+
56+
### Branch protection
57+
58+
Add `ci-success` as a required status check in your repository's branch protection rules. This single stable check name reflects the combined result of all parallel test jobs regardless of how many groups exist.
59+
60+
### Inputs
61+
62+
| Input | Type | Default | Description |
63+
|---|---|---|---|
64+
| `exclude` | string | `'["setup", "gpu"]'` | JSON array of `test/` subdirectory names to exclude from discovery |
65+
| `julia-version` | string | `'["min", "1"]'` | JSON array of Julia versions to test |
66+
| `os` | string | `'["ubuntu-latest", "macos-latest", "windows-latest"]'` | JSON array of runner OSes |
67+
| `fast` | boolean | `false` | Collapse matrix to `ubuntu-latest` + julia `1`, append `--fast` to test args |
68+
| `nthreads` | number | `2` | Julia thread count per job |
69+
| `timeout-minutes` | number | `60` | Per-job timeout |
70+
| `localregistry` | string | `""` | Newline-separated local registry URLs |
71+
| `cache` | boolean | `true` | Enable `julia-actions/cache` |
72+
| `buildpkg` | boolean | `true` | Enable `julia-actions/julia-buildpkg` |
73+
| `coverage` | boolean | `true` | Collect and upload coverage (only on `ubuntu-latest` + julia `1`) |
74+
| `coverage-directories` | string | `"src,ext"` | Directories for `julia-processcoverage` |
75+
76+
**Secrets:** `CODECOV_TOKEN` (optional, only needed when `coverage: true`)
77+
78+
### Fast path
79+
80+
When `fast: true`, the matrix collapses to a single OS and Julia version and `--fast` is appended to each group's test args. Wire it to draft PR detection for quick feedback during development:
81+
82+
```yaml
83+
with:
84+
fast: ${{ github.event.pull_request.draft == true }}
85+
```
86+
87+
ParallelTestRunner.jl passes `--fast` through to individual test files, which can use it to skip slow or expensive tests.
88+
89+
### Excluding folders
90+
91+
Override `exclude` to control which subdirectories are skipped:
92+
93+
```yaml
94+
with:
95+
exclude: '["setup", "gpu", "cuda"]'
96+
```
497

598
## Tests
699

0 commit comments

Comments
 (0)