@htekdev/actions-debugger 1.0.81 → 1.0.83
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/known-unsolved/step-log-output-truncated-64kb.yml +88 -0
- package/errors/runner-environment/git-commit-author-identity-unknown.yml +83 -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/pull-request-closed-fires-for-unmerged-prs.yml +79 -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/reusable-workflow-with-secrets-context-rejected.yml +98 -0
- package/errors/yaml-syntax/secrets-in-workflow-level-env-block-rejected.yml +91 -0
- package/package.json +1 -1
|
@@ -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,79 @@
|
|
|
1
|
+
id: triggers-057
|
|
2
|
+
title: '`on.pull_request types: [closed]` fires for both merged AND unmerged PR closures — use `if: merged == true` guard'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull_request
|
|
7
|
+
- closed
|
|
8
|
+
- merged
|
|
9
|
+
- event-types
|
|
10
|
+
- deploy-on-merge
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'types:\s*\[.*closed.*\]'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'on:\s*\n\s+pull_request:\s*\n\s+types:\s*\[.*closed'
|
|
16
|
+
flags: 'im'
|
|
17
|
+
|
|
18
|
+
error_messages:
|
|
19
|
+
- "github.event.action: closed"
|
|
20
|
+
- "github.event.pull_request.merged: false"
|
|
21
|
+
|
|
22
|
+
root_cause: |
|
|
23
|
+
The `pull_request` event `closed` type fires whenever a PR is closed — regardless
|
|
24
|
+
of whether it was merged or simply closed without merging. GitHub does not provide
|
|
25
|
+
a `merged` type for the `pull_request` event.
|
|
26
|
+
|
|
27
|
+
Developers commonly use `types: [closed]` to trigger deployment or post-merge
|
|
28
|
+
workflows, assuming `closed` is equivalent to "merged into the base branch". In
|
|
29
|
+
reality, a PR can be closed (abandoned, rejected) without being merged, and the
|
|
30
|
+
`closed` event fires in both cases.
|
|
31
|
+
|
|
32
|
+
The distinction is only available via `github.event.pull_request.merged` (a
|
|
33
|
+
boolean) or `github.event.pull_request.merge_commit_sha` (non-null if merged).
|
|
34
|
+
Without an explicit check, workflows using `types: [closed]` will run on
|
|
35
|
+
abandoned PRs, causing spurious deployments, incorrect release triggers, or
|
|
36
|
+
unnecessary job runs.
|
|
37
|
+
|
|
38
|
+
fix: |
|
|
39
|
+
Add an `if:` condition to the job or workflow that checks
|
|
40
|
+
`github.event.pull_request.merged == true` to ensure the workflow only
|
|
41
|
+
proceeds when the PR was actually merged.
|
|
42
|
+
|
|
43
|
+
fix_code:
|
|
44
|
+
- language: yaml
|
|
45
|
+
label: 'Guard job with merged == true check'
|
|
46
|
+
code: |
|
|
47
|
+
on:
|
|
48
|
+
pull_request:
|
|
49
|
+
types: [closed]
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
deploy:
|
|
53
|
+
# Only run when PR was merged, not when simply closed
|
|
54
|
+
if: github.event.pull_request.merged == true
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- run: ./deploy.sh
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: 'Equivalent guard using merge_commit_sha'
|
|
61
|
+
code: |
|
|
62
|
+
jobs:
|
|
63
|
+
notify:
|
|
64
|
+
if: github.event.pull_request.merge_commit_sha != ''
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- name: Notify on merge
|
|
68
|
+
run: echo "PR merged at ${{ github.event.pull_request.merge_commit_sha }}"
|
|
69
|
+
|
|
70
|
+
prevention:
|
|
71
|
+
- 'Never use `types: [closed]` without an `if: github.event.pull_request.merged == true` guard for merge-triggered workflows.'
|
|
72
|
+
- 'Remember: there is no `merged` event type for `pull_request` — `closed` is the closest type, but it requires the merged guard.'
|
|
73
|
+
- 'Test your workflow by manually closing a PR without merging and verifying the workflow does not run (or is skipped).'
|
|
74
|
+
|
|
75
|
+
docs:
|
|
76
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request'
|
|
77
|
+
label: 'pull_request event — GitHub Docs'
|
|
78
|
+
- url: 'https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request'
|
|
79
|
+
label: 'pull_request webhook payload — GitHub Docs'
|
|
@@ -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,98 @@
|
|
|
1
|
+
id: yaml-syntax-052
|
|
2
|
+
title: '`secrets` context not available in `with:` inputs for reusable workflow calls — use `secrets:` block instead'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- reusable-workflows
|
|
7
|
+
- secrets
|
|
8
|
+
- with
|
|
9
|
+
- workflow-call
|
|
10
|
+
- secrets-context
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Context access might be invalid: secrets'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'secrets\.[A-Z_][A-Z0-9_]*'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Context access might be invalid: secrets"
|
|
20
|
+
- "The workflow is not valid. .github/workflows/caller.yml: Unrecognized named-value: 'secrets'"
|
|
21
|
+
- "Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.MY_TOKEN"
|
|
22
|
+
|
|
23
|
+
root_cause: |
|
|
24
|
+
When calling a reusable workflow, the `with:` key (which passes inputs) does NOT
|
|
25
|
+
have access to the `secrets` context. The `secrets` context is intentionally
|
|
26
|
+
restricted to the `secrets:` block of the reusable workflow call to prevent
|
|
27
|
+
secrets from being accidentally exposed as plain-text input values.
|
|
28
|
+
|
|
29
|
+
This restriction applies ONLY to reusable workflow calls (`jobs.<id>.uses:`).
|
|
30
|
+
Regular action steps (`steps.<id>.uses:`) DO allow `${{ secrets.MY_SECRET }}`
|
|
31
|
+
in their `with:` blocks.
|
|
32
|
+
|
|
33
|
+
Attempting to pass a secret via `with:` in a reusable workflow call results in
|
|
34
|
+
an actionlint error or a runtime error: "Context access might be invalid: secrets".
|
|
35
|
+
Even if it were allowed, passing secrets as `with:` inputs would expose the
|
|
36
|
+
secret value in the workflow logs as a plain-text input.
|
|
37
|
+
|
|
38
|
+
The correct mechanism is the `secrets:` mapping on the calling job, which
|
|
39
|
+
explicitly maps caller secrets to the callee's declared `on.workflow_call.secrets`
|
|
40
|
+
parameters. This preserves masking and audit trail.
|
|
41
|
+
|
|
42
|
+
fix: |
|
|
43
|
+
Move secret values from `with:` to the `secrets:` block of the reusable
|
|
44
|
+
workflow call. Ensure the callee workflow declares the secret under
|
|
45
|
+
`on.workflow_call.secrets` with a matching name.
|
|
46
|
+
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: 'Caller workflow — pass secrets via secrets: not with:'
|
|
50
|
+
code: |
|
|
51
|
+
# BAD: secrets context not available in with:
|
|
52
|
+
# jobs:
|
|
53
|
+
# call:
|
|
54
|
+
# uses: ./.github/workflows/deploy.yml
|
|
55
|
+
# with:
|
|
56
|
+
# token: ${{ secrets.DEPLOY_TOKEN }} # Error: secrets context invalid here
|
|
57
|
+
|
|
58
|
+
# GOOD: pass secrets via secrets: block
|
|
59
|
+
jobs:
|
|
60
|
+
call:
|
|
61
|
+
uses: ./.github/workflows/deploy.yml
|
|
62
|
+
with:
|
|
63
|
+
environment: production # non-secret inputs go here
|
|
64
|
+
secrets:
|
|
65
|
+
deploy-token: ${{ secrets.DEPLOY_TOKEN }} # secrets go here
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: 'Callee workflow — declare secrets under on.workflow_call.secrets'
|
|
68
|
+
code: |
|
|
69
|
+
# .github/workflows/deploy.yml
|
|
70
|
+
on:
|
|
71
|
+
workflow_call:
|
|
72
|
+
inputs:
|
|
73
|
+
environment:
|
|
74
|
+
type: string
|
|
75
|
+
required: true
|
|
76
|
+
secrets:
|
|
77
|
+
deploy-token:
|
|
78
|
+
required: true
|
|
79
|
+
|
|
80
|
+
jobs:
|
|
81
|
+
deploy:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- name: Use the secret
|
|
85
|
+
env:
|
|
86
|
+
TOKEN: ${{ secrets.deploy-token }}
|
|
87
|
+
run: echo "Deploying to ${{ inputs.environment }}"
|
|
88
|
+
|
|
89
|
+
prevention:
|
|
90
|
+
- 'Never use `${{ secrets.* }}` inside `with:` of a reusable workflow call — the `secrets` context is blocked there.'
|
|
91
|
+
- 'Declare all required secrets in the callee under `on.workflow_call.secrets:` and pass them via `secrets:` in the caller.'
|
|
92
|
+
- 'Run actionlint in CI to catch `secrets` context misuse before it reaches runtime.'
|
|
93
|
+
|
|
94
|
+
docs:
|
|
95
|
+
- url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows#using-inputs-and-secrets-in-a-reusable-workflow'
|
|
96
|
+
label: 'Using inputs and secrets in a reusable workflow — GitHub Docs'
|
|
97
|
+
- url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows#passing-secrets-to-called-workflows'
|
|
98
|
+
label: 'Passing secrets to called workflows — GitHub Docs'
|