Skip to content

Commit ba9a5c5

Browse files
leogdionclaude
andcommitted
Optimize CI with dynamic matrix, concurrency groups, and cache cleanup (#128)
- Add concurrency groups to cancel in-progress runs on same branch - Add paths-ignore to skip CI on doc-only changes - Add pull_request trigger for PRs to main - Add configure job with dynamic matrix (minimal on feature branches, full on main/PRs) - Gate Windows builds to full matrix only - Split macOS into core (always) and full (main/PRs only) jobs - Update lint/docs conditions to run when optional jobs are skipped - Update all GitHub Actions to latest versions (checkout v6, cache v5, codecov v6, etc.) - Add cache cleanup workflows for deleted branches and closed PRs - Update CodeQL workflow action versions Closes #128, closes #126, closes #127 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 18c1e25 commit ba9a5c5

File tree

4 files changed

+274
-53
lines changed

4 files changed

+274
-53
lines changed

.github/workflows/SyntaxKit.yml

Lines changed: 115 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,71 @@ on:
33
push:
44
branches-ignore:
55
- '*WIP'
6+
paths-ignore:
7+
- '**.md'
8+
- 'Docs/**'
9+
- 'LICENSE'
10+
- '.github/ISSUE_TEMPLATE/**'
11+
pull_request:
12+
branches: [main]
13+
paths-ignore:
14+
- '**.md'
15+
- 'Docs/**'
16+
- 'LICENSE'
17+
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: true
21+
622
env:
7-
PACKAGE_NAME: SyntaxKit
23+
PACKAGE_NAME: SyntaxKit
824
jobs:
25+
configure:
26+
name: Configure Build Matrix
27+
runs-on: ubuntu-latest
28+
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
29+
outputs:
30+
full-matrix: ${{ steps.set-matrix.outputs.full-matrix }}
31+
ubuntu-matrix: ${{ steps.set-matrix.outputs.ubuntu-matrix }}
32+
steps:
33+
- name: Determine build matrix
34+
id: set-matrix
35+
run: |
36+
if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.event_name }}" == "pull_request" ]]; then
37+
echo "full-matrix=true" >> "$GITHUB_OUTPUT"
38+
echo 'ubuntu-matrix={"os":["noble","jammy"],"swift":[{"version":"6.0"},{"version":"6.1"},{"version":"6.1","nightly":true},{"version":"6.2","nightly":true}]}' >> "$GITHUB_OUTPUT"
39+
else
40+
echo "full-matrix=false" >> "$GITHUB_OUTPUT"
41+
echo 'ubuntu-matrix={"os":["noble"],"swift":[{"version":"6.1"}]}' >> "$GITHUB_OUTPUT"
42+
fi
43+
944
build-ubuntu:
1045
name: Build on Ubuntu
46+
needs: [configure]
1147
runs-on: ubuntu-latest
1248
container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }}
13-
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
1449
strategy:
15-
matrix:
16-
os: [noble, jammy]
17-
swift:
18-
- version: "6.0"
19-
- version: "6.1"
20-
- version: "6.1"
21-
nightly: true
22-
- version: "6.2"
23-
nightly: true
50+
matrix: ${{ fromJSON(needs.configure.outputs.ubuntu-matrix) }}
2451
steps:
25-
- uses: actions/checkout@v4
26-
- uses: brightdigit/swift-build@v1.3.1
27-
- uses: sersoft-gmbh/swift-coverage-action@v4
52+
- uses: actions/checkout@v6
53+
- uses: brightdigit/swift-build@v1.5.2
54+
- uses: sersoft-gmbh/swift-coverage-action@v5
2855
id: coverage-files
29-
with:
56+
with:
3057
fail-on-empty-output: true
3158
- name: Upload coverage to Codecov
32-
uses: codecov/codecov-action@v4
59+
uses: codecov/codecov-action@v6
3360
with:
3461
fail_ci_if_error: true
35-
flags: swift-${{ matrix.swift-version }},ubuntu
36-
verbose: true
37-
token: ${{ secrets.CODECOV_TOKEN }}
38-
files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }}
62+
flags: swift-${{ matrix.swift.version }},ubuntu
63+
verbose: true
64+
token: ${{ secrets.CODECOV_TOKEN }}
65+
files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }}
66+
3967
build-windows:
4068
name: Build on Windows
69+
needs: [configure]
70+
if: needs.configure.outputs.full-matrix == 'true'
4171
runs-on: ${{ matrix.runs-on }}
4272
strategy:
4373
fail-fast: false
@@ -49,59 +79,91 @@ jobs:
4979
- swift-version: swift-6.1-release
5080
swift-build: 6.1-RELEASE
5181
steps:
52-
- uses: actions/checkout@v4
53-
- uses: brightdigit/swift-build@v1.3.1
82+
- uses: actions/checkout@v6
83+
- uses: brightdigit/swift-build@v1.5.2
5484
with:
5585
windows-swift-version: ${{ matrix.swift-version }}
5686
windows-swift-build: ${{ matrix.swift-build }}
5787
- name: Upload coverage to Codecov
58-
uses: codecov/codecov-action@v5
88+
uses: codecov/codecov-action@v6
5989
with:
6090
fail_ci_if_error: true
6191
flags: swift-${{ matrix.swift-version }},windows
62-
verbose: true
92+
verbose: true
6393
token: ${{ secrets.CODECOV_TOKEN }}
6494
os: windows
6595
swift_project: SyntaxKit
66-
# files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }}
96+
6797
build-macos:
6898
name: Build on macOS
6999
env:
70100
PACKAGE_NAME: SyntaxKit
71101
runs-on: ${{ matrix.runs-on }}
72-
if: "!contains(github.event.head_commit.message, 'ci skip')"
102+
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
73103
strategy:
74104
fail-fast: false
75105
matrix:
76106
include:
77-
# SPM Build Matrix
107+
# SPM Build
78108
- runs-on: macos-15
79109
xcode: "/Applications/Xcode_26.0.app"
80110

