@htekdev/actions-debugger 1.0.81 → 1.0.82
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/workflow-level-vs-job-level-concurrency-scope.yml +92 -0
- package/errors/known-unsolved/org-required-workflow-no-per-repo-override.yml +88 -0
- package/errors/runner-environment/larger-runner-labels-require-paid-plan.yml +73 -0
- package/errors/runner-environment/macos-bash-32-no-bash4-features.yml +110 -0
- package/errors/runner-environment/pip-externally-managed-environment-pep668.yml +83 -0
- package/errors/runner-environment/ubuntu-24-ruby-not-preinstalled.yml +74 -0
- package/errors/runner-environment/windows-shell-powershell-is-ps5-not-ps7.yml +85 -0
- package/errors/silent-failures/github-event-inputs-undefined-in-workflow-call.yml +102 -0
- package/errors/silent-failures/job-outputs-block-missing-needs-always-empty.yml +85 -0
- package/errors/triggers/push-branches-filter-bypassed-by-tag-push.yml +83 -0
- package/errors/triggers/release-created-fires-on-draft.yml +75 -0
- package/errors/yaml-syntax/secrets-in-workflow-level-env-block-rejected.yml +91 -0
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
id: concurrency-timing-041
|
|
2
|
+
title: "Workflow-level concurrency cancels entire workflow — job-level concurrency only gates that specific job"
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- concurrency
|
|
7
|
+
- cancel-in-progress
|
|
8
|
+
- workflow-level
|
|
9
|
+
- job-level
|
|
10
|
+
- scope
|
|
11
|
+
- deployment
|
|
12
|
+
- test-cancellation
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: '^concurrency:\s*$'
|
|
15
|
+
flags: 'm'
|
|
16
|
+
- regex: 'jobs\.[a-zA-Z_][a-zA-Z0-9_-]*:\s*\n(\s+.*\n)*\s+concurrency:'
|
|
17
|
+
flags: 'm'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Run was cancelled. (workflow-level concurrency cancelled entire run including unrelated jobs)"
|
|
20
|
+
- "Skipping job deploy because it is not needed (cancelled by workflow-level concurrency group)"
|
|
21
|
+
root_cause: |
|
|
22
|
+
The concurrency: key in GitHub Actions behaves fundamentally differently depending on
|
|
23
|
+
where it is placed in the workflow file:
|
|
24
|
+
|
|
25
|
+
WORKFLOW-LEVEL (top-level key, outside jobs:):
|
|
26
|
+
Cancels or queues the ENTIRE workflow run. When a new run starts for the same concurrency
|
|
27
|
+
group, all pending or in-progress jobs in the workflow are affected — including build,
|
|
28
|
+
test, lint, and deploy jobs. The full workflow run is treated as the atomic unit.
|
|
29
|
+
|
|
30
|
+
JOB-LEVEL (inside jobs.<job_id>: as a sibling of runs-on: and steps:):
|
|
31
|
+
Only gates THAT SPECIFIC JOB. Other jobs in the same workflow run execute concurrently
|
|
32
|
+
without restriction. Serialization or cancellation applies only to the job that declares
|
|
33
|
+
the concurrency block.
|
|
34
|
+
|
|
35
|
+
Common mistake #1: developer adds workflow-level concurrency intending to prevent
|
|
36
|
+
duplicate deployments, but accidentally causes active test/build jobs to be cancelled
|
|
37
|
+
on every new push — wasting compute and breaking CI feedback.
|
|
38
|
+
|
|
39
|
+
Common mistake #2: developer adds job-level concurrency expecting the whole workflow
|
|
40
|
+
to be serialized, but parallel test runs from multiple pushes execute simultaneously.
|
|
41
|
+
fix: |
|
|
42
|
+
Use job-level concurrency on only the deploy job when you want to serialize
|
|
43
|
+
deployments without interrupting parallel test runs. Use workflow-level concurrency
|
|
44
|
+
only when you intend for the entire workflow to serialize or cancel on new push.
|
|
45
|
+
fix_code:
|
|
46
|
+
- language: yaml
|
|
47
|
+
label: "Correct: job-level concurrency on deploy only — tests run freely"
|
|
48
|
+
code: |
|
|
49
|
+
jobs:
|
|
50
|
+
test:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
# No concurrency — multiple test runs execute in parallel across pushes
|
|
53
|
+
steps:
|
|
54
|
+
- run: npm test
|
|
55
|
+
|
|
56
|
+
deploy:
|
|
57
|
+
needs: test
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
concurrency:
|
|
60
|
+
group: deploy-${{ github.ref_name }}
|
|
61
|
+
cancel-in-progress: false # Queue deploys, never cancel in-flight
|
|
62
|
+
steps:
|
|
63
|
+
- run: ./deploy.sh
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: "Caution: workflow-level concurrency cancels test and build jobs too"
|
|
66
|
+
code: |
|
|
67
|
+
# This cancels the ENTIRE workflow on new push — tests, build, and deploy all stop
|
|
68
|
+
concurrency:
|
|
69
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
70
|
+
cancel-in-progress: true
|
|
71
|
+
|
|
72
|
+
jobs:
|
|
73
|
+
test:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- run: npm test # Also cancelled when a new push arrives
|
|
77
|
+
|
|
78
|
+
deploy:
|
|
79
|
+
needs: test
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- run: ./deploy.sh
|
|
83
|
+
prevention:
|
|
84
|
+
- "For deploy-only serialization, use job-level concurrency on only the deploy job"
|
|
85
|
+
- "Avoid cancel-in-progress: true at workflow level for deployment workflows — an interrupted deploy may leave infrastructure in an inconsistent state"
|
|
86
|
+
- "Use cancel-in-progress: ${{ github.event_name == 'pull_request' }} to cancel PR runs but preserve default-branch deployments"
|
|
87
|
+
- "Verify scope: if concurrency: is indented under a specific jobs.<id>: block it is job-level; if it is at the same indentation as jobs: it is workflow-level"
|
|
88
|
+
docs:
|
|
89
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-concurrency"
|
|
90
|
+
label: "GitHub Docs: Using concurrency"
|
|
91
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency"
|
|
92
|
+
label: "GitHub Docs: concurrency syntax reference"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
id: known-unsolved-048
|
|
2
|
+
title: 'Organization required workflows block all repo PRs with no per-repo override'
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- required-workflows
|
|
7
|
+
- organization
|
|
8
|
+
- branch-protection
|
|
9
|
+
- enterprise
|
|
10
|
+
- admin
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Required status checks have not passed for this branch'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'required workflows? are not complete'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- 'Required status checks have not passed for this branch'
|
|
18
|
+
- 'The following required workflows are not complete: .github/workflows/required-check.yml'
|
|
19
|
+
- 'Required workflow failed: org-name/.github/.github/workflows/security-scan.yml'
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Enterprise Cloud allows org admins to configure "Required workflows"
|
|
22
|
+
under Org Settings → Actions → Required workflows. These workflows run on
|
|
23
|
+
every push and pull_request event across every repository in the organization
|
|
24
|
+
and are automatically added as required status checks on all branches.
|
|
25
|
+
|
|
26
|
+
When a required workflow fails — due to a misconfiguration, missing secret,
|
|
27
|
+
incompatibility with a specific repository's structure, or a transient error
|
|
28
|
+
— all open PRs in every affected repository are blocked from merging. There
|
|
29
|
+
is no mechanism for repo admins or developers to bypass or exempt their
|
|
30
|
+
repository from the org-level required workflow. Only the org admin can fix
|
|
31
|
+
the required workflow or remove the requirement.
|
|
32
|
+
|
|
33
|
+
This creates an org-wide single point of failure: a bug in the centralized
|
|
34
|
+
required workflow instantly blocks all engineering teams from shipping.
|
|
35
|
+
fix: |
|
|
36
|
+
There is no per-repository exemption for org required workflows. Mitigation
|
|
37
|
+
strategies for org admins:
|
|
38
|
+
|
|
39
|
+
1. Use `if:` conditions in the required workflow to skip gracefully for
|
|
40
|
+
incompatible repositories instead of failing.
|
|
41
|
+
2. Set `continue-on-error: true` on non-critical jobs within the required
|
|
42
|
+
workflow so that optional checks do not block merges.
|
|
43
|
+
3. Maintain a staging version of the required workflow tested on a canary
|
|
44
|
+
repo before rolling changes org-wide.
|
|
45
|
+
4. Set up alerting on required workflow failure rates to catch regressions
|
|
46
|
+
before they block the whole org.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: 'Use if: conditions to gracefully skip incompatible repos'
|
|
50
|
+
code: |
|
|
51
|
+
# In org's .github repo: .github/workflows/required-check.yml
|
|
52
|
+
name: Required Security Scan
|
|
53
|
+
on: [push, pull_request]
|
|
54
|
+
jobs:
|
|
55
|
+
security-scan:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
# Skip repos that are explicitly opted out
|
|
58
|
+
if: >-
|
|
59
|
+
!contains(
|
|
60
|
+
fromJSON('["org/legacy-repo", "org/infra-repo"]'),
|
|
61
|
+
github.repository
|
|
62
|
+
)
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- name: Run scan
|
|
66
|
+
run: ./scripts/security-scan.sh
|
|
67
|
+
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: 'Use continue-on-error for non-blocking checks'
|
|
70
|
+
code: |
|
|
71
|
+
jobs:
|
|
72
|
+
lint:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
continue-on-error: true # Won't block the required status check
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- run: npm run lint
|
|
78
|
+
prevention:
|
|
79
|
+
- 'Test required workflows in a canary repository before enabling org-wide'
|
|
80
|
+
- 'Use `if:` guards to skip repos that cannot satisfy the required checks'
|
|
81
|
+
- 'Monitor required workflow failure rates with org-level Actions dashboards'
|
|
82
|
+
- 'Prefer `continue-on-error: true` for advisory checks that should not hard-block PRs'
|
|
83
|
+
- 'Keep required workflows minimal — only truly mandatory gates belong here'
|
|
84
|
+
docs:
|
|
85
|
+
- url: 'https://docs.github.com/en/actions/administering-github-actions/required-workflows'
|
|
86
|
+
label: 'Required workflows — GitHub Docs'
|
|
87
|
+
- url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-status-checks-before-merging'
|
|
88
|
+
label: 'Required status checks — GitHub Docs'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
id: runner-environment-144
|
|
2
|
+
title: '`runs-on` larger hosted runner labels require GitHub Teams or Enterprise plan'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- runs-on
|
|
7
|
+
- larger-runners
|
|
8
|
+
- billing
|
|
9
|
+
- teams
|
|
10
|
+
- enterprise
|
|
11
|
+
- ubuntu-latest-4-cores
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'No runner matching the required labels was found:\s+ubuntu-latest-[0-9]+-cores'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'No runner matching the required labels was found:\s+windows-latest-[0-9]+-cores'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'No runner matching the required labels was found:\s+macos-latest-[0-9]+-cores'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- 'No runner matching the required labels was found: ubuntu-latest-4-cores'
|
|
21
|
+
- 'No runner matching the required labels was found: ubuntu-latest-8-cores'
|
|
22
|
+
- 'No runner matching the required labels was found: windows-latest-8-cores'
|
|
23
|
+
- 'No runner matching the required labels was found: macos-latest-xlarge'
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub offers larger hosted runners (4-core, 8-core, 16-core, 64-core) via
|
|
26
|
+
labels like `ubuntu-latest-4-cores`, `ubuntu-latest-8-cores`,
|
|
27
|
+
`ubuntu-22.04-8-cores`, `windows-latest-8-cores`, and `macos-latest-xlarge`.
|
|
28
|
+
These runners are only available on GitHub Teams and GitHub Enterprise Cloud
|
|
29
|
+
plans. On GitHub Free and GitHub Pro plans these labels are not provisioned,
|
|
30
|
+
so the queued job sits waiting until it times out or immediately fails with
|
|
31
|
+
"No runner matching the required labels was found."
|
|
32
|
+
|
|
33
|
+
GitHub does not display a clear billing-tier explanation in the error message,
|
|
34
|
+
leaving developers to guess whether the runner label is misspelled or simply
|
|
35
|
+
unavailable on their plan.
|
|
36
|
+
fix: |
|
|
37
|
+
Switch to the standard `ubuntu-latest` (2-core) runner for free/pro accounts,
|
|
38
|
+
upgrade to GitHub Teams/Enterprise to unlock larger runners, or use
|
|
39
|
+
self-hosted runners with more CPU/RAM as an alternative.
|
|
40
|
+
fix_code:
|
|
41
|
+
- language: yaml
|
|
42
|
+
label: 'Use standard runner on free/pro plans'
|
|
43
|
+
code: |
|
|
44
|
+
jobs:
|
|
45
|
+
build:
|
|
46
|
+
# ubuntu-latest is a 2-core runner available on all GitHub plans
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
- run: make build
|
|
51
|
+
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Conditionally use larger runner for orgs on Teams/Enterprise'
|
|
54
|
+
code: |
|
|
55
|
+
jobs:
|
|
56
|
+
build:
|
|
57
|
+
# Use larger runner for org workflows, fall back for personal repos
|
|
58
|
+
runs-on: >-
|
|
59
|
+
${{ startsWith(github.repository, 'my-org/') &&
|
|
60
|
+
'ubuntu-latest-8-cores' || 'ubuntu-latest' }}
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- run: make build
|
|
64
|
+
prevention:
|
|
65
|
+
- 'Verify your GitHub plan tier before using larger runner labels — check under Billing & plans → GitHub Actions'
|
|
66
|
+
- 'Use self-hosted runners with additional CPU as a free alternative for resource-intensive builds'
|
|
67
|
+
- 'Document which runner labels require a paid plan in your repository CONTRIBUTING guide'
|
|
68
|
+
- 'Use `ubuntu-latest` (2-core) for most workflows; only reach for larger runners when build profiling shows a genuine bottleneck'
|
|
69
|
+
docs:
|
|
70
|
+
- url: 'https://docs.github.com/en/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners'
|
|
71
|
+
label: 'About larger runners — GitHub Docs'
|
|
72
|
+
- url: 'https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions'
|
|
73
|
+
label: 'About billing for GitHub Actions — GitHub Docs'
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
id: runner-environment-148
|
|
2
|
+
title: "macOS runners ship with bash 3.2 — bash 4.x/5.x features unavailable"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- macos
|
|
7
|
+
- bash
|
|
8
|
+
- bash-version
|
|
9
|
+
- associative-arrays
|
|
10
|
+
- shell-scripting
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'declare.*-A.*invalid option'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'mapfile.*command not found'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'readarray.*command not found'
|
|
18
|
+
flags: i
|
|
19
|
+
- regex: 'syntax error near unexpected token.*\|\&'
|
|
20
|
+
flags: i
|
|
21
|
+
|
|
22
|
+
error_messages:
|
|
23
|
+
- "/bin/bash: line N: declare: -A: invalid option"
|
|
24
|
+
- "declare: usage: declare [-afFirtx] [-p] [name[=value] ...]"
|
|
25
|
+
- "/bin/bash: mapfile: command not found"
|
|
26
|
+
- "/bin/bash: readarray: command not found"
|
|
27
|
+
|
|
28
|
+
root_cause: |
|
|
29
|
+
macOS ships with GNU Bash 3.2.57 at /bin/bash due to Apple's decision not to
|
|
30
|
+
include GPL v3 software in the base OS. Bash 4.0 (released 2009) and all later
|
|
31
|
+
versions are GPL v3. macOS has not updated the system bash beyond 3.2.x for
|
|
32
|
+
over 15 years as a result.
|
|
33
|
+
|
|
34
|
+
GitHub-hosted macOS runners (macos-13, macos-14, macos-15, macos-latest) all use
|
|
35
|
+
/bin/bash as the default shell for `shell: bash` run steps. This means all bash
|
|
36
|
+
scripts on macOS runners are subject to bash 3.2 limitations:
|
|
37
|
+
|
|
38
|
+
- Associative arrays: `declare -A map` — UNAVAILABLE (added in bash 4.0)
|
|
39
|
+
- mapfile / readarray built-ins — UNAVAILABLE (added in bash 4.0)
|
|
40
|
+
- `|&` (pipe stdout and stderr): syntax error (added in bash 4.0)
|
|
41
|
+
- `**` globbing for recursive matching — UNAVAILABLE (added in bash 4.0, requires shopt -s globstar)
|
|
42
|
+
- Case-modifying parameter expansion: `${var^^}`, `${var,,}` — UNAVAILABLE (bash 4.0)
|
|
43
|
+
|
|
44
|
+
Workflows that run on ubuntu runners may work fine because ubuntu ships bash 5.x,
|
|
45
|
+
then silently fail on macOS runners at the same step due to the bash version
|
|
46
|
+
difference.
|
|
47
|
+
|
|
48
|
+
fix: |
|
|
49
|
+
Install modern bash via Homebrew and either update the PATH or use the full path
|
|
50
|
+
to the installed bash in the shell setting or script shebang.
|
|
51
|
+
|
|
52
|
+
For cross-platform workflows, avoid bash 4.x/5.x-only features or add an explicit
|
|
53
|
+
setup step for macOS.
|
|
54
|
+
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Install modern bash via Homebrew (macOS)"
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
build:
|
|
61
|
+
runs-on: macos-latest
|
|
62
|
+
steps:
|
|
63
|
+
- name: Install modern bash
|
|
64
|
+
run: brew install bash
|
|
65
|
+
|
|
66
|
+
- name: Run script with bash 5
|
|
67
|
+
run: /opt/homebrew/bin/bash my-script.sh
|
|
68
|
+
# Intel macOS: /usr/local/bin/bash
|
|
69
|
+
# Apple Silicon (macos-14+): /opt/homebrew/bin/bash
|
|
70
|
+
|
|
71
|
+
- language: yaml
|
|
72
|
+
label: "Set shell to modern bash for all steps"
|
|
73
|
+
code: |
|
|
74
|
+
jobs:
|
|
75
|
+
build:
|
|
76
|
+
runs-on: macos-latest
|
|
77
|
+
steps:
|
|
78
|
+
- name: Install modern bash
|
|
79
|
+
run: brew install bash
|
|
80
|
+
|
|
81
|
+
- name: Script using bash 4+ features
|
|
82
|
+
shell: /opt/homebrew/bin/bash {0}
|
|
83
|
+
run: |
|
|
84
|
+
declare -A mymap
|
|
85
|
+
mymap[key]="value"
|
|
86
|
+
echo "${mymap[key]}"
|
|
87
|
+
|
|
88
|
+
- language: yaml
|
|
89
|
+
label: "Cross-platform workaround — avoid bash 4+ features"
|
|
90
|
+
code: |
|
|
91
|
+
- run: |
|
|
92
|
+
# Use python or node for associative arrays cross-platform
|
|
93
|
+
python3 -c "
|
|
94
|
+
mymap = {'key': 'value'}
|
|
95
|
+
print(mymap['key'])
|
|
96
|
+
"
|
|
97
|
+
|
|
98
|
+
prevention:
|
|
99
|
+
- Test macOS workflows explicitly — a step that passes on ubuntu may fail on macos.
|
|
100
|
+
- Avoid bash 4.x/5.x-only features (declare -A, mapfile, readarray) in scripts intended to run on macOS.
|
|
101
|
+
- Add `brew install bash` as the first step in any macOS job that uses modern bash syntax.
|
|
102
|
+
- Use `bash --version` in a debug step to confirm which bash is being used.
|
|
103
|
+
|
|
104
|
+
docs:
|
|
105
|
+
- url: https://www.gnu.org/software/bash/manual/bash.html#What-is-Bash_003f
|
|
106
|
+
label: "GNU Bash — version history and features"
|
|
107
|
+
- url: https://brew.sh
|
|
108
|
+
label: "Homebrew — install modern bash on macOS"
|
|
109
|
+
- url: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md
|
|
110
|
+
label: "macos-15 runner image — pre-installed software"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
id: runner-environment-145
|
|
2
|
+
title: "ubuntu-22/24 pip install fails with 'externally-managed-environment' (PEP 668)"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu
|
|
7
|
+
- python
|
|
8
|
+
- pip
|
|
9
|
+
- pep-668
|
|
10
|
+
- packages
|
|
11
|
+
- setup-python
|
|
12
|
+
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'externally.managed.environment'
|
|
15
|
+
flags: i
|
|
16
|
+
- regex: 'This environment is externally managed'
|
|
17
|
+
flags: i
|
|
18
|
+
|
|
19
|
+
error_messages:
|
|
20
|
+
- "error: externally-managed-environment"
|
|
21
|
+
- "× This environment is externally managed"
|
|
22
|
+
- "╰─> To install Python packages system-wide, try apt install"
|
|
23
|
+
|
|
24
|
+
root_cause: |
|
|
25
|
+
PEP 668, adopted in Python 3.11+ and backported by distros like Ubuntu 22.04 and
|
|
26
|
+
24.04, prevents pip from installing packages into the system Python environment.
|
|
27
|
+
Ubuntu 22.04 ships Python 3.10 but enforces PEP 668 in the system pip. Ubuntu 24.04
|
|
28
|
+
ships Python 3.12 with full PEP 668 enforcement.
|
|
29
|
+
|
|
30
|
+
GitHub Actions runners expose the system Python when no setup-python step is used.
|
|
31
|
+
System pip blocks global installs and raises this error to prevent breaking
|
|
32
|
+
OS-managed packages that depend on system Python libraries.
|
|
33
|
+
|
|
34
|
+
Workflows that previously ran `pip install <pkg>` or `pip3 install <pkg>` directly
|
|
35
|
+
on ubuntu-20.04 succeed silently, but fail on ubuntu-22.04 and ubuntu-24.04 because
|
|
36
|
+
those images enforce the system-managed environment restriction.
|
|
37
|
+
|
|
38
|
+
fix: |
|
|
39
|
+
Use one of these approaches:
|
|
40
|
+
|
|
41
|
+
1. Use actions/setup-python (recommended) — configures an isolated Python
|
|
42
|
+
environment that allows pip installs freely.
|
|
43
|
+
|
|
44
|
+
2. Install with --break-system-packages flag — allows global install but may
|
|
45
|
+
conflict with apt-managed packages. Use only for CI where isolation is
|
|
46
|
+
acceptable.
|
|
47
|
+
|
|
48
|
+
3. Create a virtual environment inline with python -m venv.
|
|
49
|
+
|
|
50
|
+
fix_code:
|
|
51
|
+
- language: yaml
|
|
52
|
+
label: "Use actions/setup-python (recommended)"
|
|
53
|
+
code: |
|
|
54
|
+
- uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: '3.12'
|
|
57
|
+
- run: pip install my-package # installs into the setup-python venv, no error
|
|
58
|
+
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: "Virtual environment workaround"
|
|
61
|
+
code: |
|
|
62
|
+
- run: |
|
|
63
|
+
python3 -m venv .venv
|
|
64
|
+
source .venv/bin/activate
|
|
65
|
+
pip install my-package
|
|
66
|
+
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "Break system packages flag (use with caution)"
|
|
69
|
+
code: |
|
|
70
|
+
- run: pip install --break-system-packages my-package
|
|
71
|
+
|
|
72
|
+
prevention:
|
|
73
|
+
- Always use actions/setup-python before any pip install in workflows targeting ubuntu-22.04 or ubuntu-24.04.
|
|
74
|
+
- Pin ubuntu version explicitly — upgrading from ubuntu-20.04 to ubuntu-latest silently adopts PEP 668 enforcement.
|
|
75
|
+
- Prefer virtual environments over global installs in CI for reproducibility.
|
|
76
|
+
|
|
77
|
+
docs:
|
|
78
|
+
- url: https://peps.python.org/pep-0668/
|
|
79
|
+
label: "PEP 668 — Marking Python base environments as externally managed"
|
|
80
|
+
- url: https://github.com/actions/setup-python
|
|
81
|
+
label: "actions/setup-python — GitHub Actions"
|
|
82
|
+
- url: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
|
|
83
|
+
label: "ubuntu-24.04 runner image — pre-installed software"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
id: runner-environment-146
|
|
2
|
+
title: "ubuntu-24.04 runner — Ruby and gem not pre-installed"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu-24
|
|
7
|
+
- ruby
|
|
8
|
+
- gem
|
|
9
|
+
- tools-missing
|
|
10
|
+
- migration
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'ruby\s*:\s*command not found'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'gem\s*:\s*command not found'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'No such file or directory.*ruby'
|
|
18
|
+
flags: i
|
|
19
|
+
|
|
20
|
+
error_messages:
|
|
21
|
+
- "/usr/bin/bash: ruby: command not found"
|
|
22
|
+
- "/usr/bin/bash: gem: command not found"
|
|
23
|
+
- "Error: ruby not found"
|
|
24
|
+
|
|
25
|
+
root_cause: |
|
|
26
|
+
GitHub's ubuntu-24.04 runner image does not include Ruby in its pre-installed
|
|
27
|
+
software list. Unlike ubuntu-22.04 (which ships Ruby 3.0.x from the Ubuntu 22
|
|
28
|
+
package repository), ubuntu-24.04 dropped Ruby as a default pre-installed tool.
|
|
29
|
+
|
|
30
|
+
Workflows that rely on `ruby`, `gem`, `bundle`, or `rake` being available without
|
|
31
|
+
an explicit setup step will fail with "command not found" when the runner image is
|
|
32
|
+
ubuntu-24.04 or when ubuntu-latest resolves to ubuntu-24.04.
|
|
33
|
+
|
|
34
|
+
This commonly affects:
|
|
35
|
+
- Workflows that run `gem install` or `bundle install` directly
|
|
36
|
+
- Custom scripts that call `ruby my-script.rb`
|
|
37
|
+
- Jekyll static site workflows without the setup-ruby action
|
|
38
|
+
- Workflows that inherited ubuntu-22.04 behavior when pinned to ubuntu-latest
|
|
39
|
+
|
|
40
|
+
fix: |
|
|
41
|
+
Use the ruby/setup-ruby action to install Ruby on any runner. This is the
|
|
42
|
+
recommended approach for cross-platform consistency and version pinning.
|
|
43
|
+
|
|
44
|
+
Alternatively, install Ruby via apt-get if a specific package-managed version
|
|
45
|
+
is acceptable, but ruby/setup-ruby is strongly preferred for version control.
|
|
46
|
+
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: "Use ruby/setup-ruby action (recommended)"
|
|
50
|
+
code: |
|
|
51
|
+
- uses: ruby/setup-ruby@v1
|
|
52
|
+
with:
|
|
53
|
+
ruby-version: '3.3'
|
|
54
|
+
bundler-cache: true # runs bundle install and caches gems
|
|
55
|
+
|
|
56
|
+
- run: ruby my-script.rb
|
|
57
|
+
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: "Install via apt-get (version uncontrolled)"
|
|
60
|
+
code: |
|
|
61
|
+
- run: sudo apt-get install -y ruby ruby-dev
|
|
62
|
+
|
|
63
|
+
prevention:
|
|
64
|
+
- Always use ruby/setup-ruby instead of relying on pre-installed Ruby.
|
|
65
|
+
- Test workflows against ubuntu-24.04 explicitly before switching ubuntu-latest.
|
|
66
|
+
- Check the ubuntu-24.04 pre-installed software list when migrating from ubuntu-22.04.
|
|
67
|
+
|
|
68
|
+
docs:
|
|
69
|
+
- url: https://github.com/ruby/setup-ruby
|
|
70
|
+
label: "ruby/setup-ruby — GitHub Actions"
|
|
71
|
+
- url: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
|
|
72
|
+
label: "ubuntu-24.04 runner image — pre-installed software"
|
|
73
|
+
- url: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
|
|
74
|
+
label: "ubuntu-22.04 runner image — Ruby pre-installed"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
id: runner-environment-147
|
|
2
|
+
title: "Windows runner 'shell: powershell' invokes PowerShell 5 not PowerShell 7"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- windows
|
|
7
|
+
- powershell
|
|
8
|
+
- pwsh
|
|
9
|
+
- shell
|
|
10
|
+
- powershell-version
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'The term .{0,20} is not recognized as the name of a cmdlet'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'Unexpected token.*\?\.'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'parameter cannot be found.*Parallel'
|
|
18
|
+
flags: i
|
|
19
|
+
|
|
20
|
+
error_messages:
|
|
21
|
+
- "The term '??' is not recognized as the name of a cmdlet, function, script file, or operable program."
|
|
22
|
+
- "Unexpected token '?.' in expression or statement."
|
|
23
|
+
- "ForEach-Object: A parameter cannot be found that matches parameter name 'Parallel'."
|
|
24
|
+
|
|
25
|
+
root_cause: |
|
|
26
|
+
On Windows GitHub-hosted runners, explicitly setting `shell: powershell` in a
|
|
27
|
+
workflow step invokes `powershell.exe` — Windows PowerShell 5.1 — NOT the modern
|
|
28
|
+
PowerShell 7.x (pwsh).
|
|
29
|
+
|
|
30
|
+
Windows PowerShell 5.1 is a separate executable that lacks numerous features
|
|
31
|
+
introduced in PowerShell 6.0 (Core) and later:
|
|
32
|
+
- Null-coalescing operator: `$a ?? $b` (requires PS 7.0+)
|
|
33
|
+
- Null-coalescing assignment: `$a ??= 'default'` (requires PS 7.0+)
|
|
34
|
+
- Null-conditional member access: `$obj?.Property` (requires PS 7.0+)
|
|
35
|
+
- Ternary operator: `$x ? $y : $z` (requires PS 7.0+)
|
|
36
|
+
- ForEach-Object -Parallel (requires PS 7.0+)
|
|
37
|
+
- Pipeline chain operators: `&&`, `||` (requires PS 7.0+)
|
|
38
|
+
|
|
39
|
+
The default shell on Windows runners (when no `shell:` key is specified) is `pwsh`
|
|
40
|
+
(PowerShell 7). This means workflows that omit `shell:` work fine, but any step that
|
|
41
|
+
explicitly adds `shell: powershell` regresses to PowerShell 5.1.
|
|
42
|
+
|
|
43
|
+
Developers often set `shell: powershell` when they mean "use PowerShell" without
|
|
44
|
+
knowing the distinction between Windows PowerShell and PowerShell 7.
|
|
45
|
+
|
|
46
|
+
fix: |
|
|
47
|
+
Replace `shell: powershell` with `shell: pwsh` to use PowerShell 7.
|
|
48
|
+
|
|
49
|
+
If the workflow requires PowerShell 5.1 compatibility (rare), audit the script
|
|
50
|
+
and remove PS7-only syntax. However, in almost all cases the intent is PowerShell 7.
|
|
51
|
+
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: "Use pwsh for PowerShell 7 (fix)"
|
|
55
|
+
code: |
|
|
56
|
+
# Before (broken on modern PS syntax):
|
|
57
|
+
# - run: $value = $null ?? "default"
|
|
58
|
+
# shell: powershell
|
|
59
|
+
|
|
60
|
+
# After (correct):
|
|
61
|
+
- run: $value = $null ?? "default"
|
|
62
|
+
shell: pwsh
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "Set pwsh as default shell for all Windows steps"
|
|
65
|
+
code: |
|
|
66
|
+
defaults:
|
|
67
|
+
run:
|
|
68
|
+
shell: pwsh
|
|
69
|
+
|
|
70
|
+
jobs:
|
|
71
|
+
build:
|
|
72
|
+
runs-on: windows-latest
|
|
73
|
+
steps:
|
|
74
|
+
- run: $value = $null ?? "default" # uses pwsh by default
|
|
75
|
+
|
|
76
|
+
prevention:
|
|
77
|
+
- 'Use `shell: pwsh` (not `shell: powershell`) in any step using PowerShell 6+ syntax.'
|
|
78
|
+
- 'Set `defaults.run.shell: pwsh` at workflow level to apply PowerShell 7 globally on Windows.'
|
|
79
|
+
- 'Use actionlint — it does not currently distinguish PS5 vs PS7 but code review should catch explicit `shell: powershell`.'
|
|
80
|
+
|
|
81
|
+
docs:
|
|
82
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-syntax-for-github-actions#jobsjob_idstepsshell'
|
|
83
|
+
label: 'Workflow syntax: shell — GitHub Docs'
|
|
84
|
+
- url: 'https://learn.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell'
|
|
85
|
+
label: 'Differences between Windows PowerShell 5.1 and PowerShell 7 — Microsoft Docs'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
id: silent-failures-077
|
|
2
|
+
title: '`github.event.inputs` is undefined (null) when workflow triggered via `workflow_call`'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- workflow-call
|
|
7
|
+
- workflow-dispatch
|
|
8
|
+
- inputs
|
|
9
|
+
- github-event-inputs
|
|
10
|
+
- reusable-workflow
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'github\.event\.inputs\.'
|
|
13
|
+
flags: 'g'
|
|
14
|
+
- regex: 'on:\s*\n\s+workflow_call:'
|
|
15
|
+
flags: 'im'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "Expression github.event.inputs.environment evaluated to ''"
|
|
18
|
+
- 'github.event.inputs is null'
|
|
19
|
+
- 'unexpected value '''''
|
|
20
|
+
root_cause: |
|
|
21
|
+
When a workflow supports both `workflow_dispatch` and `workflow_call` triggers,
|
|
22
|
+
developers often use `github.event.inputs.param` to read input values.
|
|
23
|
+
This works for `workflow_dispatch` but silently fails for `workflow_call`:
|
|
24
|
+
|
|
25
|
+
- `workflow_dispatch`: inputs are surfaced at `github.event.inputs.*`
|
|
26
|
+
(always strings regardless of declared type)
|
|
27
|
+
- `workflow_call`: inputs are NOT placed in `github.event.inputs`; they are
|
|
28
|
+
only available via the `inputs` context (`inputs.param`)
|
|
29
|
+
|
|
30
|
+
When a reusable workflow is invoked via `workflow_call`, `github.event.inputs`
|
|
31
|
+
is an empty object — referencing `github.event.inputs.param` evaluates to an
|
|
32
|
+
empty string `''`, not an error. The workflow continues running with silently
|
|
33
|
+
missing parameter values, producing incorrect behavior rather than a visible
|
|
34
|
+
failure.
|
|
35
|
+
|
|
36
|
+
The `inputs` context (without `github.event.`) is the correct way to read
|
|
37
|
+
inputs in both scenarios, as it is populated for both `workflow_dispatch` and
|
|
38
|
+
`workflow_call`.
|
|
39
|
+
fix: |
|
|
40
|
+
Replace all `github.event.inputs.*` references with `inputs.*` — the `inputs`
|
|
41
|
+
context works correctly for both `workflow_dispatch` and `workflow_call` events.
|
|
42
|
+
fix_code:
|
|
43
|
+
- language: yaml
|
|
44
|
+
label: 'Use inputs context instead of github.event.inputs'
|
|
45
|
+
code: |
|
|
46
|
+
on:
|
|
47
|
+
workflow_dispatch:
|
|
48
|
+
inputs:
|
|
49
|
+
environment:
|
|
50
|
+
type: string
|
|
51
|
+
required: true
|
|
52
|
+
workflow_call:
|
|
53
|
+
inputs:
|
|
54
|
+
environment:
|
|
55
|
+
type: string
|
|
56
|
+
required: true
|
|
57
|
+
|
|
58
|
+
jobs:
|
|
59
|
+
deploy:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
# BAD: github.event.inputs is empty/null in workflow_call context
|
|
63
|
+
- run: echo "Deploying to ${{ github.event.inputs.environment }}"
|
|
64
|
+
|
|
65
|
+
# GOOD: inputs context works for both workflow_dispatch and workflow_call
|
|
66
|
+
- run: echo "Deploying to ${{ inputs.environment }}"
|
|
67
|
+
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: 'Dual-trigger workflow using inputs context correctly'
|
|
70
|
+
code: |
|
|
71
|
+
on:
|
|
72
|
+
workflow_dispatch:
|
|
73
|
+
inputs:
|
|
74
|
+
dry_run:
|
|
75
|
+
type: boolean
|
|
76
|
+
default: false
|
|
77
|
+
workflow_call:
|
|
78
|
+
inputs:
|
|
79
|
+
dry_run:
|
|
80
|
+
type: boolean
|
|
81
|
+
default: false
|
|
82
|
+
|
|
83
|
+
jobs:
|
|
84
|
+
release:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
# inputs.dry_run works for both dispatch and call
|
|
89
|
+
- if: '!inputs.dry_run'
|
|
90
|
+
run: ./publish.sh
|
|
91
|
+
prevention:
|
|
92
|
+
- 'Always use the `inputs` context (not `github.event.inputs`) when reading inputs in workflows that support both `workflow_dispatch` and `workflow_call`'
|
|
93
|
+
- 'Grep your workflow files for `github.event.inputs` and replace with `inputs` before adding a `workflow_call` trigger'
|
|
94
|
+
- '`github.event.inputs` values are always strings; `inputs` respects the declared type (boolean, number, string, choice)'
|
|
95
|
+
- 'Test reusable workflows by calling them from another workflow, not just via the UI dispatch button'
|
|
96
|
+
docs:
|
|
97
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_call'
|
|
98
|
+
label: 'workflow_call event — GitHub Docs'
|
|
99
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch'
|
|
100
|
+
label: 'workflow_dispatch event — GitHub Docs'
|
|
101
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#inputs-context'
|
|
102
|
+
label: 'inputs context — GitHub Docs'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
id: silent-failures-076
|
|
2
|
+
title: "Job outputs: block missing — needs.job.outputs.key is always empty string in downstream jobs"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- job-outputs
|
|
7
|
+
- needs-context
|
|
8
|
+
- GITHUB_OUTPUT
|
|
9
|
+
- outputs-block
|
|
10
|
+
- empty-string
|
|
11
|
+
- downstream-jobs
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'needs\.[a-z_][a-z0-9_-]*\.outputs\.[a-z_][a-z0-9_-]*'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "needs.deploy.outputs.artifact_url is always empty string"
|
|
17
|
+
- "needs.build.outputs.sha: empty output in downstream job"
|
|
18
|
+
root_cause: |
|
|
19
|
+
Writing to $GITHUB_OUTPUT inside a step only makes the value available within that job
|
|
20
|
+
via steps.<step_id>.outputs.<key>. To expose an output to downstream jobs via
|
|
21
|
+
needs.<job>.outputs.<key>, the job must explicitly declare an outputs: block that maps
|
|
22
|
+
key names to step output expressions using ${{ steps.<id>.outputs.<key> }}.
|
|
23
|
+
|
|
24
|
+
Without the job-level outputs: mapping, $GITHUB_OUTPUT writes are silently ignored by
|
|
25
|
+
downstream consumers — needs.<job>.outputs.<key> evaluates to an empty string with no
|
|
26
|
+
warning, error, or annotation. The step that wrote to $GITHUB_OUTPUT exits with code 0
|
|
27
|
+
and no indication of the problem.
|
|
28
|
+
|
|
29
|
+
This two-step mechanism is required: the step writes to $GITHUB_OUTPUT (job-internal),
|
|
30
|
+
and the job's outputs: block re-exports selected values to the workflow's needs graph.
|
|
31
|
+
fix: |
|
|
32
|
+
Add an outputs: block at the job level (as a sibling of runs-on:, steps:, etc.)
|
|
33
|
+
that maps the desired key names to the corresponding step output expressions.
|
|
34
|
+
Every key you want accessible via needs.<job>.outputs.<key> must appear in this block.
|
|
35
|
+
fix_code:
|
|
36
|
+
- language: yaml
|
|
37
|
+
label: "Correct: job outputs: block declares which step outputs to expose"
|
|
38
|
+
code: |
|
|
39
|
+
jobs:
|
|
40
|
+
build:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
outputs:
|
|
43
|
+
artifact-version: ${{ steps.version.outputs.value }}
|
|
44
|
+
build-sha: ${{ steps.hash.outputs.sha }}
|
|
45
|
+
steps:
|
|
46
|
+
- name: Compute version
|
|
47
|
+
id: version
|
|
48
|
+
run: echo "value=1.2.3" >> $GITHUB_OUTPUT
|
|
49
|
+
- name: Compute hash
|
|
50
|
+
id: hash
|
|
51
|
+
run: echo "sha=$(sha256sum dist/app | cut -d' ' -f1)" >> $GITHUB_OUTPUT
|
|
52
|
+
|
|
53
|
+
deploy:
|
|
54
|
+
needs: build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- run: echo "Deploying ${{ needs.build.outputs.artifact-version }}"
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: "Wrong: outputs: block absent — needs.build.outputs.* is always empty"
|
|
60
|
+
code: |
|
|
61
|
+
jobs:
|
|
62
|
+
build:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
# No outputs: block — step writes are invisible to downstream jobs
|
|
65
|
+
steps:
|
|
66
|
+
- name: Compute version
|
|
67
|
+
id: version
|
|
68
|
+
run: echo "value=1.2.3" >> $GITHUB_OUTPUT
|
|
69
|
+
|
|
70
|
+
deploy:
|
|
71
|
+
needs: build
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
steps:
|
|
74
|
+
- run: echo "${{ needs.build.outputs.artifact-version }}" # Always ''
|
|
75
|
+
prevention:
|
|
76
|
+
- "Any job that produces values for downstream jobs MUST have a matching outputs: block"
|
|
77
|
+
- "Use actionlint — it warns when needs.<job>.outputs.<key> references an undeclared key"
|
|
78
|
+
- "Debug by printing ${{ toJSON(needs) }} in a downstream step to see all available outputs"
|
|
79
|
+
- "Note: steps.<id>.outputs.<key> within the same job does NOT require an outputs: block"
|
|
80
|
+
- "Composite action outputs and reusable workflow outputs have separate but analogous declaration requirements"
|
|
81
|
+
docs:
|
|
82
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/passing-information-between-jobs"
|
|
83
|
+
label: "GitHub Docs: Passing information between jobs"
|
|
84
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idoutputs"
|
|
85
|
+
label: "GitHub Docs: jobs.<job_id>.outputs syntax"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
id: triggers-055
|
|
2
|
+
title: "branches: filter does not apply to tag pushes — tag push always triggers unless tags-ignore is set"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- push
|
|
7
|
+
- branches-filter
|
|
8
|
+
- tags
|
|
9
|
+
- tag-push
|
|
10
|
+
- tags-ignore
|
|
11
|
+
- ref-type
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on:\s*\n\s+push:\s*\n\s+branches:'
|
|
14
|
+
flags: 'm'
|
|
15
|
+
- regex: 'Triggered by refs/tags/'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Run triggered by push to refs/tags/v1.0.0 (unexpected — only branches: [main] was set)"
|
|
19
|
+
root_cause: |
|
|
20
|
+
The branches: filter under on.push: only evaluates branch refs (refs/heads/*). A push
|
|
21
|
+
to a tag ref (refs/tags/*) is an entirely separate ref type that the branches: filter
|
|
22
|
+
has no authority to gate. If no tags: or tags-ignore: filter is specified under
|
|
23
|
+
on.push:, ALL tag pushes pass through unconditionally.
|
|
24
|
+
|
|
25
|
+
This causes workflows intended to run "only on pushes to main" to also run on every
|
|
26
|
+
release tag, annotated tag, and automated tag created by CI pipelines.
|
|
27
|
+
|
|
28
|
+
Branch and tag filters are independent dimensions of the push event:
|
|
29
|
+
branches: / branches-ignore: — applies ONLY to branch refs (refs/heads/*)
|
|
30
|
+
tags: / tags-ignore: — applies ONLY to tag refs (refs/tags/*)
|
|
31
|
+
|
|
32
|
+
If a filter type is absent, all refs of that type pass through.
|
|
33
|
+
Setting branches: [main] does NOT automatically exclude tags.
|
|
34
|
+
|
|
35
|
+
The same mechanism applies in reverse: a tags: filter does not affect branch pushes.
|
|
36
|
+
And the paths: filter is also bypassed by tag pushes (see push-paths-filter-bypassed-by-tag).
|
|
37
|
+
fix: |
|
|
38
|
+
To restrict the workflow to branch pushes only, add a tags-ignore: ['**'] pattern
|
|
39
|
+
to exclude all tag pushes. Alternatively, add an if: condition to check github.ref_type.
|
|
40
|
+
fix_code:
|
|
41
|
+
- language: yaml
|
|
42
|
+
label: "Option 1: add tags-ignore to explicitly exclude all tag pushes"
|
|
43
|
+
code: |
|
|
44
|
+
on:
|
|
45
|
+
push:
|
|
46
|
+
branches:
|
|
47
|
+
- main
|
|
48
|
+
- 'release/**'
|
|
49
|
+
tags-ignore:
|
|
50
|
+
- '**' # Exclude all tag pushes from triggering this workflow
|
|
51
|
+
- language: yaml
|
|
52
|
+
label: "Option 2: use if condition on the job to check ref_type"
|
|
53
|
+
code: |
|
|
54
|
+
on:
|
|
55
|
+
push:
|
|
56
|
+
branches:
|
|
57
|
+
- main
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
build:
|
|
61
|
+
if: github.ref_type == 'branch' # Skip if triggered by a tag push
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- run: echo "Only runs on branch pushes"
|
|
65
|
+
- language: yaml
|
|
66
|
+
label: "Option 3: explicitly allow specific tags alongside branches"
|
|
67
|
+
code: |
|
|
68
|
+
on:
|
|
69
|
+
push:
|
|
70
|
+
branches:
|
|
71
|
+
- main
|
|
72
|
+
tags:
|
|
73
|
+
- 'v[0-9]+.[0-9]+.[0-9]+' # Only semver release tags; other tags excluded
|
|
74
|
+
prevention:
|
|
75
|
+
- "Whenever you set branches:, also consider whether you need tags-ignore: ['**'] to exclude tag pushes"
|
|
76
|
+
- "Check if automated tooling (release-please, semantic-release, etc.) creates tags in your repo — they will trigger this workflow"
|
|
77
|
+
- "The same principle applies to paths: filter — it also does not block tag pushes"
|
|
78
|
+
- "Remember the rule: absent filter = all refs of that type pass through"
|
|
79
|
+
docs:
|
|
80
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushbranchestagsbranches-ignoretags-ignore"
|
|
81
|
+
label: "GitHub Docs: push event branches/tags filter syntax"
|
|
82
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push"
|
|
83
|
+
label: "GitHub Docs: Push event triggers"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
id: triggers-056
|
|
2
|
+
title: '`on.release: types: [created]` fires when a draft release is first saved'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- release
|
|
7
|
+
- triggers
|
|
8
|
+
- draft
|
|
9
|
+
- created
|
|
10
|
+
- published
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'types:\s*\[?created\]?'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'on:\s*\n\s+release:'
|
|
15
|
+
flags: 'im'
|
|
16
|
+
error_messages:
|
|
17
|
+
- 'Triggered by: release'
|
|
18
|
+
- 'github.event.action: created'
|
|
19
|
+
- 'github.event.release.draft: true'
|
|
20
|
+
root_cause: |
|
|
21
|
+
The `release` event with `types: [created]` fires when ANY release record is
|
|
22
|
+
first created in GitHub — including draft releases. When a developer clicks
|
|
23
|
+
"Save draft" to prepare release notes before publishing, GitHub fires the
|
|
24
|
+
`created` event immediately. This causes deployment or publish workflows to
|
|
25
|
+
run against an incomplete, unpublished draft.
|
|
26
|
+
|
|
27
|
+
The `created` type maps to the moment the release row is inserted in GitHub's
|
|
28
|
+
database, regardless of draft or published state. Many developers assume
|
|
29
|
+
`created` means "publicly released" because that mirrors the UI action of
|
|
30
|
+
clicking "Publish release", but the event fires earlier.
|
|
31
|
+
|
|
32
|
+
The correct type for "a release is now publicly visible" is `published`, which
|
|
33
|
+
fires only when the release transitions from draft (or pre-release) to a
|
|
34
|
+
published, non-draft release.
|
|
35
|
+
fix: |
|
|
36
|
+
Use `types: [published]` for workflows that should only run when a release
|
|
37
|
+
becomes publicly available. Add an `if:` guard as a defensive layer when
|
|
38
|
+
using `created`.
|
|
39
|
+
fix_code:
|
|
40
|
+
- language: yaml
|
|
41
|
+
label: 'Use published instead of created for deploy workflows'
|
|
42
|
+
code: |
|
|
43
|
+
# BAD: fires on draft creation too
|
|
44
|
+
on:
|
|
45
|
+
release:
|
|
46
|
+
types: [created]
|
|
47
|
+
|
|
48
|
+
# GOOD: fires only when release becomes publicly published
|
|
49
|
+
on:
|
|
50
|
+
release:
|
|
51
|
+
types: [published]
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Defensive if: guard when created type is required'
|
|
54
|
+
code: |
|
|
55
|
+
on:
|
|
56
|
+
release:
|
|
57
|
+
types: [created]
|
|
58
|
+
jobs:
|
|
59
|
+
deploy:
|
|
60
|
+
# Skip execution when triggered by a draft save
|
|
61
|
+
if: '!github.event.release.draft'
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- run: ./deploy.sh
|
|
66
|
+
prevention:
|
|
67
|
+
- 'Prefer `types: [published]` for deployment workflows to avoid triggering on draft saves'
|
|
68
|
+
- 'Note: `published` also fires when a pre-release is promoted to a full release — guard with `!github.event.release.prerelease` if needed'
|
|
69
|
+
- 'Add `if: ''!github.event.release.draft''` as an explicit guard when using the `created` type'
|
|
70
|
+
- 'Test your release workflow by creating a draft release and verifying it does or does not trigger as expected'
|
|
71
|
+
docs:
|
|
72
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#release'
|
|
73
|
+
label: 'release event — GitHub Docs'
|
|
74
|
+
- url: 'https://docs.github.com/en/rest/releases/releases#create-a-release'
|
|
75
|
+
label: 'Create a release REST API — GitHub Docs'
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
id: yaml-syntax-051
|
|
2
|
+
title: "secrets context unavailable in top-level workflow env: block — only valid at job/step level"
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- secrets
|
|
7
|
+
- env-block
|
|
8
|
+
- workflow-level
|
|
9
|
+
- context-availability
|
|
10
|
+
- expression-error
|
|
11
|
+
- validation
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Context access might be invalid: secrets'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Unrecognized named-value: .secrets.'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'The workflow is not valid.*named-value.*secrets'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "The workflow is not valid. .github/workflows/build.yml (Line: 5, Col: 20): Unexpected value 'secrets'"
|
|
21
|
+
- "Context access might be invalid: secrets"
|
|
22
|
+
- "Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.MY_SECRET"
|
|
23
|
+
root_cause: |
|
|
24
|
+
The secrets context (${{ secrets.* }}) is only available at the job and step level,
|
|
25
|
+
not at the top-level (workflow-level) env: block. Placing secret references under the
|
|
26
|
+
top-level env: key generates a workflow validation error because GitHub evaluates
|
|
27
|
+
the workflow-level env: block before any job context is established and before
|
|
28
|
+
secret injection has occurred.
|
|
29
|
+
|
|
30
|
+
Available secret context locations:
|
|
31
|
+
OK jobs.<job_id>.env:
|
|
32
|
+
OK jobs.<job_id>.steps[*].env:
|
|
33
|
+
OK jobs.<job_id>.steps[*].with:
|
|
34
|
+
OK jobs.<job_id>.container.env:
|
|
35
|
+
OK jobs.<job_id>.services.<id>.env:
|
|
36
|
+
NOT OK env: (top-level, outside jobs:)
|
|
37
|
+
NOT OK jobs.<job_id>.if:
|
|
38
|
+
NOT OK jobs.<job_id>.steps[*].if:
|
|
39
|
+
|
|
40
|
+
This is the most common cause of the "Unexpected value 'secrets'" validation error
|
|
41
|
+
in newly written workflows. The workflow fails the pre-run validation check and
|
|
42
|
+
no jobs execute.
|
|
43
|
+
fix: |
|
|
44
|
+
Move the secrets reference from the top-level env: block down to the job-level or
|
|
45
|
+
step-level env: block where the secrets context is available.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Wrong: secrets in top-level env: block generates validation error"
|
|
49
|
+
code: |
|
|
50
|
+
env:
|
|
51
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Invalid at top level
|
|
52
|
+
API_TOKEN: ${{ secrets.API_TOKEN }} # Invalid at top level
|
|
53
|
+
|
|
54
|
+
jobs:
|
|
55
|
+
test:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- run: ./run-tests.sh
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: "Correct: secrets moved to job-level env: block"
|
|
61
|
+
code: |
|
|
62
|
+
jobs:
|
|
63
|
+
test:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
env:
|
|
66
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Valid at job level
|
|
67
|
+
API_TOKEN: ${{ secrets.API_TOKEN }} # Valid at job level
|
|
68
|
+
steps:
|
|
69
|
+
- run: ./run-tests.sh
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "Alternative: secrets at step-level env: for minimal exposure"
|
|
72
|
+
code: |
|
|
73
|
+
jobs:
|
|
74
|
+
test:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- name: Run tests
|
|
78
|
+
run: ./run-tests.sh
|
|
79
|
+
env:
|
|
80
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }} # Valid at step level
|
|
81
|
+
prevention:
|
|
82
|
+
- "The top-level env: block only supports literals and expressions that don't reference secrets, needs, or job context"
|
|
83
|
+
- "Use top-level env: for constants like APP_ENV: production or NODE_ENV: test"
|
|
84
|
+
- "Prefer step-level env: for secrets to minimize the scope where secrets are exposed"
|
|
85
|
+
- "actionlint checks context availability and will flag secret-placement errors before push"
|
|
86
|
+
- "GitHub validates workflow files on push — the validation error appears as a failed check before any job runs"
|
|
87
|
+
docs:
|
|
88
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability"
|
|
89
|
+
label: "GitHub Docs: Context availability"
|
|
90
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#env"
|
|
91
|
+
label: "GitHub Docs: Workflow env: syntax"
|
package/package.json
CHANGED