Skip to content

Commit e9c6587

Browse files
cpcloudclaude
andauthored
perf(ci): skip Go jobs for docs-only changes (#727)
## Summary - Add shared detection script (`.github/detect-go-changes.bash`) that uses the GitHub API to classify PR/push file changes as Go-related or docs-only - Skip Go-specific jobs (build, test, benchmarks, nix build, linters, security scans) when only docs/website paths change - Add gate jobs (`CI / Result`, `Lint / Result`, `Security / Result`) per workflow that roll up all job results into a single required check - Always-run jobs (Docs Build, Pre-commit, Secret Scan) are unaffected - Detection is intentionally conservative: unknown/new file types trigger Go CI **Post-merge action**: Update the repo ruleset (ID 12645641) to require only the 3 gate jobs instead of the current 13 individual check names. Remove the redundant `CodeQL` (integration 57789) entry that duplicates `CodeQL (Go)`. closes #726 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b8b8697 commit e9c6587

4 files changed

Lines changed: 174 additions & 1 deletion

File tree

.github/detect-go-changes.bash

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
# Detect whether a PR or push includes Go-related changes.
3+
# Prints "true" or "false" to stdout.
4+
#
5+
# Requires: GH_TOKEN, GITHUB_REPOSITORY
6+
# Arguments: <event_name> <pr_number|""> <before_sha|""> <head_sha|"">
7+
set -euo pipefail
8+
9+
event_name=$1
10+
pr_number=${2:-}
11+
before_sha=${3:-}
12+
head_sha=${4:-}
13+
14+
if [ "$event_name" = "pull_request" ]; then
15+
changed=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${pr_number}/files" \
16+
--paginate --jq '.[].filename')
17+
else
18+
if [ "$before_sha" = "0000000000000000000000000000000000000000" ]; then
19+
echo true
20+
exit 0
21+
fi
22+
changed=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${before_sha}...${head_sha}" \
23+
--jq '.files[].filename')
24+
fi
25+
26+
if [ -z "$changed" ]; then
27+
echo false
28+
exit 0
29+
fi
30+
31+
# For push events, the compare API caps at 300 files.
32+
if [ "$event_name" != "pull_request" ]; then
33+
file_count=$(echo "$changed" | wc -l)
34+
if [ "$file_count" -ge 300 ]; then
35+
echo "::warning::Compare API file cap hit ($file_count files), assuming Go changes"
36+
echo true
37+
exit 0
38+
fi
39+
fi
40+
41+
# Anything outside these paths is considered Go-related.
42+
# Intentionally conservative: unknown paths trigger Go CI.
43+
non_docs=$(echo "$changed" | grep -vE '^docs/|^images/|\.md$|^LICENSE|^\.github/workflows/pages\.yml$|^\.claude/' || true)
44+
if [ -n "$non_docs" ]; then
45+
echo true
46+
else
47+
echo false
48+
fi

.github/workflows/ci.yml

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,34 @@ env:
1717
TZ: UTC
1818

1919
jobs:
20+
changes:
21+
name: Detect Changes
22+
runs-on: ubuntu-latest
23+
outputs:
24+
go: ${{ steps.detect.outputs.go }}
25+
steps:
26+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
27+
with:
28+
sparse-checkout: .github/detect-go-changes.bash
29+
sparse-checkout-cone-mode: false
30+
persist-credentials: false
31+
32+
- name: Check for Go-related changes
33+
id: detect
34+
env:
35+
GH_TOKEN: ${{ github.token }}
36+
run: |
37+
go=$(bash .github/detect-go-changes.bash \
38+
"${{ github.event_name }}" \
39+
"${{ github.event.pull_request.number }}" \
40+
"${{ github.event.before }}" \
41+
"${{ github.sha }}")
42+
echo "go=$go" >> "$GITHUB_OUTPUT"
43+
2044
test:
2145
name: Build & Test (${{ matrix.os }})
46+
needs: changes
47+
if: needs.changes.outputs.go == 'true'
2248
runs-on: ${{ matrix.os }}
2349
concurrency:
2450
group: ci-test-${{ matrix.os }}-${{ github.ref }}
@@ -133,6 +159,8 @@ jobs:
133159

134160
benchmarks:
135161
name: Benchmarks (smoke)
162+
needs: changes
163+
if: needs.changes.outputs.go == 'true'
136164
runs-on: ubuntu-latest
137165
concurrency:
138166
group: ci-benchmarks-${{ github.ref }}
@@ -151,6 +179,8 @@ jobs:
151179

152180
nix-build:
153181
name: Nix Build
182+
needs: changes
183+
if: needs.changes.outputs.go == 'true'
154184
runs-on: ubuntu-latest
155185
concurrency:
156186
group: ci-nix-build-${{ github.ref }}
@@ -196,7 +226,12 @@ jobs:
196226

