@htekdev/actions-debugger 1.0.74 → 1.0.76
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/known-unsolved/reusable-workflow-local-action-path-resolves-to-called-repo.yml +114 -0
- package/errors/permissions-auth/org-policy-blocks-pr-creation-despite-pull-requests-write.yml +115 -0
- package/errors/runner-environment/bash-set-e-grep-no-matches-exit-1.yml +103 -0
- package/errors/runner-environment/github-script-require-module-not-found.yml +98 -0
- package/errors/runner-environment/npm-ci-cross-platform-optional-native-binary-missing.yml +113 -0
- package/errors/silent-failures/env-block-sibling-key-reference-empty-string.yml +92 -0
- package/errors/triggers/create-event-fires-for-branch-and-tag.yml +108 -0
- package/errors/triggers/required-status-check-not-in-branch-protection-dropdown.yml +115 -0
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
id: known-unsolved-047
|
|
2
|
+
title: "Reusable workflow uses: ./local-action resolves relative to the called repo, not the calling repo"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- reusable-workflow
|
|
7
|
+
- composite-action
|
|
8
|
+
- path-resolution
|
|
9
|
+
- local-action
|
|
10
|
+
- cross-repo
|
|
11
|
+
- limitation
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Can''t find ''action\.ya?ml'''
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Unable to resolve action.*\.\/'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'action\.ya?ml.*does not exist'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '/home/runner/work/repo/repo/local-action'"
|
|
21
|
+
- "Unable to resolve action `./local-action`, unable to find 'action.yaml' or 'action.yml'"
|
|
22
|
+
- "Error: Can't find 'action.yml' for step uses: ./shared-actions/deploy"
|
|
23
|
+
root_cause: |
|
|
24
|
+
When a reusable workflow file (stored in org/shared-workflows) contains steps that
|
|
25
|
+
reference local composite actions using relative paths (uses: ./path/to/action), the
|
|
26
|
+
path resolves relative to the CALLED (reusable) repository — not the CALLING
|
|
27
|
+
repository.
|
|
28
|
+
|
|
29
|
+
Resolution rule: In any workflow file, uses: ./path always resolves to the root of
|
|
30
|
+
the repository that CONTAINS the workflow YAML file.
|
|
31
|
+
|
|
32
|
+
So in org/shared-workflows/.github/workflows/ci.yml:
|
|
33
|
+
- uses: ./actions/lint → looks in org/shared-workflows/actions/lint/action.yml
|
|
34
|
+
- Does NOT look in org/app-repo/actions/lint/ (the calling repo)
|
|
35
|
+
|
|
36
|
+
This means teams cannot:
|
|
37
|
+
- Share a reusable workflow that calls composite actions defined in the CALLING repo
|
|
38
|
+
- Have callers "inject" local actions into a shared reusable workflow via relative paths
|
|
39
|
+
- Centralize reusable workflows in one repo while composite actions stay in team repos
|
|
40
|
+
|
|
41
|
+
This limitation is by design and has been acknowledged by the GitHub Actions team as
|
|
42
|
+
a fundamental architectural constraint. The calling repository's workspace may be
|
|
43
|
+
checked out during the job, but action path resolution happens at workflow evaluation
|
|
44
|
+
time using the workflow file's own repository root, not the runtime workspace.
|
|
45
|
+
fix: |
|
|
46
|
+
There is no direct fix — this is a platform-level path resolution limitation.
|
|
47
|
+
|
|
48
|
+
Option 1 (recommended) — Co-locate composite actions in the same repository as the
|
|
49
|
+
reusable workflow and reference them with uses: ./path (resolves correctly).
|
|
50
|
+
|
|
51
|
+
Option 2 — Publish shared composite actions to a dedicated standalone repository
|
|
52
|
+
(e.g., org/shared-actions) and reference them with the full path:
|
|
53
|
+
uses: org/shared-actions/path/to-action@main
|
|
54
|
+
|
|
55
|
+
Option 3 — Pass all data that the local action would have computed as inputs to the
|
|
56
|
+
reusable workflow, removing the need for the caller's local action inside the callee.
|
|
57
|
+
fix_code:
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: "Co-locate composite action in the same repo as the reusable workflow"
|
|
60
|
+
code: |
|
|
61
|
+
# Repository: org/shared-workflows
|
|
62
|
+
# Structure:
|
|
63
|
+
# .github/
|
|
64
|
+
# workflows/
|
|
65
|
+
# ci.yml <- reusable workflow
|
|
66
|
+
# actions/
|
|
67
|
+
# shared-lint/
|
|
68
|
+
# action.yml <- composite action lives HERE (same repo)
|
|
69
|
+
|
|
70
|
+
# In org/shared-workflows/.github/workflows/ci.yml:
|
|
71
|
+
on:
|
|
72
|
+
workflow_call:
|
|
73
|
+
inputs:
|
|
74
|
+
source-directory:
|
|
75
|
+
type: string
|
|
76
|
+
required: true
|
|
77
|
+
|
|
78
|
+
jobs:
|
|
79
|
+
lint:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
# Resolves to org/shared-workflows/.github/actions/shared-lint
|
|
83
|
+
- uses: ./.github/actions/shared-lint
|
|
84
|
+
with:
|
|
85
|
+
src-path: ${{ inputs.source-directory }}
|
|
86
|
+
|
|
87
|
+
- language: yaml
|
|
88
|
+
label: "Reference composite action from a standalone shared repo"
|
|
89
|
+
code: |
|
|
90
|
+
# Instead of: uses: ./local-action (broken — resolves to reusable workflow's repo)
|
|
91
|
+
# Use the full repo reference pointing to org/shared-actions:
|
|
92
|
+
jobs:
|
|
93
|
+
deploy:
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
steps:
|
|
96
|
+
- uses: org/shared-actions/deploy@v2
|
|
97
|
+
with:
|
|
98
|
+
environment: ${{ inputs.environment }}
|
|
99
|
+
- uses: org/shared-actions/notify@v2
|
|
100
|
+
with:
|
|
101
|
+
status: ${{ job.status }}
|
|
102
|
+
|
|
103
|
+
prevention:
|
|
104
|
+
- "Keep reusable workflow files and their local composite action dependencies in the same repository"
|
|
105
|
+
- "Publish composite actions shared across many repositories to a dedicated org/shared-actions repo"
|
|
106
|
+
- "Test reusable workflows by calling them from a different repository early in development"
|
|
107
|
+
- "Document which composite actions a reusable workflow depends on and where they must be located"
|
|
108
|
+
docs:
|
|
109
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows"
|
|
110
|
+
label: "GitHub docs — Reusing workflows"
|
|
111
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/about-custom-actions#choosing-a-location-for-your-action"
|
|
112
|
+
label: "GitHub docs — Choosing a location for your action"
|
|
113
|
+
- url: "https://github.com/orgs/community/discussions/17244"
|
|
114
|
+
label: "GitHub Community discussion — local composite action reference from reusable workflow"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: permissions-auth-047
|
|
2
|
+
title: "Org-Level Policy Blocks GitHub Actions from Creating or Approving Pull Requests Even with pull-requests: write"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- pull-requests
|
|
7
|
+
- permissions
|
|
8
|
+
- org-policy
|
|
9
|
+
- github-token
|
|
10
|
+
- create-pr
|
|
11
|
+
- branch-protection
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'GitHub Actions is not permitted to create or approve pull requests'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Resource not accessible by integration'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'HttpError.*403.*pull request'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "GitHub Actions is not permitted to create or approve pull requests."
|
|
21
|
+
- "HttpError: Resource not accessible by integration"
|
|
22
|
+
- "Error: 403 - {\"message\":\"GitHub Actions is not permitted to create or approve pull requests.\",\"documentation_url\":\"https://docs.github.com/rest/pulls/pulls\"}"
|
|
23
|
+
- "Error: Validation Failed: pull request creation is not allowed for this repository"
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub enforces a two-level policy hierarchy for GitHub Actions creating or
|
|
26
|
+
approving pull requests:
|
|
27
|
+
|
|
28
|
+
1. **Organization level** — GitHub organization owners must enable
|
|
29
|
+
"Allow GitHub Actions to create and approve pull requests" under:
|
|
30
|
+
Organization Settings → Actions → General → Workflow permissions
|
|
31
|
+
|
|
32
|
+
2. **Repository level** — After the org-level setting is enabled, each repository
|
|
33
|
+
can independently enable or disable the same option. The repo-level option is
|
|
34
|
+
grayed out and non-interactive until the org-level gate is open.
|
|
35
|
+
|
|
36
|
+
3. **Enterprise level** — For GitHub Enterprise accounts, the same setting must
|
|
37
|
+
also be enabled at the enterprise level (Enterprise Settings → Actions → General)
|
|
38
|
+
before it can be configured at the org level.
|
|
39
|
+
|
|
40
|
+
Adding `permissions: pull-requests: write` (or `permissions: write-all`) to the
|
|
41
|
+
workflow YAML only controls the GITHUB_TOKEN's OAuth scope. The org/enterprise policy
|
|
42
|
+
is an additional administrative gate enforced by the GitHub API regardless of what
|
|
43
|
+
the token is scoped for. The token can have `pull-requests: write` scope but still
|
|
44
|
+
receive HTTP 403 if the org policy is disabled.
|
|
45
|
+
|
|
46
|
+
The repo-level "Read and write permissions" setting in repo Settings → Actions → General
|
|
47
|
+
is a second distinct control — it sets the token's default permissions but still
|
|
48
|
+
cannot override a disabled org-level policy.
|
|
49
|
+
fix: |
|
|
50
|
+
Step 1 — Enable the setting at the **organization level** (requires org owner):
|
|
51
|
+
https://github.com/organizations/YOUR_ORG/settings/actions
|
|
52
|
+
Under "Workflow permissions" → check "Allow GitHub Actions to create and approve pull requests"
|
|
53
|
+
|
|
54
|
+
Step 2 — Enable the setting at the **repository level** (the option is now clickable):
|
|
55
|
+
https://github.com/YOUR_ORG/YOUR_REPO/settings/actions
|
|
56
|
+
Under "Workflow permissions" → check "Allow GitHub Actions to create and approve pull requests"
|
|
57
|
+
|
|
58
|
+
Step 3 — Add the permission to the workflow file:
|
|
59
|
+
permissions: pull-requests: write (plus contents: write if creating commits)
|
|
60
|
+
|
|
61
|
+
For GitHub Enterprise: also enable at enterprise level first:
|
|
62
|
+
https://github.com/enterprises/YOUR_ENTERPRISE/settings/actions
|
|
63
|
+
|
|
64
|
+
For personal accounts (no org):
|
|
65
|
+
Go to Repository Settings → Actions → General → Workflow permissions →
|
|
66
|
+
set "Read and write permissions".
|
|
67
|
+
fix_code:
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Add required permissions to the workflow job"
|
|
70
|
+
code: |
|
|
71
|
+
jobs:
|
|
72
|
+
create-pr:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
permissions:
|
|
75
|
+
contents: write # required to push branches
|
|
76
|
+
pull-requests: write # required to create/comment on PRs
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- name: Create Pull Request
|
|
80
|
+
uses: peter-evans/create-pull-request@v6
|
|
81
|
+
with:
|
|
82
|
+
title: 'chore: automated update'
|
|
83
|
+
branch: 'automated/update'
|
|
84
|
+
- language: yaml
|
|
85
|
+
label: "Using gh CLI to create a PR (also requires pull-requests: write + org policy enabled)"
|
|
86
|
+
code: |
|
|
87
|
+
jobs:
|
|
88
|
+
open-pr:
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
permissions:
|
|
91
|
+
contents: write
|
|
92
|
+
pull-requests: write
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v4
|
|
95
|
+
- name: Open PR
|
|
96
|
+
env:
|
|
97
|
+
GH_TOKEN: ${{ github.token }}
|
|
98
|
+
run: |
|
|
99
|
+
gh pr create \
|
|
100
|
+
--title "chore: automated update" \
|
|
101
|
+
--body "Created by GitHub Actions" \
|
|
102
|
+
--base main \
|
|
103
|
+
--head "${{ github.ref_name }}"
|
|
104
|
+
prevention:
|
|
105
|
+
- "Before building PR-automation workflows, verify the org-level 'Allow GitHub Actions to create and approve pull requests' setting is enabled."
|
|
106
|
+
- "Check the setting at all applicable levels — enterprise (if applicable) → organization → repository."
|
|
107
|
+
- "Always declare explicit `permissions:` in workflows that create PRs; do not rely on repository defaults."
|
|
108
|
+
- "Use `permissions: pull-requests: write` at the job level (not workflow level) to minimize token scope."
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#preventing-github-actions-from-creating-or-approving-pull-requests"
|
|
111
|
+
label: "GitHub Docs — Preventing GitHub Actions from creating or approving pull requests"
|
|
112
|
+
- url: "https://docs.github.com/en/organizations/managing-organization-settings/disabling-or-limiting-github-actions-for-your-organization#preventing-github-actions-from-creating-or-approving-pull-requests"
|
|
113
|
+
label: "GitHub Docs — Org-level: disabling GitHub Actions from creating pull requests"
|
|
114
|
+
- url: "https://stackoverflow.com/questions/72376229/github-actions-is-not-permitted-to-create-or-approve-pull-requests"
|
|
115
|
+
label: "SO: GitHub Actions is not permitted to create or approve pull requests (67 votes, 40k views)"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
id: runner-environment-137
|
|
2
|
+
title: "bash -eo pipefail Default Causes grep Exit Code 1 (No Matches) to Fail the Step"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- bash
|
|
7
|
+
- grep
|
|
8
|
+
- pipefail
|
|
9
|
+
- set-e
|
|
10
|
+
- shell
|
|
11
|
+
- exit-code
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Error: Process completed with exit code 1\.'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'grep.*exit code 1'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
root_cause: |
|
|
18
|
+
GitHub Actions `run:` steps on Linux/macOS use `bash -e {0}` by default when no
|
|
19
|
+
`shell:` is specified. When `shell: bash` is explicitly set, the shell is invoked
|
|
20
|
+
as `bash --noprofile --norc -eo pipefail {0}`, adding `-o pipefail`.
|
|
21
|
+
|
|
22
|
+
The key behavior:
|
|
23
|
+
- `-e` (set -e) — any command that exits non-zero immediately aborts the whole step.
|
|
24
|
+
- `grep` returns exit code 1 when it finds zero matches (not an error in the shell
|
|
25
|
+
sense, but a non-zero exit). Combined with `-e`, a `grep` that matches nothing
|
|
26
|
+
kills the step with "Error: Process completed with exit code 1."
|
|
27
|
+
- The issue is silent — the step log shows the grep command output (or no output)
|
|
28
|
+
and then suddenly fails. Developers who test locally (where `set -e` is not the
|
|
29
|
+
default interactive shell setting) never reproduce the failure.
|
|
30
|
+
|
|
31
|
+
Common triggers:
|
|
32
|
+
- `grep -c pattern file` when pattern not found → exits 1
|
|
33
|
+
- `git log | grep "pattern"` in CI where the pattern is absent
|
|
34
|
+
- Variable assignment: `COUNT=$(echo "$LIST" | grep -c "value")` — if grep exits 1,
|
|
35
|
+
the subshell exits 1, and `-e` aborts the parent step
|
|
36
|
+
error_messages:
|
|
37
|
+
- "Error: Process completed with exit code 1."
|
|
38
|
+
- "##[error]Process completed with exit code 1."
|
|
39
|
+
fix: |
|
|
40
|
+
Several options depending on your use case:
|
|
41
|
+
|
|
42
|
+
Option 1 — Disable fail-fast for a specific command using `|| true`:
|
|
43
|
+
Append `|| true` to any grep command where zero matches is acceptable. The `|| true`
|
|
44
|
+
ensures the overall expression always exits 0.
|
|
45
|
+
|
|
46
|
+
Option 2 — Disable `set -e` for the entire step with `shell: bash {0}`:
|
|
47
|
+
Using `{0}` as the script placeholder removes the `-e` flag. Use this when you need
|
|
48
|
+
full control over exit codes throughout the step.
|
|
49
|
+
|
|
50
|
+
Option 3 — Use `set +e` at the top of the run block to disable fail-fast for all
|
|
51
|
+
subsequent commands, then selectively handle errors manually.
|
|
52
|
+
|
|
53
|
+
Option 4 — Use grep's `--quiet` / `-q` flag and evaluate with an `if` statement,
|
|
54
|
+
never relying on exit code propagation.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "WRONG — grep exit code 1 kills step when no match found"
|
|
58
|
+
code: |
|
|
59
|
+
- name: Count matches
|
|
60
|
+
run: |
|
|
61
|
+
COUNT=$(echo "$DIFF" | grep -c "src/my_folder")
|
|
62
|
+
echo "Changed files: $COUNT"
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "CORRECT — append || true to grep commands where zero matches is valid"
|
|
65
|
+
code: |
|
|
66
|
+
- name: Count matches
|
|
67
|
+
run: |
|
|
68
|
+
COUNT=$(echo "$DIFF" | grep -c "src/my_folder" || true)
|
|
69
|
+
echo "Changed files: $COUNT"
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "CORRECT — disable set -e for entire step with shell: bash {0}"
|
|
72
|
+
code: |
|
|
73
|
+
- name: Count matches
|
|
74
|
+
shell: bash {0}
|
|
75
|
+
run: |
|
|
76
|
+
COUNT=$(echo "$DIFF" | grep -c "src/my_folder")
|
|
77
|
+
echo "Changed files: $COUNT"
|
|
78
|
+
# now exit codes must be checked manually
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: "CORRECT — use grep -q with explicit if for boolean presence check"
|
|
81
|
+
code: |
|
|
82
|
+
- name: Check for changes
|
|
83
|
+
run: |
|
|
84
|
+
if echo "$DIFF" | grep -q "src/my_folder"; then
|
|
85
|
+
echo "Changes detected in src/my_folder"
|
|
86
|
+
else
|
|
87
|
+
echo "No changes in src/my_folder"
|
|
88
|
+
fi
|
|
89
|
+
prevention:
|
|
90
|
+
- "Append `|| true` to any `grep`, `awk`, or other command where a non-zero exit is not an error."
|
|
91
|
+
- "Use `shell: bash {0}` (no `-e`) for steps that need fine-grained exit code handling."
|
|
92
|
+
- "Prefer `grep -q` with `if/else` blocks instead of relying on grep exit codes for flow control."
|
|
93
|
+
- "Test CI scripts locally with `bash -e script.sh` to reproduce the fail-fast behavior."
|
|
94
|
+
- "Use `set -o pipefail` explicitly when you need pipeline failures to surface, and document it."
|
|
95
|
+
docs:
|
|
96
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell"
|
|
97
|
+
label: "jobs.<job_id>.steps[*].shell — default shell invocation"
|
|
98
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference"
|
|
99
|
+
label: "Exit codes and error action preference"
|
|
100
|
+
- url: "https://stackoverflow.com/questions/73066461/github-actions-why-an-intermediate-command-failure-in-shell-script-would-cause"
|
|
101
|
+
label: "SO: Why an intermediate command failure causes the whole step to fail (17 votes)"
|
|
102
|
+
- url: "https://stackoverflow.com/questions/75419587/does-a-github-action-step-use-set-e-semantics-by-default"
|
|
103
|
+
label: "SO: Does a GitHub action step use set -e semantics by default? (13 votes)"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
id: runner-environment-136
|
|
2
|
+
title: "actions/github-script require() of third-party npm packages fails with MODULE_NOT_FOUND"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-script
|
|
7
|
+
- nodejs
|
|
8
|
+
- require
|
|
9
|
+
- npm
|
|
10
|
+
- module-not-found
|
|
11
|
+
- third-party-package
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Cannot find module'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'MODULE_NOT_FOUND'
|
|
16
|
+
flags: ''
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Error: Cannot find module 'axios'"
|
|
19
|
+
- "Error: Cannot find module 'lodash'"
|
|
20
|
+
- "Error: Cannot find module 'js-yaml'"
|
|
21
|
+
- "Error: Cannot find module 'semver'"
|
|
22
|
+
- "Cannot find module at /home/runner/work/_actions/actions/github-script/v7/lib/async-function.js:1"
|
|
23
|
+
root_cause: |
|
|
24
|
+
The actions/github-script action executes scripts in a sandboxed Node.js runtime that
|
|
25
|
+
only includes packages bundled with the action itself. Built-in parameters available
|
|
26
|
+
in the script context are:
|
|
27
|
+
- github — authenticated Octokit client (@octokit/rest)
|
|
28
|
+
- context — workflow event context
|
|
29
|
+
- core — @actions/core
|
|
30
|
+
- glob — @actions/glob
|
|
31
|
+
- io — @actions/io
|
|
32
|
+
- exec — @actions/exec
|
|
33
|
+
- fetch — Node.js built-in fetch (Node 18+)
|
|
34
|
+
|
|
35
|
+
Any call to require() for packages NOT in this list — such as axios, lodash, js-yaml,
|
|
36
|
+
semver, chalk, or any other npm package — will fail with MODULE_NOT_FOUND because the
|
|
37
|
+
packages are not installed in the runner environment that executes the script.
|
|
38
|
+
|
|
39
|
+
The action does NOT auto-install packages from the repo's package.json or any other
|
|
40
|
+
manifest. Node.js module resolution only searches paths within the bundled action's
|
|
41
|
+
own node_modules directory, which contains only the above built-ins.
|
|
42
|
+
fix: |
|
|
43
|
+
Option 1 (install before use) — Run npm install in a prior step and reference the
|
|
44
|
+
package via full path using RUNNER_TEMP or GITHUB_WORKSPACE:
|
|
45
|
+
- name: Install npm package
|
|
46
|
+
run: npm install <package-name>
|
|
47
|
+
working-directory: ${{ runner.temp }}
|
|
48
|
+
- uses: actions/github-script@v7
|
|
49
|
+
with:
|
|
50
|
+
script: |
|
|
51
|
+
const pkg = require(process.env.RUNNER_TEMP + '/node_modules/<package-name>')
|
|
52
|
+
|
|
53
|
+
Option 2 (use built-ins) — Refactor to use only the built-in parameters. For HTTP
|
|
54
|
+
requests use fetch() instead of axios; for YAML parsing use a run: step with a script
|
|
55
|
+
file.
|
|
56
|
+
|
|
57
|
+
Option 3 (separate Node.js script) — Move complex logic into a .js file in the repo
|
|
58
|
+
and run it with node in a run: step after npm install, giving full package access.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: "Install package before github-script and require via RUNNER_TEMP"
|
|
62
|
+
code: |
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
|
|
66
|
+
- name: Install axios for github-script
|
|
67
|
+
run: npm install axios
|
|
68
|
+
working-directory: ${{ runner.temp }}
|
|
69
|
+
|
|
70
|
+
- uses: actions/github-script@v7
|
|
71
|
+
with:
|
|
72
|
+
script: |
|
|
73
|
+
const axios = require(process.env.RUNNER_TEMP + '/node_modules/axios')
|
|
74
|
+
const { data } = await axios.get('https://api.example.com/status')
|
|
75
|
+
core.setOutput('api-status', data.status)
|
|
76
|
+
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Replace axios with built-in fetch() (Node 18+, no require needed)"
|
|
79
|
+
code: |
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/github-script@v7
|
|
82
|
+
with:
|
|
83
|
+
script: |
|
|
84
|
+
// fetch is built-in — no require() needed (Node 18+, github-script v7+)
|
|
85
|
+
const response = await fetch('https://api.example.com/status')
|
|
86
|
+
const data = await response.json()
|
|
87
|
+
core.setOutput('api-status', data.status)
|
|
88
|
+
|
|
89
|
+
prevention:
|
|
90
|
+
- "Check the github-script README 'Built-in variables' section before adding require() calls"
|
|
91
|
+
- "Use fetch() (built-in from Node 18+ / github-script v7+) instead of axios for HTTP requests"
|
|
92
|
+
- "For complex logic needing many npm packages, use a separate script file with a run: node step"
|
|
93
|
+
- "Consider caching the npm install step if packages are large or installed frequently"
|
|
94
|
+
docs:
|
|
95
|
+
- url: "https://github.com/actions/github-script?tab=readme-ov-file#built-in-variables"
|
|
96
|
+
label: "actions/github-script — Built-in variables (official README)"
|
|
97
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-github-script-in-a-workflow"
|
|
98
|
+
label: "GitHub docs — Using GitHub Script in a workflow"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
id: runner-environment-138
|
|
2
|
+
title: "npm ci Cross-Platform Lockfile Missing Optional Native Binaries (esbuild, rollup, vite)"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- npm
|
|
7
|
+
- npm-ci
|
|
8
|
+
- esbuild
|
|
9
|
+
- rollup
|
|
10
|
+
- vite
|
|
11
|
+
- optional-dependencies
|
|
12
|
+
- cross-platform
|
|
13
|
+
- lockfile
|
|
14
|
+
- native-binary
|
|
15
|
+
patterns:
|
|
16
|
+
- regex: 'Cannot find module @rollup/rollup-linux-x64'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'You installed esbuild (on|for) another platform'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'Cannot find module @esbuild/linux'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
- regex: 'npm has a bug related to optional dependencies'
|
|
23
|
+
flags: 'i'
|
|
24
|
+
error_messages:
|
|
25
|
+
- "Cannot find module @rollup/rollup-linux-x64-gnu"
|
|
26
|
+
- "Cannot find module @rollup/rollup-linux-x64-musl"
|
|
27
|
+
- "You installed esbuild on another platform than the one you're currently using"
|
|
28
|
+
- "Cannot find module @esbuild/linux-x64"
|
|
29
|
+
- "npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory."
|
|
30
|
+
root_cause: |
|
|
31
|
+
Modern JavaScript bundlers (rollup, vite, esbuild, SWC, Turbopack) ship platform-
|
|
32
|
+
specific optional native binaries as separate npm packages — e.g.,
|
|
33
|
+
`@rollup/rollup-linux-x64-gnu`, `@esbuild/darwin-arm64`, `@esbuild/linux-x64`.
|
|
34
|
+
|
|
35
|
+
When a developer runs `npm install` on macOS (darwin-arm64 or darwin-x64), the
|
|
36
|
+
generated `package-lock.json` records the macOS-specific optional packages but NOT
|
|
37
|
+
the Linux equivalents. GitHub-hosted runners run on Linux. When `npm ci` uses
|
|
38
|
+
this macOS-generated lockfile in CI:
|
|
39
|
+
|
|
40
|
+
1. `npm ci` correctly installs exactly what's in the lockfile — only the macOS
|
|
41
|
+
native binary entries are present.
|
|
42
|
+
2. The Linux-specific optional dependency (`@rollup/rollup-linux-x64-gnu`) is
|
|
43
|
+
absent from the lockfile, so `npm ci` never installs it.
|
|
44
|
+
3. The build step then fails because the required native binary is missing.
|
|
45
|
+
|
|
46
|
+
This was a long-standing npm bug (npm/cli#4828) that was fixed in npm 11.3.0
|
|
47
|
+
(shipped with Node.js 24.0.2). On older npm versions the issue must be worked around.
|
|
48
|
+
|
|
49
|
+
Note: `npm ci` itself succeeds — the failure occurs downstream during the build/test
|
|
50
|
+
step that imports the native module.
|
|
51
|
+
fix: |
|
|
52
|
+
Option 1 (recommended for npm 11.3.0+ / Node.js 24.0.2+):
|
|
53
|
+
Update Node.js to 24.0.2 or npm to 11.3.0+, then regenerate the lockfile with
|
|
54
|
+
`npm install` from scratch. The bug is fixed in this version.
|
|
55
|
+
|
|
56
|
+
Option 2 — Explicitly list the Linux optional dependencies in `package.json`:
|
|
57
|
+
Add all required platform-native packages to the `optionalDependencies` section.
|
|
58
|
+
npm will then include them in the lockfile regardless of the OS where it was generated.
|
|
59
|
+
|
|
60
|
+
Option 3 — Regenerate the lockfile in CI using `npm install --package-lock-only`
|
|
61
|
+
before running `npm ci`. This is slow but guarantees the lockfile matches the runner OS.
|
|
62
|
+
|
|
63
|
+
Option 4 — Use a Linux environment locally (Docker, WSL2, or devcontainer) to
|
|
64
|
+
generate the lockfile so it contains Linux-specific entries.
|
|
65
|
+
fix_code:
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: "Option 2 — Add Linux optional native packages to package.json optionalDependencies"
|
|
68
|
+
code: |
|
|
69
|
+
# In package.json, add the Linux optional dependency explicitly:
|
|
70
|
+
# "optionalDependencies": {
|
|
71
|
+
# "@rollup/rollup-linux-x64-gnu": "*"
|
|
72
|
+
# }
|
|
73
|
+
# Then run npm install locally to regenerate the lockfile.
|
|
74
|
+
# In your workflow, npm ci will now install it on Linux runners.
|
|
75
|
+
jobs:
|
|
76
|
+
build:
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- uses: actions/setup-node@v4
|
|
81
|
+
with:
|
|
82
|
+
node-version: '20'
|
|
83
|
+
cache: 'npm'
|
|
84
|
+
- run: npm ci
|
|
85
|
+
- run: npm run build
|
|
86
|
+
- language: yaml
|
|
87
|
+
label: "Option 1 — Use Node.js 24+ where the bug is fixed"
|
|
88
|
+
code: |
|
|
89
|
+
jobs:
|
|
90
|
+
build:
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
- uses: actions/setup-node@v4
|
|
95
|
+
with:
|
|
96
|
+
node-version: '24' # npm 11.3.0+ ships with Node 24.0.2+
|
|
97
|
+
cache: 'npm'
|
|
98
|
+
- run: npm ci
|
|
99
|
+
- run: npm run build
|
|
100
|
+
prevention:
|
|
101
|
+
- "Pin your team's lockfile generation to Linux (use devcontainers, Docker, or WSL2 on Windows)."
|
|
102
|
+
- "Upgrade to Node.js 24.0.2+ or npm 11.3.0+ where the optional dependency platform bug is fixed."
|
|
103
|
+
- "If cross-platform lockfiles are unavoidable, explicitly add platform-specific packages to `optionalDependencies` in `package.json`."
|
|
104
|
+
- "After adding `optionalDependencies`, regenerate the lockfile from scratch — delete `package-lock.json` and `node_modules`, then run `npm install`."
|
|
105
|
+
docs:
|
|
106
|
+
- url: "https://stackoverflow.com/questions/79048814/github-action-is-failing-due-to-rollup-rollup-linux-x64-gnu"
|
|
107
|
+
label: "SO: Github Action failing due to @rollup/rollup-linux-x64-gnu (13 votes, 8k views)"
|
|
108
|
+
- url: "https://stackoverflow.com/questions/73139649/you-installed-esbuild-on-another-platform-than-the-one-youre-currently-using"
|
|
109
|
+
label: "SO: You installed esbuild on another platform (36 votes, 51k views)"
|
|
110
|
+
- url: "https://github.com/npm/cli/issues/4828"
|
|
111
|
+
label: "npm/cli#4828 — Optional dependencies not installed for different platform"
|
|
112
|
+
- url: "https://vitejs.dev/guide/troubleshooting#vite-cjs-node-api-deprecated"
|
|
113
|
+
label: "Vite troubleshooting — esbuild platform mismatch"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
id: silent-failures-071
|
|
2
|
+
title: "${{ env.VAR }} in a step's env: block evaluates to empty string when VAR is a sibling key in the same block"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- env-context
|
|
7
|
+
- expression
|
|
8
|
+
- env-block
|
|
9
|
+
- sibling-reference
|
|
10
|
+
- empty-string
|
|
11
|
+
- configuration
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'env\.\w+\}\}.*\{\{\s*env\.\w+'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "(no error message — step runs successfully but env var is assigned an incorrect partial value)"
|
|
17
|
+
root_cause: |
|
|
18
|
+
When multiple environment variables are defined in the same step (or job) env: block
|
|
19
|
+
and one attempts to compose a value from a sibling key using ${{ env.X }}, the
|
|
20
|
+
reference silently evaluates to an EMPTY STRING.
|
|
21
|
+
|
|
22
|
+
GitHub Actions evaluates all expressions in an env: block BEFORE the step runs, using
|
|
23
|
+
the env context that exists at that point in time. That context includes:
|
|
24
|
+
- Variables defined in the workflow-level env: block
|
|
25
|
+
- Variables defined in the job-level env: block (jobs.<id>.env)
|
|
26
|
+
- Variables added by previous steps via $GITHUB_ENV
|
|
27
|
+
|
|
28
|
+
It does NOT include sibling keys defined elsewhere in the SAME env: block being
|
|
29
|
+
evaluated. The block is evaluated atomically — each value is expanded using only
|
|
30
|
+
the env context from BEFORE the block, not from within it.
|
|
31
|
+
|
|
32
|
+
Example of the mistake:
|
|
33
|
+
env:
|
|
34
|
+
BASE_URL: https://api.example.com # this is fine
|
|
35
|
+
FULL_URL: ${{ env.BASE_URL }}/v2 # SILENTLY wrong: evaluates to "/v2"
|
|
36
|
+
|
|
37
|
+
FULL_URL becomes "/v2" (not "https://api.example.com/v2") because BASE_URL is not
|
|
38
|
+
in the env context at the time the step's env: block expressions are evaluated —
|
|
39
|
+
it is only available AFTER the step starts executing.
|
|
40
|
+
|
|
41
|
+
This is a silent failure: no warning is produced, the step succeeds, and the
|
|
42
|
+
misconfigured variable is silently assigned the wrong value.
|
|
43
|
+
fix: |
|
|
44
|
+
Option 1 (recommended) — Move the "base" variable to the workflow-level or job-level
|
|
45
|
+
env: block so it is already in the env context when the step's env: block is evaluated.
|
|
46
|
+
|
|
47
|
+
Option 2 — Compose the derived value inside the run: step using shell variable
|
|
48
|
+
expansion, which operates at runtime when all variables are already set.
|
|
49
|
+
|
|
50
|
+
Option 3 — Use an expression based on inputs, vars, or secrets contexts which ARE
|
|
51
|
+
fully available during env: block evaluation.
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: "Hoist base variable to workflow-level env so it's in context"
|
|
55
|
+
code: |
|
|
56
|
+
# Define BASE_URL at workflow level — it will be in the env context
|
|
57
|
+
env:
|
|
58
|
+
BASE_URL: https://api.example.com
|
|
59
|
+
|
|
60
|
+
jobs:
|
|
61
|
+
deploy:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- name: Call API
|
|
65
|
+
run: curl "$FULL_URL"
|
|
66
|
+
env:
|
|
67
|
+
# env.BASE_URL is available here because it's defined at workflow level
|
|
68
|
+
FULL_URL: ${{ env.BASE_URL }}/v2/endpoint
|
|
69
|
+
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "Compose the value inside run: using shell variable expansion"
|
|
72
|
+
code: |
|
|
73
|
+
steps:
|
|
74
|
+
- name: Call API endpoint
|
|
75
|
+
run: |
|
|
76
|
+
# Shell variable composition works at runtime when BASE_URL is already set
|
|
77
|
+
FULL_URL="${BASE_URL}/v2/endpoint"
|
|
78
|
+
curl "$FULL_URL"
|
|
79
|
+
env:
|
|
80
|
+
BASE_URL: https://api.example.com
|
|
81
|
+
# FULL_URL built in shell — no need to compose in env: block
|
|
82
|
+
|
|
83
|
+
prevention:
|
|
84
|
+
- "Define 'base' env vars at workflow or job level when they need to be referenced by step-level env: blocks"
|
|
85
|
+
- "Use shell variable composition inside run: steps rather than ${{ env.X }} in the same env: block"
|
|
86
|
+
- "Verify composed env var values by printing them with echo before use in critical steps"
|
|
87
|
+
- "Review GitHub Actions context availability documentation when authoring env: blocks"
|
|
88
|
+
docs:
|
|
89
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/variables#using-the-env-context-to-access-environment-variable-values"
|
|
90
|
+
label: "GitHub docs — Using the env context to access environment variable values"
|
|
91
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability"
|
|
92
|
+
label: "GitHub docs — Context availability table"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
id: triggers-051
|
|
2
|
+
title: "on: create event fires for both branch and tag creation — must filter with github.ref_type"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- create-event
|
|
7
|
+
- delete-event
|
|
8
|
+
- ref-type
|
|
9
|
+
- tag
|
|
10
|
+
- branch
|
|
11
|
+
- trigger-filter
|
|
12
|
+
- release
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'on:\s*[\r\n]+\s+create:'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "(no error message — workflow triggers unexpectedly on branch creation, not just tag creation)"
|
|
18
|
+
root_cause: |
|
|
19
|
+
The on: create event triggers whenever ANY ref (branch OR tag) is created in the
|
|
20
|
+
repository. There is no built-in filter to restrict it to only tag creation or only
|
|
21
|
+
branch creation.
|
|
22
|
+
|
|
23
|
+
Many developers use on: create expecting to run release or version-publish workflows
|
|
24
|
+
only when a version tag is pushed, but the workflow also fires for:
|
|
25
|
+
- Feature branches created via the GitHub UI or API
|
|
26
|
+
- Dependabot version update branches (dependabot/npm_and_yarn/...)
|
|
27
|
+
- Automated branches created by bots or scripts
|
|
28
|
+
- PR branches created by tooling
|
|
29
|
+
- Any branch creation event from any actor
|
|
30
|
+
|
|
31
|
+
The github.ref_type context variable ('branch' or 'tag') is available in the run
|
|
32
|
+
context but on: create has no built-in filter for it — an explicit if: condition
|
|
33
|
+
on the job or step must be used to restrict execution.
|
|
34
|
+
|
|
35
|
+
The same behavior applies to on: delete — it fires for both branch and tag deletion.
|
|
36
|
+
fix: |
|
|
37
|
+
Add an if: condition at the job level (or step level) to filter by github.ref_type:
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
release:
|
|
41
|
+
if: github.ref_type == 'tag' # restrict to tag creation only
|
|
42
|
+
|
|
43
|
+
Alternative: Use on: push with a tags: filter instead of on: create.
|
|
44
|
+
The push event with tags: filter is more explicit, supports glob patterns,
|
|
45
|
+
and does not fire for branch operations at all.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Filter on: create to tags only using ref_type job condition"
|
|
49
|
+
code: |
|
|
50
|
+
on:
|
|
51
|
+
create:
|
|
52
|
+
|
|
53
|
+
jobs:
|
|
54
|
+
release:
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
# Only run for tag creation (e.g., v1.2.3), not branch creation
|
|
57
|
+
if: github.ref_type == 'tag'
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- name: Build and publish release
|
|
61
|
+
run: echo "Publishing ${{ github.ref_name }}"
|
|
62
|
+
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "Preferred: use on: push with tags filter for tag-triggered workflows"
|
|
65
|
+
code: |
|
|
66
|
+
# More explicit and common pattern for release workflows
|
|
67
|
+
on:
|
|
68
|
+
push:
|
|
69
|
+
tags:
|
|
70
|
+
- 'v*.*.*' # triggers on v1.0.0, v2.3.1, etc.
|
|
71
|
+
# - 'v[0-9]+.*' # alternative glob
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
release:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
- name: Build and publish release
|
|
79
|
+
run: echo "Publishing ${{ github.ref_name }}"
|
|
80
|
+
|
|
81
|
+
- language: yaml
|
|
82
|
+
label: "Filter on: delete to branch deletion only (e.g., branch cleanup)"
|
|
83
|
+
code: |
|
|
84
|
+
on:
|
|
85
|
+
delete:
|
|
86
|
+
|
|
87
|
+
jobs:
|
|
88
|
+
cleanup:
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
# Only run for branch deletion, not tag deletion
|
|
91
|
+
if: github.ref_type == 'branch'
|
|
92
|
+
steps:
|
|
93
|
+
- name: Cleanup branch resources
|
|
94
|
+
run: echo "Cleaning up resources for ${{ github.ref_name }}"
|
|
95
|
+
|
|
96
|
+
prevention:
|
|
97
|
+
- "Always add if: github.ref_type == 'tag' when using on: create for release/publish workflows"
|
|
98
|
+
- "Prefer on: push with tags: filter for tag-triggered release workflows — it's more explicit"
|
|
99
|
+
- "Add if: github.ref_type == 'branch' when using on: delete for branch cleanup automation"
|
|
100
|
+
- "Test create/delete workflows by creating a feature branch to verify it does NOT trigger unexpectedly"
|
|
101
|
+
- "Review all on: create workflows in the repository after adding Dependabot — it creates many branches"
|
|
102
|
+
docs:
|
|
103
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#create"
|
|
104
|
+
label: "GitHub docs — create event"
|
|
105
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#delete"
|
|
106
|
+
label: "GitHub docs — delete event"
|
|
107
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#github-context"
|
|
108
|
+
label: "GitHub docs — github context (ref_type field)"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: triggers-052
|
|
2
|
+
title: "Required Status Check Not Appearing in Branch Protection Dropdown"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- branch-protection
|
|
7
|
+
- required-status-check
|
|
8
|
+
- check-name
|
|
9
|
+
- job-name
|
|
10
|
+
- workflow-name
|
|
11
|
+
- protected-branch
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'status check.*not found'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'waiting for status to be reported'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Waiting for status to be reported"
|
|
19
|
+
- "Required status check is expected — check name not found in the dropdown"
|
|
20
|
+
root_cause: |
|
|
21
|
+
Branch protection required status checks have several constraints that cause them
|
|
22
|
+
to silently not appear in the "Status checks that are required" search dropdown:
|
|
23
|
+
|
|
24
|
+
1. **Wrong name searched** — The check name in branch protection is the job's
|
|
25
|
+
`name:` field (if set) or the job's YAML key (ID) — NOT the workflow's `name:`
|
|
26
|
+
field. Developers often search for the workflow name and can't find the check.
|
|
27
|
+
For matrix jobs, the check name includes matrix values:
|
|
28
|
+
e.g., `build (ubuntu-latest, 20.x)` not just `build`.
|
|
29
|
+
|
|
30
|
+
2. **Check must have run recently** — A status check only appears in the dropdown
|
|
31
|
+
after it has completed successfully at least once on that repository within the
|
|
32
|
+
past 7 days. Brand-new workflows that have never been triggered won't show up.
|
|
33
|
+
|
|
34
|
+
3. **Workflow must target the protected branch** — For PR-triggered checks, the
|
|
35
|
+
workflow must have `on: pull_request: branches: [main]` (or the target branch).
|
|
36
|
+
A workflow that only runs on `push` to feature branches never posts a status
|
|
37
|
+
to main's pull requests.
|
|
38
|
+
|
|
39
|
+
4. **Paths filters suppressing the check** — If the workflow has `paths:` filters,
|
|
40
|
+
PRs that don't change filtered files never trigger the workflow, so the check
|
|
41
|
+
is absent (causing PRs to be permanently stuck as "Waiting"). This is covered
|
|
42
|
+
separately in `required-status-check-paths-filter-pr-stuck.yml`.
|
|
43
|
+
|
|
44
|
+
All of these issues are silent — no error is shown. The dropdown simply doesn't
|
|
45
|
+
include the expected check name.
|
|
46
|
+
fix: |
|
|
47
|
+
Step 1 — Confirm the exact check name:
|
|
48
|
+
Go to any recently-merged or open PR in the repository. Find the check in the
|
|
49
|
+
PR's "Checks" tab. Copy the exact displayed name — it is the job's `name:` field
|
|
50
|
+
or the YAML job key, not the workflow `name:` at the top of the file.
|
|
51
|
+
For matrix jobs, include the matrix combination: `build (ubuntu-latest, 20.x)`.
|
|
52
|
+
|
|
53
|
+
Step 2 — Trigger the workflow at least once on a PR against the protected branch:
|
|
54
|
+
The check must have a successful run within the past 7 days to appear.
|
|
55
|
+
Create a test PR or push a temporary commit to get the first run.
|
|
56
|
+
|
|
57
|
+
Step 3 — Add a descriptive `name:` to each job that will be a required check.
|
|
58
|
+
This makes the check name human-readable and stable even if you rename the job key.
|
|
59
|
+
|
|
60
|
+
Step 4 — For matrix workflows, consider adding a fanout job (a job that `needs:`
|
|
61
|
+
all matrix jobs) with a simple pass/fail step, and set THAT as the required check.
|
|
62
|
+
This avoids needing to list every matrix combination individually in branch protection.
|
|
63
|
+
fix_code:
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: "Add explicit job name for a stable, searchable required status check name"
|
|
66
|
+
code: |
|
|
67
|
+
jobs:
|
|
68
|
+
build:
|
|
69
|
+
name: "CI Build" # This is the check name in branch protection, NOT 'build'
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
- run: npm ci && npm test
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: "Matrix jobs — add a fanout job to use as the single required check"
|
|
76
|
+
code: |
|
|
77
|
+
jobs:
|
|
78
|
+
test:
|
|
79
|
+
name: "Test (${{ matrix.os }}, Node ${{ matrix.node }})"
|
|
80
|
+
runs-on: ${{ matrix.os }}
|
|
81
|
+
strategy:
|
|
82
|
+
matrix:
|
|
83
|
+
os: [ubuntu-latest, windows-latest]
|
|
84
|
+
node: ['18', '20']
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- uses: actions/setup-node@v4
|
|
88
|
+
with:
|
|
89
|
+
node-version: ${{ matrix.node }}
|
|
90
|
+
- run: npm ci && npm test
|
|
91
|
+
|
|
92
|
+
all-tests-pass:
|
|
93
|
+
name: "All Tests Pass" # Set THIS as the required status check
|
|
94
|
+
needs: test
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
if: always()
|
|
97
|
+
steps:
|
|
98
|
+
- name: Verify all matrix jobs passed
|
|
99
|
+
run: |
|
|
100
|
+
if [[ "${{ needs.test.result }}" != "success" ]]; then
|
|
101
|
+
echo "One or more test matrix jobs failed"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
prevention:
|
|
105
|
+
- "Always add a `name:` to jobs that will be required status checks — job IDs change when refactoring."
|
|
106
|
+
- "Trigger the workflow at least once before setting up branch protection, so the check appears in the dropdown."
|
|
107
|
+
- "For matrix jobs, add a dedicated fanout job that passes only when all matrix jobs pass; set it as the required check."
|
|
108
|
+
- "Avoid `paths:` filters on required-check workflows — use them on optional checks only, or use `required-status-check-paths-filter-pr-stuck.yml` workaround."
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks"
|
|
111
|
+
label: "GitHub Docs — Troubleshooting required status checks"
|
|
112
|
+
- url: "https://stackoverflow.com/questions/68554735/github-action-status-check-missing-from-the-list-of-checks-in-protected-branch-s"
|
|
113
|
+
label: "SO: Github Action Status check missing from Protected branch settings (75 votes, 29k views)"
|
|
114
|
+
- url: "https://stackoverflow.com/questions/61989951/github-action-workflow-not-running"
|
|
115
|
+
label: "SO: GitHub Action workflow not running (158 votes, 202k views)"
|
package/package.json
CHANGED