@htekdev/actions-debugger 1.0.89 → 1.0.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/caching-artifacts/caching-artifacts-051.yml +86 -0
- package/errors/concurrency-timing/concurrency-timing-044.yml +95 -0
- package/errors/known-unsolved/known-unsolved-052.yml +119 -0
- package/errors/permissions-auth/permissions-auth-051.yml +103 -0
- package/errors/runner-environment/runner-environment-157.yml +92 -0
- package/errors/silent-failures/silent-failures-085.yml +69 -0
- package/errors/triggers/triggers-060.yml +86 -0
- package/errors/yaml-syntax/yaml-syntax-057.yml +93 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
id: caching-artifacts-051
|
|
2
|
+
title: 'download-artifact@v4 places each artifact in a named subdirectory, breaking v3 flat path assumption'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- download-artifact
|
|
7
|
+
- v4-migration
|
|
8
|
+
- directory-structure
|
|
9
|
+
- breaking-change
|
|
10
|
+
- path
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Downloading artifact.*to.*'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'ENOENT.*no such file or directory'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'no such file or directory.*dist\/'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Downloading artifact: my-artifact to /home/runner/work/repo/dist"
|
|
20
|
+
- "Error: ENOENT: no such file or directory, open 'dist/index.js'"
|
|
21
|
+
- "Error: Cannot find module '/home/runner/work/repo/dist/index.js'"
|
|
22
|
+
- "cp: cannot stat 'dist/main.js': No such file or directory"
|
|
23
|
+
root_cause: |
|
|
24
|
+
In actions/download-artifact@v3, downloading a named artifact with `path: dist`
|
|
25
|
+
placed files directly inside the `dist/` directory (flat layout). In v4,
|
|
26
|
+
the action changed to place each artifact in a subdirectory named after the
|
|
27
|
+
artifact: files land at `dist/<artifact-name>/filename` instead of `dist/filename`.
|
|
28
|
+
|
|
29
|
+
This is a silent failure: the `download-artifact` step succeeds with exit code 0
|
|
30
|
+
and emits no warning about the path change. Downstream steps that reference
|
|
31
|
+
`dist/index.js` fail with "no such file" errors — the file is actually at
|
|
32
|
+
`dist/my-artifact/index.js`. The behavior is especially confusing in matrix
|
|
33
|
+
workflows where each job uploads an artifact with a unique name, and the
|
|
34
|
+
consuming job downloads all artifacts into a single `path:`.
|
|
35
|
+
|
|
36
|
+
The change was made to support downloading multiple artifacts into the same
|
|
37
|
+
`path:` without name collisions, but it breaks any workflow that migrated from
|
|
38
|
+
v3 without adjusting downstream path references.
|
|
39
|
+
fix: |
|
|
40
|
+
Option 1 (recommended): Set the `path:` to a staging directory and reference
|
|
41
|
+
files using the artifact subdirectory path, or use `merge-multiple: true` to
|
|
42
|
+
flatten all artifacts into the `path:` directory.
|
|
43
|
+
|
|
44
|
+
Option 2: Name the artifact the same as the target directory and download into
|
|
45
|
+
the parent, so the subdirectory becomes the expected location.
|
|
46
|
+
|
|
47
|
+
Option 3: Add a step to move the artifact contents up one level after download.
|
|
48
|
+
fix_code:
|
|
49
|
+
- language: yaml
|
|
50
|
+
label: 'Use merge-multiple: true to restore v3 flat behavior for single artifact'
|
|
51
|
+
code: |
|
|
52
|
+
- uses: actions/download-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: dist-bundle
|
|
55
|
+
path: dist
|
|
56
|
+
merge-multiple: true # Flattens into dist/ directly, matching v3 behavior
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: 'Adjust downstream path to include artifact subdirectory'
|
|
59
|
+
code: |
|
|
60
|
+
- uses: actions/download-artifact@v4
|
|
61
|
+
with:
|
|
62
|
+
name: dist-bundle
|
|
63
|
+
path: staging
|
|
64
|
+
|
|
65
|
+
# Files land at staging/dist-bundle/index.js — reference accordingly
|
|
66
|
+
- run: node staging/dist-bundle/index.js
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Download all matrix artifacts and merge into one directory'
|
|
69
|
+
code: |
|
|
70
|
+
- uses: actions/download-artifact@v4
|
|
71
|
+
with:
|
|
72
|
+
pattern: build-* # Matches all matrix artifacts
|
|
73
|
+
path: dist
|
|
74
|
+
merge-multiple: true # All files merged flat into dist/
|
|
75
|
+
prevention:
|
|
76
|
+
- "When migrating from download-artifact@v3 to v4, audit all `path:` references and downstream file access patterns"
|
|
77
|
+
- "Use `merge-multiple: true` if you need the v3 flat download behavior for a single named artifact"
|
|
78
|
+
- "Set `path:` to a staging directory and explicitly reference the `path/<artifact-name>/` subdirectory in downstream steps"
|
|
79
|
+
- "Run the workflow once after migrating and check the action log for 'Downloading artifact: X to Y' to verify the actual destination path"
|
|
80
|
+
docs:
|
|
81
|
+
- url: 'https://github.com/actions/download-artifact/blob/main/docs/migration-guide.md'
|
|
82
|
+
label: 'download-artifact migration guide: v3 to v4 breaking changes'
|
|
83
|
+
- url: 'https://github.com/actions/download-artifact?tab=readme-ov-file#inputs'
|
|
84
|
+
label: 'download-artifact@v4 inputs reference — merge-multiple option'
|
|
85
|
+
- url: 'https://github.com/actions/upload-artifact/discussions/562'
|
|
86
|
+
label: 'Community discussion: v4 directory structure change from v3'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
id: concurrency-timing-044
|
|
2
|
+
title: 'Matrix jobs with a static concurrency group key serialize instead of running in parallel'
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- concurrency
|
|
8
|
+
- parallelism
|
|
9
|
+
- group-key
|
|
10
|
+
- strategy
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Waiting for a pending job to finish'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'This workflow is waiting for a pending job to complete'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- 'Waiting for a pending job to finish — concurrency: ci-deploy'
|
|
18
|
+
root_cause: |
|
|
19
|
+
When a workflow-level or job-level concurrency group is configured with a static
|
|
20
|
+
key that does not include the matrix dimension values, all matrix legs share the
|
|
21
|
+
same concurrency slot. GitHub Actions enforces that only one run occupies each
|
|
22
|
+
slot at a time, so instead of running N matrix legs in parallel, the legs queue
|
|
23
|
+
and execute one at a time — silently serializing a job strategy that was intended
|
|
24
|
+
to be parallel.
|
|
25
|
+
|
|
26
|
+
This can multiply workflow duration by the number of matrix dimensions (e.g., a
|
|
27
|
+
3-OS matrix that should finish in 10 minutes takes 30 minutes). The GitHub Actions
|
|
28
|
+
UI shows later matrix legs as "Waiting for a pending job to finish" which hints at
|
|
29
|
+
the problem, but developers often interpret this as runner resource contention rather
|
|
30
|
+
than a concurrency group misconfiguration.
|
|
31
|
+
|
|
32
|
+
Common patterns that trigger this:
|
|
33
|
+
concurrency:
|
|
34
|
+
group: ci-${{ github.ref }} # all matrix legs share "ci-refs/heads/main"
|
|
35
|
+
|
|
36
|
+
concurrency:
|
|
37
|
+
group: ${{ github.workflow }} # all legs share workflow name
|
|
38
|
+
fix: |
|
|
39
|
+
Include the relevant matrix dimension values in the concurrency group key so that
|
|
40
|
+
each matrix leg gets a unique slot. If cancel-in-progress behavior is still needed,
|
|
41
|
+
it will apply per-matrix-leg rather than globally across all legs.
|
|
42
|
+
|
|
43
|
+
If you intentionally want to serialize matrix legs (e.g., sequential deploys to
|
|
44
|
+
environments), keep the static key but document the serialization intent.
|
|
45
|
+
fix_code:
|
|
46
|
+
- language: yaml
|
|
47
|
+
label: 'Include matrix values in concurrency group key'
|
|
48
|
+
code: |
|
|
49
|
+
jobs:
|
|
50
|
+
test:
|
|
51
|
+
strategy:
|
|
52
|
+
matrix:
|
|
53
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
54
|
+
node: [18, 20, 22]
|
|
55
|
+
runs-on: ${{ matrix.os }}
|
|
56
|
+
concurrency:
|
|
57
|
+
# Unique slot per matrix leg — allows full parallelism
|
|
58
|
+
group: ci-${{ github.ref }}-${{ matrix.os }}-${{ matrix.node }}
|
|
59
|
+
cancel-in-progress: true
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
- uses: actions/setup-node@v4
|
|
63
|
+
with:
|
|
64
|
+
node-version: ${{ matrix.node }}
|
|
65
|
+
- run: npm test
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: 'Workflow-level concurrency must also include matrix values'
|
|
68
|
+
code: |
|
|
69
|
+
# BAD: workflow-level concurrency applies to ALL jobs, including matrix legs
|
|
70
|
+
# concurrency:
|
|
71
|
+
# group: ci-${{ github.ref }} # serializes all legs!
|
|
72
|
+
# cancel-in-progress: true
|
|
73
|
+
|
|
74
|
+
# GOOD: set concurrency at the job level with matrix dimensions in the key
|
|
75
|
+
jobs:
|
|
76
|
+
build:
|
|
77
|
+
strategy:
|
|
78
|
+
matrix:
|
|
79
|
+
platform: [linux, windows, macos]
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
concurrency:
|
|
82
|
+
group: build-${{ github.ref }}-${{ matrix.platform }}
|
|
83
|
+
cancel-in-progress: true
|
|
84
|
+
steps:
|
|
85
|
+
- run: echo "Building for ${{ matrix.platform }}"
|
|
86
|
+
prevention:
|
|
87
|
+
- 'Always include matrix dimension values in concurrency group keys: group: ci-${{ github.ref }}-${{ matrix.os }}'
|
|
88
|
+
- 'Check workflow execution time after adding concurrency groups — unexpected serialization increases total duration'
|
|
89
|
+
- 'Use workflow-level concurrency only for single-job workflows; for matrix jobs, always configure concurrency at the job level'
|
|
90
|
+
- 'Verify parallelism by checking the GitHub Actions timeline view — all matrix legs should show overlapping execution'
|
|
91
|
+
docs:
|
|
92
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-concurrency'
|
|
93
|
+
label: 'Using concurrency in GitHub Actions'
|
|
94
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix'
|
|
95
|
+
label: 'Matrix strategy syntax reference'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
id: known-unsolved-052
|
|
2
|
+
title: 'GitHub-hosted runner IP addresses change on every workflow run — static IP allowlisting unreliable'
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- runner
|
|
7
|
+
- ip-address
|
|
8
|
+
- firewall
|
|
9
|
+
- allowlist
|
|
10
|
+
- network
|
|
11
|
+
- hosted-runner
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Connection refused|Connection timed out|Unable to connect'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'connect ECONNREFUSED|ECONNRESET|ETIMEDOUT'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'blocked by firewall|access denied.*firewall|ip.*not.*allowed'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- 'Error: connect ECONNREFUSED 10.0.0.5:5432'
|
|
21
|
+
- 'curl: (7) Failed to connect to internal-api.company.com port 443: Connection refused'
|
|
22
|
+
- 'Error: connect ETIMEDOUT'
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub-hosted runners are allocated from a large, rotating pool of virtual machines.
|
|
25
|
+
The IP address assigned to each run is drawn from GitHub's published CIDR ranges
|
|
26
|
+
(available via https://api.github.com/meta under "actions") but changes on every
|
|
27
|
+
workflow run — there is no way to obtain a stable, predictable IP for a GitHub-hosted
|
|
28
|
+
runner in advance.
|
|
29
|
+
|
|
30
|
+
GitHub publishes these IP ranges for documentation purposes, but:
|
|
31
|
+
1. The ranges are broad (/20 or larger) and overlap with other GitHub services
|
|
32
|
+
2. The specific IP within the range cannot be predicted before the run starts
|
|
33
|
+
3. The ranges update periodically, requiring allowlist maintenance
|
|
34
|
+
4. On each run, the runner's IP can be any address in the published ranges
|
|
35
|
+
|
|
36
|
+
This makes static IP allowlisting in external firewalls, database security groups
|
|
37
|
+
(AWS RDS, Azure SQL, Cloudflare), or on-premise systems unreliable. Allowlisting the
|
|
38
|
+
entire published range exposes hundreds of thousands of IP addresses and violates
|
|
39
|
+
least-privilege security principles.
|
|
40
|
+
|
|
41
|
+
This is a fundamental architecture constraint of GitHub-hosted runners and has no
|
|
42
|
+
perfect solution — only workarounds.
|
|
43
|
+
fix: |
|
|
44
|
+
There is no way to guarantee a stable IP for GitHub-hosted runners. Use one of these
|
|
45
|
+
patterns instead of IP allowlisting:
|
|
46
|
+
|
|
47
|
+
1. OIDC (preferred): Use OIDC tokens to authenticate to cloud providers (AWS, Azure,
|
|
48
|
+
GCP) rather than network-level access controls. No IP allowlisting needed.
|
|
49
|
+
|
|
50
|
+
2. Self-hosted runners: Deploy runners on your network with known, stable IPs.
|
|
51
|
+
|
|
52
|
+
3. Dynamic allowlisting: At workflow start, call your cloud provider API to add the
|
|
53
|
+
runner's current IP to the security group; remove it at workflow end. Brittle but
|
|
54
|
+
workable for database access.
|
|
55
|
+
|
|
56
|
+
4. VPN/tunnel action: Use actions like cloudflare/cloudflare-warp-action or a
|
|
57
|
+
WireGuard setup action to tunnel runner traffic through a fixed gateway IP.
|
|
58
|
+
|
|
59
|
+
5. Private hosted runners (GitHub Enterprise): If on GHEC, configure static IP ranges
|
|
60
|
+
via network configurations for GitHub-hosted runners.
|
|
61
|
+
fix_code:
|
|
62
|
+
- language: yaml
|
|
63
|
+
label: 'Use OIDC authentication instead of IP allowlisting (AWS example)'
|
|
64
|
+
code: |
|
|
65
|
+
jobs:
|
|
66
|
+
deploy:
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
permissions:
|
|
69
|
+
id-token: write
|
|
70
|
+
contents: read
|
|
71
|
+
steps:
|
|
72
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
73
|
+
with:
|
|
74
|
+
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
|
|
75
|
+
aws-region: us-east-1
|
|
76
|
+
# No IP allowlisting needed — OIDC validates the token, not the IP
|
|
77
|
+
- run: aws rds describe-db-instances
|
|
78
|
+
- language: yaml
|
|
79
|
+
label: 'Dynamic security group allowlisting (temporary, for legacy systems)'
|
|
80
|
+
code: |
|
|
81
|
+
jobs:
|
|
82
|
+
test-with-db:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
steps:
|
|
85
|
+
- name: Get runner IP
|
|
86
|
+
id: ip
|
|
87
|
+
run: echo "ip=$(curl -s https://api.ipify.org)" >> $GITHUB_OUTPUT
|
|
88
|
+
|
|
89
|
+
- name: Add runner IP to DB security group
|
|
90
|
+
run: |
|
|
91
|
+
aws ec2 authorize-security-group-ingress \
|
|
92
|
+
--group-id sg-0abc123 \
|
|
93
|
+
--protocol tcp \
|
|
94
|
+
--port 5432 \
|
|
95
|
+
--cidr "${{ steps.ip.outputs.ip }}/32"
|
|
96
|
+
|
|
97
|
+
- name: Run tests
|
|
98
|
+
run: npm test
|
|
99
|
+
|
|
100
|
+
- name: Remove runner IP from security group
|
|
101
|
+
if: always()
|
|
102
|
+
run: |
|
|
103
|
+
aws ec2 revoke-security-group-ingress \
|
|
104
|
+
--group-id sg-0abc123 \
|
|
105
|
+
--protocol tcp \
|
|
106
|
+
--port 5432 \
|
|
107
|
+
--cidr "${{ steps.ip.outputs.ip }}/32"
|
|
108
|
+
prevention:
|
|
109
|
+
- 'Design CI pipelines to use token-based authentication (OIDC, API keys, mTLS) rather than IP allowlisting from the start'
|
|
110
|
+
- 'For AWS: use IAM roles with OIDC (aws-actions/configure-aws-credentials); for Azure: use azure/login with OIDC; for GCP: use google-github-actions/auth with Workload Identity Federation'
|
|
111
|
+
- 'If IP allowlisting is unavoidable, use GitHub Enterprise Cloud with a configured network allowing stable egress IPs'
|
|
112
|
+
- 'Subscribe to GitHub changelog for updates to runner IP range publications — the ranges do expand over time'
|
|
113
|
+
docs:
|
|
114
|
+
- url: 'https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#ip-addresses'
|
|
115
|
+
label: 'GitHub-hosted runner IP addresses'
|
|
116
|
+
- url: 'https://api.github.com/meta'
|
|
117
|
+
label: 'GitHub meta API — published actions IP ranges'
|
|
118
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-cloud-providers'
|
|
119
|
+
label: 'Configuring OIDC in cloud providers'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
id: permissions-auth-051
|
|
2
|
+
title: 'GITHUB_TOKEN push blocked by repository ruleset despite contents: write permission'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-token
|
|
7
|
+
- rulesets
|
|
8
|
+
- contents-write
|
|
9
|
+
- branch-protection
|
|
10
|
+
- bypass-list
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'GH013: Repository rule violations found for refs\/'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'remote: error: Changes must be made through a pull request'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'refusing to allow.*to create or update'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "remote: error: GH013: Repository rule violations found for refs/heads/main."
|
|
20
|
+
- "remote: error: GH013: Repository rule violations found for refs/tags/v1.0.0."
|
|
21
|
+
- "remote: error: Changes must be made through a pull request."
|
|
22
|
+
- "error: failed to push some refs to 'https://github.com/ORG/REPO.git'"
|
|
23
|
+
- "refusing to allow a GitHub App to create or update file"
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub Rulesets (generally available January 2025) allow organization and repository
|
|
26
|
+
administrators to define push rules that enforce requirements such as "all pushes
|
|
27
|
+
must come through a pull request", "signed commits required", or "tag patterns
|
|
28
|
+
must match a format". Unlike legacy branch protection rules, rulesets apply to
|
|
29
|
+
ALL actors including GitHub Apps and the GITHUB_TOKEN by default — regardless of
|
|
30
|
+
the `contents: write` permission granted in the workflow's `permissions:` block.
|
|
31
|
+
|
|
32
|
+
The `contents: write` permission only controls whether the token is *authorized*
|
|
33
|
+
to make write API calls; it does not grant exemption from repository rulesets.
|
|
34
|
+
Rulesets have a separate "bypass list" in Settings > Rules > Rulesets that must
|
|
35
|
+
explicitly include "GitHub Actions" (or a specific GitHub App) to allow workflow
|
|
36
|
+
pushes to bypass ruleset restrictions.
|
|
37
|
+
|
|
38
|
+
This error affects workflows that push commits directly (e.g., auto-format,
|
|
39
|
+
changelog generation, version bump commits) or push tags (e.g., release tagging)
|
|
40
|
+
to branches or tags governed by a ruleset.
|
|
41
|
+
fix: |
|
|
42
|
+
Option 1 (recommended): Add GitHub Actions to the ruleset bypass list.
|
|
43
|
+
Go to Settings > Rules > Rulesets > (select the ruleset) > Bypass list >
|
|
44
|
+
Add bypass > select "GitHub Actions". This allows workflow GITHUB_TOKEN
|
|
45
|
+
pushes to bypass the ruleset.
|
|
46
|
+
|
|
47
|
+
Option 2: Use a Personal Access Token (PAT) or GitHub App token from a user
|
|
48
|
+
account that is in the bypass list. Pass the token via a secret and use it
|
|
49
|
+
in the push step.
|
|
50
|
+
|
|
51
|
+
Option 3: Restructure the workflow to push via a pull request instead of
|
|
52
|
+
directly to the protected branch, satisfying the "PR required" ruleset rule.
|
|
53
|
+
fix_code:
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: 'Use a PAT secret to push when GITHUB_TOKEN is blocked by rulesets'
|
|
56
|
+
code: |
|
|
57
|
+
jobs:
|
|
58
|
+
release:
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
with:
|
|
63
|
+
token: ${{ secrets.RELEASE_PAT }} # PAT from bypass-listed account
|
|
64
|
+
fetch-depth: 0
|
|
65
|
+
|
|
66
|
+
# ... version bump or commit steps ...
|
|
67
|
+
|
|
68
|
+
- name: Push release commit and tag
|
|
69
|
+
env:
|
|
70
|
+
GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
|
|
71
|
+
run: |
|
|
72
|
+
# Use the PAT-authenticated remote for push
|
|
73
|
+
echo "Push authorized via PAT from bypass-listed account"
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: 'Use a GitHub App token with bypass list membership'
|
|
76
|
+
code: |
|
|
77
|
+
jobs:
|
|
78
|
+
release:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/create-github-app-token@v2
|
|
82
|
+
id: app-token
|
|
83
|
+
with:
|
|
84
|
+
app-id: ${{ vars.RELEASE_APP_ID }}
|
|
85
|
+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
|
86
|
+
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
with:
|
|
89
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
90
|
+
|
|
91
|
+
# Push step uses the App token — ensure the App is in bypass list
|
|
92
|
+
prevention:
|
|
93
|
+
- "When enabling rulesets on a repository or organization, immediately check CI/CD workflows that push directly to protected branches or tags"
|
|
94
|
+
- "Add GitHub Actions to ruleset bypass lists proactively when creating rulesets that restrict direct pushes"
|
|
95
|
+
- "Prefer pushing via pull requests (open PR, auto-merge) over direct commits in automated workflows — this satisfies 'PR required' rules and avoids bypass list management"
|
|
96
|
+
- "Audit existing legacy branch protection rules when migrating to rulesets — bypass behavior differs: branch protection 'Restrict who can push' exempts admins by default; rulesets do not"
|
|
97
|
+
docs:
|
|
98
|
+
- url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets'
|
|
99
|
+
label: 'GitHub Docs: Available rules for rulesets'
|
|
100
|
+
- url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/creating-rulesets-for-a-repository#granting-bypass-permissions-for-your-ruleset'
|
|
101
|
+
label: 'GitHub Docs: Granting bypass permissions for a ruleset'
|
|
102
|
+
- url: 'https://github.blog/changelog/2025-01-29-github-rulesets-are-generally-available/'
|
|
103
|
+
label: 'GitHub Changelog: Rulesets generally available — January 2025'
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
id: runner-environment-157
|
|
2
|
+
title: 'setup-python EOL Python version triggers slow pyenv compilation on ubuntu-24.04'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- setup-python
|
|
7
|
+
- pyenv
|
|
8
|
+
- ubuntu-24.04
|
|
9
|
+
- python-version
|
|
10
|
+
- toolcache
|
|
11
|
+
- build-time
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Version \d+\.\d+\.\d+ was not found in the local cache'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'pyenv install python-\d+\.\d+\.\d+'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'python-build: .*not installed'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Version 3.8.20 was not found in the local cache"
|
|
21
|
+
- "Version 3.9.19 was not found in the local cache"
|
|
22
|
+
- "pyenv install python-3.8.20"
|
|
23
|
+
- "python-build: python not found in PATH"
|
|
24
|
+
- "Successfully installed cpython: 3.9.18 (took: 523.19 seconds)"
|
|
25
|
+
root_cause: |
|
|
26
|
+
ubuntu-24.04 runners ship with Python 3.11, 3.12, and 3.13 in the pre-installed
|
|
27
|
+
toolcache. Python 3.8 (EOL October 2024), 3.9, and 3.10 were removed from the
|
|
28
|
+
ubuntu-24.04 toolcache entirely. When actions/setup-python requests one of these
|
|
29
|
+
versions, it cannot find a cached build and falls back to compiling Python from
|
|
30
|
+
source via pyenv. The compilation process — downloading Python source, running
|
|
31
|
+
./configure, make, make install with GCC — typically takes 5–10 minutes and can
|
|
32
|
+
exhaust the default 6-hour job timeout in heavily parallelized pipelines or
|
|
33
|
+
cause unexpectedly slow CI on free-tier runners. The warning "Version X was not
|
|
34
|
+
found in the local cache" is emitted but easy to miss in long logs, and the
|
|
35
|
+
action still exits 0 on success, making the slowdown invisible until CI bills
|
|
36
|
+
or timeout failures appear.
|
|
37
|
+
fix: |
|
|
38
|
+
Option 1 (recommended): Upgrade to a Python version pre-installed on ubuntu-24.04
|
|
39
|
+
(3.11, 3.12, or 3.13). These are available instantly from the toolcache.
|
|
40
|
+
|
|
41
|
+
Option 2: Pin the runner to ubuntu-22.04, which retains Python 3.9 and 3.10
|
|
42
|
+
in the toolcache through its support window.
|
|
43
|
+
|
|
44
|
+
Option 3: Add the deadsnakes PPA before calling setup-python to provide fast
|
|
45
|
+
pre-built binaries for older Python versions on ubuntu-24.
|
|
46
|
+
|
|
47
|
+
Option 4: Use a container image that pre-bundles your Python version to avoid
|
|
48
|
+
the toolcache entirely.
|
|
49
|
+
fix_code:
|
|
50
|
+
- language: yaml
|
|
51
|
+
label: 'Upgrade to a toolcache-available Python version'
|
|
52
|
+
code: |
|
|
53
|
+
- uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: '3.12' # Pre-installed on ubuntu-24.04; no pyenv fallback
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'Use ubuntu-22.04 runner if 3.9 or 3.10 is required'
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
test:
|
|
61
|
+
runs-on: ubuntu-22.04 # Retains Python 3.9/3.10 in toolcache
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: '3.10'
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: 'Deadsnakes PPA for fast pre-built older Python on ubuntu-24.04'
|
|
68
|
+
code: |
|
|
69
|
+
jobs:
|
|
70
|
+
test:
|
|
71
|
+
runs-on: ubuntu-24.04
|
|
72
|
+
steps:
|
|
73
|
+
- name: Add deadsnakes PPA
|
|
74
|
+
run: |
|
|
75
|
+
sudo add-apt-repository ppa:deadsnakes/ppa
|
|
76
|
+
sudo apt-get update
|
|
77
|
+
sudo apt-get install -y python3.9 python3.9-venv python3.9-dev
|
|
78
|
+
- uses: actions/setup-python@v5
|
|
79
|
+
with:
|
|
80
|
+
python-version: '3.9'
|
|
81
|
+
prevention:
|
|
82
|
+
- "Avoid pinning `python-version` to EOL releases (3.8 EOL Oct 2024, 3.9 EOL Oct 2025)"
|
|
83
|
+
- "Check the ubuntu-24.04 runner toolcache inventory at github.com/actions/runner-images for available Python versions"
|
|
84
|
+
- "Use `python-version-file: .python-version` or `pyproject.toml` to track the project's minimum supported version and update it when runners drop EOL toolcache entries"
|
|
85
|
+
- "Set a `timeout-minutes` on the job so pyenv compilation failures surface quickly rather than running for hours"
|
|
86
|
+
docs:
|
|
87
|
+
- url: 'https://github.com/actions/setup-python/issues/726'
|
|
88
|
+
label: 'setup-python: Python 3.8/3.9 not found in toolcache on ubuntu-24.04'
|
|
89
|
+
- url: 'https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md'
|
|
90
|
+
label: 'ubuntu-24.04 runner image readme — pre-installed Python versions'
|
|
91
|
+
- url: 'https://devguide.python.org/versions/'
|
|
92
|
+
label: 'Python release lifecycle — EOL dates by version'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
id: silent-failures-085
|
|
2
|
+
title: 'actions/checkout does not initialize submodules by default — empty submodule directories silently break builds'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- submodules
|
|
8
|
+
- git-submodule
|
|
9
|
+
- empty-directory
|
|
10
|
+
- build-failure
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'fatal: not a git repository'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'No such file or directory'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'cannot open.*No such file or directory'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- 'fatal: not a git repository (or any of the parent directories): .git'
|
|
20
|
+
- 'CMake Error: The source directory "/home/runner/work/repo/vendor/lib" does not appear to contain CMakeLists.txt.'
|
|
21
|
+
- 'error: cannot open include file: ../vendor/lib/include/lib.h: No such file or directory'
|
|
22
|
+
root_cause: |
|
|
23
|
+
actions/checkout defaults to submodules: false, meaning any Git submodules
|
|
24
|
+
defined in .gitmodules are NOT cloned — they appear as empty directories in the
|
|
25
|
+
workspace. There is no warning or error in the checkout step output; it completes
|
|
26
|
+
successfully with exit code 0.
|
|
27
|
+
|
|
28
|
+
Downstream build steps that depend on submodule content then fail with generic
|
|
29
|
+
"file not found" or "not a git repository" errors that point to the submodule
|
|
30
|
+
directory, not to the checkout configuration. This mismatch between where the error
|
|
31
|
+
appears (the build step) and its root cause (the checkout step) makes it
|
|
32
|
+
time-consuming to diagnose, especially for contributors unfamiliar with the repo's
|
|
33
|
+
submodule structure.
|
|
34
|
+
|
|
35
|
+
Affected scenarios include:
|
|
36
|
+
- CMake projects with vendored dependencies as submodules
|
|
37
|
+
- Projects using git submodule for shared library code
|
|
38
|
+
- Repos with submodules for test fixtures or documentation themes
|
|
39
|
+
fix: |
|
|
40
|
+
Add submodules: true (or submodules: 'recursive' for nested submodules) to your
|
|
41
|
+
actions/checkout step. For private submodules, you may also need to set
|
|
42
|
+
token: with a PAT that has access to the submodule repositories — the default
|
|
43
|
+
GITHUB_TOKEN only has access to the current repository.
|
|
44
|
+
fix_code:
|
|
45
|
+
- language: yaml
|
|
46
|
+
label: 'Initialize submodules during checkout'
|
|
47
|
+
code: |
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
submodules: true # 'true' for one level, 'recursive' for nested
|
|
51
|
+
# For private submodule repos, provide a PAT with access:
|
|
52
|
+
# token: ${{ secrets.SUBMODULE_PAT }}
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: 'Recursive submodules for nested submodule trees'
|
|
55
|
+
code: |
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
with:
|
|
58
|
+
submodules: 'recursive'
|
|
59
|
+
fetch-depth: 0 # Full history if submodule pinning requires it
|
|
60
|
+
prevention:
|
|
61
|
+
- 'Add submodules: true to actions/checkout in all workflows that build code depending on submodule content'
|
|
62
|
+
- 'Add a verification step after checkout to confirm critical submodule paths are non-empty: test -f vendor/lib/README.md'
|
|
63
|
+
- 'Document submodule requirements in your repo README and CONTRIBUTING.md'
|
|
64
|
+
- 'If the submodule repo is private, use a GitHub App token or fine-grained PAT — the default GITHUB_TOKEN cannot access other repositories'
|
|
65
|
+
docs:
|
|
66
|
+
- url: 'https://github.com/actions/checkout#usage'
|
|
67
|
+
label: 'actions/checkout — submodules input reference'
|
|
68
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-submodules-in-workflows'
|
|
69
|
+
label: 'Using submodules in GitHub Actions workflows'
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
id: triggers-060
|
|
2
|
+
title: 'pull_request workflow not re-triggered when PR title or description is edited'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull_request
|
|
7
|
+
- edited
|
|
8
|
+
- pr-title
|
|
9
|
+
- types
|
|
10
|
+
- conventional-commits
|
|
11
|
+
- status-check
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on:\s*\n\s*pull_request:\s*\n(?:(?!\s*types:).*\n)*'
|
|
14
|
+
flags: 'ms'
|
|
15
|
+
error_messages: []
|
|
16
|
+
root_cause: |
|
|
17
|
+
The default pull_request event types are [opened, synchronize, reopened]. The
|
|
18
|
+
"edited" activity type — which fires when the PR title, body, or base branch is
|
|
19
|
+
changed — is NOT included in the default set. Workflows that validate PR titles
|
|
20
|
+
(e.g., enforcing Conventional Commits format, ticket-number requirements, or length
|
|
21
|
+
limits) will never re-run when a contributor fixes the PR title after an initial
|
|
22
|
+
failure.
|
|
23
|
+
|
|
24
|
+
This creates a persistent UX problem: the required status check shows as failed
|
|
25
|
+
after the title is corrected, and the PR cannot be merged without manually
|
|
26
|
+
re-running the workflow or pushing a new commit. The fix is visible in the GitHub
|
|
27
|
+
UI and source, but the root cause is non-obvious — nothing in the failure log
|
|
28
|
+
indicates the workflow won't respond to a title edit.
|
|
29
|
+
fix: |
|
|
30
|
+
Add "edited" to the pull_request types list for any workflow that validates PR
|
|
31
|
+
metadata (title, description, or target branch). The "edited" type also fires on
|
|
32
|
+
body changes and base branch changes, which is typically harmless for title-checking
|
|
33
|
+
workflows. Do not add "edited" to workflows that trigger expensive CI operations
|
|
34
|
+
unless you intend them to run on every title/body change.
|
|
35
|
+
fix_code:
|
|
36
|
+
- language: yaml
|
|
37
|
+
label: 'Add edited type to re-run on PR title changes'
|
|
38
|
+
code: |
|
|
39
|
+
on:
|
|
40
|
+
pull_request:
|
|
41
|
+
types:
|
|
42
|
+
- opened
|
|
43
|
+
- synchronize
|
|
44
|
+
- reopened
|
|
45
|
+
- edited # Re-run when PR title or description is changed
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
lint-pr-title:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: amannn/action-semantic-pull-request@v5
|
|
52
|
+
env:
|
|
53
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: 'Conditional execution — only validate title on edited events'
|
|
56
|
+
code: |
|
|
57
|
+
on:
|
|
58
|
+
pull_request:
|
|
59
|
+
types: [opened, synchronize, reopened, edited]
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
check-title:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- name: Validate PR title format
|
|
66
|
+
if: >
|
|
67
|
+
github.event_name == 'pull_request' &&
|
|
68
|
+
(github.event.action == 'opened' ||
|
|
69
|
+
github.event.action == 'edited' ||
|
|
70
|
+
github.event.action == 'reopened')
|
|
71
|
+
run: |
|
|
72
|
+
TITLE="${{ github.event.pull_request.title }}"
|
|
73
|
+
if ! echo "$TITLE" | grep -qP '^(feat|fix|docs|chore|refactor|test|ci)(\(.+\))?: .+'; then
|
|
74
|
+
echo "PR title does not follow Conventional Commits format: $TITLE"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
prevention:
|
|
78
|
+
- 'Always include "edited" in pull_request types when the workflow validates PR title or description format'
|
|
79
|
+
- 'Test that your PR title validation workflow re-runs when you edit the title — do not assume it does'
|
|
80
|
+
- 'Document required PR title format in CONTRIBUTING.md so contributors know title edits (not just new commits) trigger re-checks'
|
|
81
|
+
- 'Consider adding a job summary or annotation with the exact format requirement when validation fails'
|
|
82
|
+
docs:
|
|
83
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request'
|
|
84
|
+
label: 'pull_request event — activity types reference'
|
|
85
|
+
- url: 'https://github.com/amannn/action-semantic-pull-request'
|
|
86
|
+
label: 'action-semantic-pull-request (popular PR title lint action)'
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: yaml-syntax-057
|
|
2
|
+
title: 'needs: referencing undefined job ID causes instant workflow validation failure'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- needs
|
|
7
|
+
- job-id
|
|
8
|
+
- validation
|
|
9
|
+
- typo
|
|
10
|
+
- workflow-syntax
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Job '.*' depends on unknown job '.*'"
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: "A job named '.*' does not exist in this workflow"
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'The workflow is not valid.*depends on unknown job'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "The workflow is not valid. .github/workflows/ci.yml (Line 42, Col 14): Job 'deploy' depends on unknown job 'biuld'."
|
|
20
|
+
- "A job named 'biuld' does not exist in this workflow."
|
|
21
|
+
- "Job 'release' depends on unknown job 'test-and-build'."
|
|
22
|
+
- "The workflow is not valid. .github/workflows/release.yml: Job 'publish' depends on unknown job 'build_and_test'."
|
|
23
|
+
root_cause: |
|
|
24
|
+
The `needs:` key in a job definition must exactly reference the IDs of other jobs
|
|
25
|
+
defined under `jobs:` in the same workflow file. Job IDs are the YAML map keys
|
|
26
|
+
(e.g., `jobs.build:`, `jobs.test:`), not the `name:` display values.
|
|
27
|
+
|
|
28
|
+
A typo in a `needs:` value (e.g., `needs: [biuld]` instead of `needs: [build]`,
|
|
29
|
+
or `needs: test-and-build` when the job is named `test_and_build`) causes GitHub
|
|
30
|
+
Actions to reject the entire workflow at parse time with a validation error.
|
|
31
|
+
No jobs run — the workflow fails before any step executes.
|
|
32
|
+
|
|
33
|
+
This error is particularly common after renaming jobs, since the `name:` field
|
|
34
|
+
(display name in the UI) can be changed without affecting the job ID used in
|
|
35
|
+
`needs:`. Developers who update `name:` but forget to update downstream `needs:`
|
|
36
|
+
references see the workflow fail immediately on next push with no prior warning.
|
|
37
|
+
fix: |
|
|
38
|
+
Ensure every value in `needs:` exactly matches a job ID key defined at the
|
|
39
|
+
same level under `jobs:`. Job IDs are case-sensitive and use the exact YAML key
|
|
40
|
+
name — not the `name:` display value.
|
|
41
|
+
|
|
42
|
+
Use `actionlint` locally or in pre-commit hooks to catch unknown `needs:` references
|
|
43
|
+
before pushing.
|
|
44
|
+
fix_code:
|
|
45
|
+
- language: yaml
|
|
46
|
+
label: 'Correct needs: to match the exact job ID key'
|
|
47
|
+
code: |
|
|
48
|
+
jobs:
|
|
49
|
+
build: # <-- This is the job ID (the YAML key)
|
|
50
|
+
name: Build and Test # This is the display name — NOT used in needs:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- run: echo "building"
|
|
54
|
+
|
|
55
|
+
deploy:
|
|
56
|
+
needs: [build] # Must reference the job ID key, not the name: value
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- run: echo "deploying"
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: 'Rename both job ID and needs: reference after a job rename'
|
|
62
|
+
code: |
|
|
63
|
+
# WRONG — job was renamed from 'build' to 'build-and-test' but needs: not updated
|
|
64
|
+
# jobs:
|
|
65
|
+
# build-and-test:
|
|
66
|
+
# ...
|
|
67
|
+
# deploy:
|
|
68
|
+
# needs: [build] # Error: 'build' no longer exists
|
|
69
|
+
|
|
70
|
+
# CORRECT — update needs: to match the new job ID
|
|
71
|
+
jobs:
|
|
72
|
+
build-and-test:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- run: echo "building"
|
|
76
|
+
|
|
77
|
+
deploy:
|
|
78
|
+
needs: [build-and-test] # Updated to match renamed job ID
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- run: echo "deploying"
|
|
82
|
+
prevention:
|
|
83
|
+
- "Use `actionlint` (rhysd/actionlint) locally or as a pre-commit hook — it validates `needs:` references against defined job IDs at lint time"
|
|
84
|
+
- "Keep job IDs short and stable (e.g., `build`, `test`, `deploy`); use the `name:` field for human-readable display names that can be changed freely"
|
|
85
|
+
- "When renaming a job ID, search the entire workflow file for all `needs:` references to that ID before committing"
|
|
86
|
+
- "Enable the actionlint GitHub Actions workflow check in your repository to catch validation errors in PRs before merge"
|
|
87
|
+
docs:
|
|
88
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds'
|
|
89
|
+
label: 'GitHub Docs: jobs.<job_id>.needs syntax'
|
|
90
|
+
- url: 'https://rhysd.github.io/actionlint/'
|
|
91
|
+
label: 'actionlint — static checker for GitHub Actions workflow files'
|
|
92
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idname'
|
|
93
|
+
label: 'GitHub Docs: jobs.<job_id>.name vs job ID distinction'
|
package/package.json
CHANGED