197227
semantic-release:
198228
name: Semantic Release
199-
needs: [test, nix-build, docs]
229+
needs: [changes, test, nix-build, docs]
230+
if: >-
231+
always()
232+
&& !contains(needs.*.result, 'failure')
233+
&& !contains(needs.*.result, 'cancelled')
234+
&& (needs.changes.outputs.go != 'true' || (needs.test.result == 'success' && needs.nix-build.result == 'success'))
200235
runs-on: ubuntu-latest
201236
concurrency:
202237
group: semantic-release-${{ github.ref }}
@@ -227,3 +262,17 @@ jobs:
227262
run: npx semantic-release ${{ github.event_name == 'pull_request' && '--dry-run' || '' }}
228263
env:
229264
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
265+
266+
# ---------------------------------------------------------------------------
267+
# Gate: single required check that rolls up all jobs above.
268+
# After merging, update the repo ruleset to require only "CI / Result".
269+
# ---------------------------------------------------------------------------
270+
271+
result:
272+
name: CI / Result
273+
if: always()
274+
needs: [changes, test, benchmarks, nix-build, docs, semantic-release]
275+
runs-on: ubuntu-latest
276+
steps:
277+
- run: exit 1
278+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')

.github/workflows/lint.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,34 @@ permissions:
1313
contents: read
1414

1515
jobs:
16+
changes:
17+
name: Detect Changes
18+
runs-on: ubuntu-latest
19+
outputs:
20+
go: ${{ steps.detect.outputs.go }}
21+
steps:
22+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
23+
with:
24+
sparse-checkout: .github/detect-go-changes.bash
25+
sparse-checkout-cone-mode: false
26+
persist-credentials: false
27+
28+
- name: Check for Go-related changes
29+
id: detect
30+
env:
31+
GH_TOKEN: ${{ github.token }}
32+
run: |
33+
go=$(bash .github/detect-go-changes.bash \
34+
"${{ github.event_name }}" \
35+
"${{ github.event.pull_request.number }}" \
36+
"${{ github.event.before }}" \
37+
"${{ github.sha }}")
38+
echo "go=$go" >> "$GITHUB_OUTPUT"
39+
1640
deadcode:
1741
name: Dead Code
42+
needs: changes
43+
if: needs.changes.outputs.go == 'true'
1844
runs-on: ubuntu-latest
1945
concurrency:
2046
group: lint-deadcode-${{ github.ref }}
@@ -31,6 +57,8 @@ jobs:
3157

3258
golangci-lint:
3359
name: Lint
60+
needs: changes
61+
if: needs.changes.outputs.go == 'true'
3462
runs-on: ubuntu-latest
3563
concurrency:
3664
group: lint-golangci-lint-${{ github.ref }}
@@ -61,3 +89,12 @@ jobs:
6189

6290
- name: Run pre-commit hooks
6391
run: nix run '.#pre-commit' -- --from-ref 'origin/${{ github.base_ref || 'main' }}' --to-ref HEAD
92+
93+
result:
94+
name: Lint / Result
95+
if: always()
96+
needs: [changes, deadcode, golangci-lint, pre-commit]
97+
runs-on: ubuntu-latest
98+
steps:
99+
- run: exit 1
100+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')

.github/workflows/security.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,34 @@ permissions:
1313
contents: read
1414

1515
jobs:
16+
changes:
17+
name: Detect Changes
18+
runs-on: ubuntu-latest
19+
outputs:
20+
go: ${{ steps.detect.outputs.go }}
21+
steps:
22+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
23+
with:
24+
sparse-checkout: .github/detect-go-changes.bash
25+
sparse-checkout-cone-mode: false
26+
persist-credentials: false
27+
28+
- name: Check for Go-related changes
29+
id: detect
30+
env:
31+
GH_TOKEN: ${{ github.token }}
32+
run: |
33+
go=$(bash .github/detect-go-changes.bash \
34+
"${{ github.event_name }}" \
35+
"${{ github.event.pull_request.number }}" \
36+
"${{ github.event.before }}" \
37+
"${{ github.sha }}")
38+
echo "go=$go" >> "$GITHUB_OUTPUT"
39+
1640
govulncheck:
1741
name: Vulnerability Check
42+
needs: changes
43+
if: needs.changes.outputs.go == 'true'
1844
runs-on: ubuntu-latest
1945
concurrency:
2046
group: security-govulncheck-${{ github.ref }}
@@ -31,6 +57,8 @@ jobs:
3157

3258
osv-scanner:
3359
name: OSV Scan
60+
needs: changes
61+
if: needs.changes.outputs.go == 'true'
3462
runs-on: ubuntu-latest
3563
concurrency:
3664
group: security-osv-scanner-${{ github.ref }}
@@ -63,6 +91,8 @@ jobs:
6391

6492
codeql:
6593
name: CodeQL (Go)
94+
needs: changes
95+
if: needs.changes.outputs.go == 'true'
6696
runs-on: ubuntu-latest
6797
concurrency:
6898
group: security-codeql-${{ github.ref }}
@@ -91,3 +121,12 @@ jobs:
91121

92122
- name: Perform CodeQL analysis
93123
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
124+
125+
result:
126+
name: Security / Result
127+
if: always()
128+
needs: [changes, govulncheck, osv-scanner, secrets, codeql]
129+
runs-on: ubuntu-latest
130+
steps:
131+
- run: exit 1
132+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')

0 commit comments

Comments
 (0)