@htekdev/actions-debugger 1.0.88 → 1.0.90
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/concurrency-timing/concurrency-timing-044.yml +95 -0
- package/errors/known-unsolved/known-unsolved-052.yml +119 -0
- package/errors/silent-failures/silent-failures-082.yml +93 -0
- package/errors/silent-failures/silent-failures-083.yml +64 -0
- package/errors/silent-failures/silent-failures-084.yml +96 -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-056.yml +87 -0
- package/package.json +1 -1
|
@@ -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,93 @@
|
|
|
1
|
+
id: silent-failures-082
|
|
2
|
+
title: "pull_request_target checkout defaults to base branch, not PR head"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull_request_target
|
|
7
|
+
- checkout
|
|
8
|
+
- security
|
|
9
|
+
- fork
|
|
10
|
+
- base-branch
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'on:\s*\n\s+pull_request_target'
|
|
13
|
+
flags: 'im'
|
|
14
|
+
- regex: 'pull_request_target'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "Workflow runs on base branch code instead of PR contributor changes"
|
|
18
|
+
- "Tests pass on base but fail on PR code with no visible error in logs"
|
|
19
|
+
root_cause: |
|
|
20
|
+
When a workflow is triggered by `pull_request_target`, GitHub Actions runs the workflow
|
|
21
|
+
from the TARGET repository's base branch — not the contributor's PR head. This is
|
|
22
|
+
intentional: `pull_request_target` provides write-level token access (for labeling,
|
|
23
|
+
commenting, posting status checks), so GitHub protects secrets by refusing to run
|
|
24
|
+
untrusted fork code with elevated permissions.
|
|
25
|
+
|
|
26
|
+
As a result, `actions/checkout` checks out `github.sha`, which resolves to the merge
|
|
27
|
+
base commit on the target branch. Any tests, linting, or code analysis in this
|
|
28
|
+
workflow validate the base branch, not the contributor's changes. The workflow
|
|
29
|
+
succeeds silently while testing the wrong code.
|
|
30
|
+
fix: |
|
|
31
|
+
Option 1 (recommended — safe): Use `pull_request` (not `pull_request_target`) for
|
|
32
|
+
running PR code. `pull_request` triggers run with read-only tokens and check out the
|
|
33
|
+
PR head by default. Use `pull_request_target` only for privileged actions like posting
|
|
34
|
+
comments or labels.
|
|
35
|
+
|
|
36
|
+
Option 2 (explicit checkout — use with caution): Explicitly check out the PR head
|
|
37
|
+
using `github.event.pull_request.head.sha`. Only do this if you fully trust all
|
|
38
|
+
contributors and understand that fork code will execute with the repository write token.
|
|
39
|
+
fix_code:
|
|
40
|
+
- language: yaml
|
|
41
|
+
label: "Recommended: use pull_request for code execution, pull_request_target for privileged writes"
|
|
42
|
+
code: |
|
|
43
|
+
# Workflow 1: run tests on PR code (read-only, safe for forks)
|
|
44
|
+
on:
|
|
45
|
+
pull_request:
|
|
46
|
+
branches: [main]
|
|
47
|
+
|
|
48
|
+
jobs:
|
|
49
|
+
test:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
# Checks out PR head automatically — no ref: override needed
|
|
54
|
+
- run: npm test
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
# Workflow 2: post labels (write access via pull_request_target)
|
|
58
|
+
on:
|
|
59
|
+
pull_request_target:
|
|
60
|
+
types: [opened]
|
|
61
|
+
|
|
62
|
+
jobs:
|
|
63
|
+
label:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
permissions:
|
|
66
|
+
pull-requests: write
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/labeler@v5
|
|
69
|
+
# Never runs contributor code here — only labels the PR
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "If you must run PR code under pull_request_target (risky)"
|
|
72
|
+
code: |
|
|
73
|
+
on:
|
|
74
|
+
pull_request_target:
|
|
75
|
+
branches: [main]
|
|
76
|
+
|
|
77
|
+
jobs:
|
|
78
|
+
test:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
with:
|
|
83
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
84
|
+
# WARNING: executes untrusted fork code with write-level token
|
|
85
|
+
prevention:
|
|
86
|
+
- "Use pull_request (not pull_request_target) for workflows that run code — pull_request tokens are read-only and checkout the PR head correctly by default"
|
|
87
|
+
- "Reserve pull_request_target for privileged write-only actions: posting comments, applying labels, updating statuses — never checkout and execute untrusted PR code in these workflows"
|
|
88
|
+
- "If CI consistently passes but reviewers find bugs tests should catch, verify which commit your checkout step is using by printing github.sha vs github.event.pull_request.head.sha"
|
|
89
|
+
docs:
|
|
90
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target"
|
|
91
|
+
label: "pull_request_target event — security considerations"
|
|
92
|
+
- url: "https://securitylab.github.com/research/github-actions-preventing-pwn-requests/"
|
|
93
|
+
label: "GitHub Security Lab: Preventing pwn requests (pull_request_target risks)"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
id: silent-failures-083
|
|
2
|
+
title: "matrix strategy fail-fast true by default silently cancels all remaining jobs on first failure"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- fail-fast
|
|
8
|
+
- cancellation
|
|
9
|
+
- strategy
|
|
10
|
+
- parallel-jobs
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'strategy:\s*\n\s+matrix:'
|
|
13
|
+
flags: 'im'
|
|
14
|
+
- regex: 'Some jobs were not executed because a previous required job failed'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "Some jobs were not executed because a previous required job failed"
|
|
18
|
+
- "Skipping this step because a previous step failed or the workflow was cancelled"
|
|
19
|
+
- "This job was cancelled because another job in the workflow failed"
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions matrix strategy has `fail-fast: true` as the default. When any single
|
|
22
|
+
matrix job fails, GitHub immediately cancels all remaining in-progress and queued
|
|
23
|
+
matrix jobs from the same workflow run.
|
|
24
|
+
|
|
25
|
+
This means:
|
|
26
|
+
- You only see the failure from the first (or earliest) failing matrix combination
|
|
27
|
+
- All other combinations — whether they would pass or fail — are cancelled immediately
|
|
28
|
+
- The workflow summary shows cancelled jobs with no diagnostic information
|
|
29
|
+
- Developers may spend time fixing the first failure only to discover a second unrelated
|
|
30
|
+
failure in a different matrix combination afterward
|
|
31
|
+
|
|
32
|
+
Many developers set up matrix builds specifically to validate across multiple
|
|
33
|
+
OS/language version combinations and expect every combination to run to completion.
|
|
34
|
+
fix: |
|
|
35
|
+
Explicitly set `fail-fast: false` in your matrix strategy to allow all matrix jobs to
|
|
36
|
+
run to completion regardless of individual failures. This provides a complete picture
|
|
37
|
+
of which matrix combinations are broken.
|
|
38
|
+
fix_code:
|
|
39
|
+
- language: yaml
|
|
40
|
+
label: "Set fail-fast: false to run all matrix combinations to completion"
|
|
41
|
+
code: |
|
|
42
|
+
jobs:
|
|
43
|
+
test:
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false # default is true — set false for full matrix visibility
|
|
46
|
+
matrix:
|
|
47
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
48
|
+
node: [18, 20, 22]
|
|
49
|
+
runs-on: ${{ matrix.os }}
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- uses: actions/setup-node@v4
|
|
53
|
+
with:
|
|
54
|
+
node-version: ${{ matrix.node }}
|
|
55
|
+
- run: npm test
|
|
56
|
+
prevention:
|
|
57
|
+
- "Always explicitly set fail-fast: false when you want all matrix combinations to run — for example when building cross-platform or cross-version compatibility matrices"
|
|
58
|
+
- "The default fail-fast: true is intentional for workflows where you only care about any single failure (saves CI minutes), but must be changed when you need full failure visibility"
|
|
59
|
+
- "If matrix jobs are silently cancelled and show no error output, check whether fail-fast: true (or its absence — the default) is the cause"
|
|
60
|
+
docs:
|
|
61
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast"
|
|
62
|
+
label: "jobs.<job_id>.strategy.fail-fast documentation"
|
|
63
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix"
|
|
64
|
+
label: "Matrix strategy documentation"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: silent-failures-084
|
|
2
|
+
title: "github.sha on pull_request event is the ephemeral merge commit SHA, not the PR head commit"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- github-sha
|
|
7
|
+
- pull_request
|
|
8
|
+
- merge-commit
|
|
9
|
+
- docker
|
|
10
|
+
- tagging
|
|
11
|
+
- status-checks
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: '\$\{\{\s*github\.sha\s*\}\}'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'github\.sha'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Docker image tagged with SHA not found in git history"
|
|
19
|
+
- "Deployment status does not appear on the PR commit"
|
|
20
|
+
- "Commit status check not visible on pull request"
|
|
21
|
+
root_cause: |
|
|
22
|
+
On `pull_request` events, `github.sha` is NOT the PR branch's head commit SHA. It is
|
|
23
|
+
the SHA of a temporary merge commit that GitHub creates automatically to simulate
|
|
24
|
+
"what would happen if this PR were merged right now." This ephemeral merge commit:
|
|
25
|
+
|
|
26
|
+
- Does NOT appear in the repository's git history
|
|
27
|
+
- Changes every time the base branch advances, even with no new PR commits
|
|
28
|
+
- Is not accessible via normal git operations outside the workflow run
|
|
29
|
+
|
|
30
|
+
Consequences developers hit in the wild:
|
|
31
|
+
- Docker images tagged with `github.sha` have tags that don't correspond to any real
|
|
32
|
+
commit, making rollbacks and tracing impossible
|
|
33
|
+
- Commit status checks or deployment markers posted to `github.sha` don't appear on
|
|
34
|
+
any commit in the PR and are effectively invisible to reviewers
|
|
35
|
+
- Scripts that fetch the commit from the API using github.sha return 404
|
|
36
|
+
|
|
37
|
+
The actual PR head commit SHA is available via `github.event.pull_request.head.sha`.
|
|
38
|
+
fix: |
|
|
39
|
+
Use `github.event.pull_request.head.sha` to get the actual PR head commit on
|
|
40
|
+
pull_request events. For workflows triggered by both push and pull_request, use a
|
|
41
|
+
conditional to select the correct SHA per event type.
|
|
42
|
+
fix_code:
|
|
43
|
+
- language: yaml
|
|
44
|
+
label: "Get the correct SHA for both push and pull_request events"
|
|
45
|
+
code: |
|
|
46
|
+
jobs:
|
|
47
|
+
build:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
|
|
52
|
+
- name: Resolve commit SHA
|
|
53
|
+
id: sha
|
|
54
|
+
run: |
|
|
55
|
+
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
56
|
+
echo "value=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
|
|
57
|
+
else
|
|
58
|
+
echo "value=${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Tag Docker image with correct SHA
|
|
62
|
+
run: |
|
|
63
|
+
docker build -t myapp:${{ steps.sha.outputs.value }} .
|
|
64
|
+
docker push myapp:${{ steps.sha.outputs.value }}
|
|
65
|
+
- language: yaml
|
|
66
|
+
label: "Post commit status to the correct PR head SHA"
|
|
67
|
+
code: |
|
|
68
|
+
on:
|
|
69
|
+
pull_request:
|
|
70
|
+
|
|
71
|
+
jobs:
|
|
72
|
+
status:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- name: Post status to actual PR commit
|
|
76
|
+
uses: actions/github-script@v7
|
|
77
|
+
with:
|
|
78
|
+
script: |
|
|
79
|
+
// Use PR head SHA, not github.sha (merge commit)
|
|
80
|
+
await github.rest.repos.createCommitStatus({
|
|
81
|
+
owner: context.repo.owner,
|
|
82
|
+
repo: context.repo.repo,
|
|
83
|
+
sha: context.payload.pull_request.head.sha,
|
|
84
|
+
state: 'success',
|
|
85
|
+
description: 'Build passed',
|
|
86
|
+
context: 'ci/build'
|
|
87
|
+
});
|
|
88
|
+
prevention:
|
|
89
|
+
- "Never tag Docker images or create commit statuses using github.sha on pull_request events — use github.event.pull_request.head.sha for the real PR head"
|
|
90
|
+
- "If deployment statuses or commit checks are missing from PR commits, verify you are not posting them to github.sha (the merge commit)"
|
|
91
|
+
- "The merge commit SHA changes every time the base branch gets new commits, even without new PR activity — this makes github.sha unstable for artifact tagging on PRs"
|
|
92
|
+
docs:
|
|
93
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request"
|
|
94
|
+
label: "pull_request event — note on github.sha merge commit"
|
|
95
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context"
|
|
96
|
+
label: "GitHub context — sha property"
|
|
@@ -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,87 @@
|
|
|
1
|
+
id: yaml-syntax-056
|
|
2
|
+
title: "env context unavailable inside with: inputs of uses: steps"
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- env-context
|
|
7
|
+
- with-inputs
|
|
8
|
+
- uses
|
|
9
|
+
- expression-context
|
|
10
|
+
- action-inputs
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Unrecognized named-value: 'env'"
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'Invalid workflow file.*Unrecognized named-value'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "Unrecognized named-value: 'env'. Located at position 1 within expression: env.MY_VAR"
|
|
18
|
+
- "Invalid workflow file: .github/workflows/ci.yml (Line 14, Col 15): Unrecognized named-value: 'env'"
|
|
19
|
+
- "The workflow is not valid. .github/workflows/build.yml: Unrecognized named-value: 'env'"
|
|
20
|
+
root_cause: |
|
|
21
|
+
The `env` context is NOT available inside the `with:` input block of a `uses:` step.
|
|
22
|
+
GitHub Actions evaluates `uses:` step inputs using a restricted set of contexts, and
|
|
23
|
+
`env` is explicitly excluded because env var values may depend on previous step
|
|
24
|
+
outputs (which are also unavailable at `with:` evaluation time).
|
|
25
|
+
|
|
26
|
+
Contexts available in `with:` inputs: `github`, `needs`, `strategy`, `matrix`,
|
|
27
|
+
`secrets`, `inputs`, `vars`, and `steps` (but only from previously completed steps).
|
|
28
|
+
The `env` context, `runner` context, and job-level env vars are all unavailable.
|
|
29
|
+
|
|
30
|
+
This catches developers who define env vars at the workflow or job level and then try
|
|
31
|
+
to pass them as action inputs, which fails with "Unrecognized named-value: 'env'".
|
|
32
|
+
fix: |
|
|
33
|
+
Three options:
|
|
34
|
+
1. Reference the value directly (hardcoded or via a different supported context)
|
|
35
|
+
2. Use `vars` context (repository/org variables) — available in `with:` blocks
|
|
36
|
+
3. Capture the env value in a prior step's GITHUB_OUTPUT and reference it via
|
|
37
|
+
`steps.<id>.outputs.<name>` in the subsequent `uses:` step
|
|
38
|
+
fix_code:
|
|
39
|
+
- language: yaml
|
|
40
|
+
label: "Wrong: env context in with: inputs (causes parse error)"
|
|
41
|
+
code: |
|
|
42
|
+
env:
|
|
43
|
+
API_URL: https://api.example.com
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
deploy:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: my-org/deploy-action@v1
|
|
50
|
+
with:
|
|
51
|
+
url: ${{ env.API_URL }} # ERROR: Unrecognized named-value: 'env'
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: "Fix option A: use vars context (repository variable)"
|
|
54
|
+
code: |
|
|
55
|
+
# Set API_URL as a repository variable in Settings > Secrets and variables > Variables
|
|
56
|
+
jobs:
|
|
57
|
+
deploy:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: my-org/deploy-action@v1
|
|
61
|
+
with:
|
|
62
|
+
url: ${{ vars.API_URL }} # vars context IS available in with:
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "Fix option B: capture in prior step output, reference via steps context"
|
|
65
|
+
code: |
|
|
66
|
+
env:
|
|
67
|
+
API_URL: https://api.example.com
|
|
68
|
+
|
|
69
|
+
jobs:
|
|
70
|
+
deploy:
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- id: config
|
|
74
|
+
run: echo "api_url=$API_URL" >> $GITHUB_OUTPUT
|
|
75
|
+
|
|
76
|
+
- uses: my-org/deploy-action@v1
|
|
77
|
+
with:
|
|
78
|
+
url: ${{ steps.config.outputs.api_url }} # steps context IS available
|
|
79
|
+
prevention:
|
|
80
|
+
- "In with: inputs, only use: github, needs, strategy, matrix, secrets, inputs, vars, and steps (from prior completed steps) — never env or runner"
|
|
81
|
+
- "For static configuration values, prefer repository variables (vars context) over env — vars are available everywhere env is not"
|
|
82
|
+
- "If you need a dynamically computed value (from a script or env var) in a with: block, capture it to GITHUB_OUTPUT in a prior step and reference via steps.<id>.outputs"
|
|
83
|
+
docs:
|
|
84
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#context-availability"
|
|
85
|
+
label: "Context availability table — which contexts are valid per syntax location"
|
|
86
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepswith"
|
|
87
|
+
label: "jobs.<job_id>.steps[*].with syntax documentation"
|
package/package.json
CHANGED