81-
- runs-on: macos-15
82-
xcode: "/Applications/Xcode_16.4.app"
83-
84-
# macOS Build Matrix
111+
# macOS Build
85112
- type: macos
86113
runs-on: macos-15
87114
xcode: "/Applications/Xcode_26.0.app"
88115

89-
- type: macos
90-
runs-on: macos-15
116+
steps:
117+
- uses: actions/checkout@v6
118+
119+
- name: Build and Test
120+
uses: brightdigit/swift-build@v1.5.2
121+
with:
122+
scheme: ${{ env.PACKAGE_NAME }}-Package
123+
type: ${{ matrix.type }}
124+
xcode: ${{ matrix.xcode }}
125+
deviceName: ${{ matrix.deviceName }}
126+
osVersion: ${{ matrix.osVersion }}
127+
download-platform: ${{ matrix.download-platform }}
128+
129+
# Common Coverage Steps
130+
- name: Process Coverage
131+
uses: sersoft-gmbh/swift-coverage-action@v5
132+
133+
- name: Upload Coverage
134+
uses: codecov/codecov-action@v6
135+
with:
136+
token: ${{ secrets.CODECOV_TOKEN }}
137+
flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }}
138+
139+
build-macos-full:
140+
name: Build on macOS (Full)
141+
needs: [configure]
142+
if: needs.configure.outputs.full-matrix == 'true'
143+
env:
144+
PACKAGE_NAME: SyntaxKit
145+
runs-on: ${{ matrix.runs-on }}
146+
strategy:
147+
fail-fast: false
148+
matrix:
149+
include:
150+
# SPM Build (older Xcode)
151+
- runs-on: macos-15
91152
xcode: "/Applications/Xcode_16.4.app"
92153

154+
# macOS Build (older Xcode)
93155
- type: macos
94156
runs-on: macos-15
95-
xcode: "/Applications/Xcode_26.0.app"
96-
157+
xcode: "/Applications/Xcode_16.4.app"
158+
97159
# iOS Build Matrix
98160
- type: ios
99161
runs-on: macos-15
100162
xcode: "/Applications/Xcode_26.0.app"
101163
deviceName: "iPhone 17 Pro"
102164
osVersion: "26.0"
103165
download-platform: true
104-
166+
105167
# watchOS Build Matrix
106168
- type: watchos
107169
runs-on: macos-15
@@ -118,59 +180,59 @@ jobs:
118180
osVersion: "26.0"
119181
download-platform: true
120182

121-
# visionOS Build Matrix
183+
# visionOS Build Matrix
122184
- type: visionos
123185
runs-on: macos-15
124186
xcode: "/Applications/Xcode_26.0.app"
125187
deviceName: "Apple Vision Pro"
126188
osVersion: "26.0"
127189
download-platform: true
128-
190+
129191
steps:
130-
- uses: actions/checkout@v4
192+
- uses: actions/checkout@v6
131193

132194
- name: Build and Test
133-
uses: brightdigit/swift-build@v1.3.1
195+
uses: brightdigit/swift-build@v1.5.2
134196
with:
135197
scheme: ${{ env.PACKAGE_NAME }}-Package
136198
type: ${{ matrix.type }}
137199
xcode: ${{ matrix.xcode }}
138200
deviceName: ${{ matrix.deviceName }}
139201
osVersion: ${{ matrix.osVersion }}
140202
download-platform: ${{ matrix.download-platform }}
141-
203+
142204
# Common Coverage Steps
143205
- name: Process Coverage
144-
uses: sersoft-gmbh/swift-coverage-action@v4
145-
206+
uses: sersoft-gmbh/swift-coverage-action@v5
207+
146208
- name: Upload Coverage
147-
uses: codecov/codecov-action@v4
209+
uses: codecov/codecov-action@v6
148210
with:
149211
token: ${{ secrets.CODECOV_TOKEN }}
150212
flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }}
151213

152214
lint:
153215
name: Linting
154-
if: "!contains(github.event.head_commit.message, 'ci skip')"
216+
if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }}
155217
runs-on: ubuntu-latest
156-
needs: [build-ubuntu, build-macos, build-windows]
218+
needs: [build-ubuntu, build-macos, build-windows, build-macos-full]
157219
env:
158220
MINT_PATH: .mint/lib
159221
MINT_LINK_PATH: .mint/bin
160222
steps:
161-
- uses: actions/checkout@v4
223+
- uses: actions/checkout@v6
162224
- name: Cache mint
163225
id: cache-mint
164-
uses: actions/cache@v4
226+
uses: actions/cache@v5
165227
env:
166228
cache-name: cache
167229
with:
168230
path: |
169231
.mint
170-
Mint
232+
Mint
171233
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
172234
restore-keys: |
173-
${{ runner.os }}-mint-
235+
${{ runner.os }}-mint-
174236
- name: Install mint
175237
if: steps.cache-mint.outputs.cache-hit == ''
176238
run: |
@@ -182,10 +244,10 @@ jobs:
182244

183245
docs:
184246
name: Documentation Validation
185-
if: "!contains(github.event.head_commit.message, 'ci skip')"
186-
needs: [build-ubuntu, build-macos, build-windows]
247+
if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }}
248+
needs: [build-ubuntu, build-macos, build-windows, build-macos-full]
187249
runs-on: ubuntu-latest
188250
steps:
189-
- uses: actions/checkout@v4
251+
- uses: actions/checkout@v6
190252
- name: Validate Documentation
191253
run: ./Scripts/validate-docs.sh
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Cleanup Branch Caches
2+
on:
3+
delete:
4+
5+
jobs:
6+
cleanup:
7+
name: Cleanup Caches for Deleted Branch
8+
runs-on: ubuntu-latest
9+
permissions:
10+
actions: write
11+
steps:
12+
- uses: actions/checkout@v6
13+
- name: Delete branch caches
14+
uses: actions/github-script@v8
15+
with:
16+
script: |
17+
const ref = `refs/heads/${context.payload.ref}`;
18+
console.log(`Cleaning up caches for branch: ${ref}`);
19+
20+
const caches = await github.paginate(
21+
github.rest.actions.getActionsCacheList,
22+
{
23+
owner: context.repo.owner,
24+
repo: context.repo.repo,
25+
ref: ref,
26+
}
27+
);
28+
29+
for (const cache of caches) {
30+
console.log(`Deleting cache: ${cache.key} (${cache.id})`);
31+
await github.rest.actions.deleteActionsCacheById({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
cache_id: cache.id,
35+
});
36+
}
37+
38+
console.log(`Deleted ${caches.length} cache(s) for ${ref}`);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Cleanup PR Caches
2+
on:
3+
pull_request:
4+
types: [closed]
5+
6+
jobs:
7+
cleanup:
8+
name: Cleanup Caches for PR
9+
runs-on: ubuntu-latest
10+
permissions:
11+
actions: write
12+
steps:
13+
- uses: actions/checkout@v6
14+
- name: Delete PR branch caches
15+
uses: actions/github-script@v8
16+
with:
17+
script: |
18+
const ref = `refs/pull/${context.payload.pull_request.number}/merge`;
19+
console.log(`Cleaning up caches for PR: ${ref}`);
20+
21+
const caches = await github.paginate(
22+
github.rest.actions.getActionsCacheList,
23+
{
24+
owner: context.repo.owner,
25+
repo: context.repo.repo,
26+
ref: ref,
27+
}
28+
);
29+
30+
for (const cache of caches) {
31+
console.log(`Deleting cache: ${cache.key} (${cache.id})`);
32+
await github.rest.actions.deleteActionsCacheById({
33+
owner: context.repo.owner,
34+
repo: context.repo.repo,
35+
cache_id: cache.id,
36+
});
37+
}
38+
39+
console.log(`Deleted ${caches.length} cache(s) for ${ref}`);

0 commit comments

Comments
 (0)