@htekdev/actions-debugger 1.0.99 → 1.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/caching-artifacts/caching-artifacts-055.yml +104 -0
- package/errors/known-unsolved/known-unsolved-055.yml +124 -0
- package/errors/permissions-auth/permissions-auth-058.yml +101 -0
- package/errors/runner-environment/runner-environment-169.yml +88 -0
- package/errors/silent-failures/github-event-pull-request-null-on-non-pr-events.yml +96 -0
- package/errors/triggers/pull-request-edited-type-not-in-defaults.yml +82 -0
- package/errors/triggers/skip-ci-commit-message-silently-skips-workflows.yml +80 -0
- package/errors/yaml-syntax/yaml-syntax-error-hides-workflow-from-actions-ui.yml +94 -0
- package/package.json +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
id: caching-artifacts-055
|
|
2
|
+
title: 'setup-python cache: poetry broken when poetry is installed before setup-python — virtualenv 20.33.0+ Python version mismatch'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-python
|
|
7
|
+
- poetry
|
|
8
|
+
- cache
|
|
9
|
+
- virtualenv
|
|
10
|
+
- python-version
|
|
11
|
+
- pipx
|
|
12
|
+
- ordering
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'Current Python version \([\d.]+\) is not allowed by the project'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'poetry.*install.*exit code 1'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'Please change python executable via the "env use" command'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'installed package poetry.*using Python \d+\.\d+'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- "Current Python version (3.12.3) is not allowed by the project (>= 3.13, < 4). Please change python executable via the \"env use\" command."
|
|
24
|
+
- "Current Python version (3.11.9) is not allowed by the project (>=3.12,<3.13). Please change python executable via the \"env use\" command."
|
|
25
|
+
- "Error: Process completed with exit code 1."
|
|
26
|
+
root_cause: |
|
|
27
|
+
When using actions/setup-python with cache: 'poetry', the action requires
|
|
28
|
+
poetry to be installed BEFORE it runs (to resolve the cache path). Developers
|
|
29
|
+
commonly install poetry via pipx before calling setup-python, which causes
|
|
30
|
+
pipx to anchor poetry to the runner's pre-installed system Python (e.g., 3.12.3).
|
|
31
|
+
|
|
32
|
+
After setup-python runs and installs the project's target Python version
|
|
33
|
+
(e.g., 3.13), poetry's internal virtualenv logic still references the Python
|
|
34
|
+
version it was originally installed with. Consequently, when poetry creates
|
|
35
|
+
or activates a virtual environment, it resolves to the old Python version —
|
|
36
|
+
which violates the project's python requirement and produces the
|
|
37
|
+
"Current Python version is not allowed" error.
|
|
38
|
+
|
|
39
|
+
This became a consistent failure in August 2025 when virtualenv 20.33.0
|
|
40
|
+
changed how it resolves the Python interpreter for new virtual environments,
|
|
41
|
+
making the version mismatch explicit and fatal rather than silently using
|
|
42
|
+
the wrong interpreter.
|
|
43
|
+
|
|
44
|
+
The fundamental issue is a circular dependency in the documented workflow:
|
|
45
|
+
setup-python needs poetry installed to set up caching, but poetry must be
|
|
46
|
+
installed after setup-python to use the correct Python version.
|
|
47
|
+
fix: |
|
|
48
|
+
Use actions/cache manually instead of setup-python's built-in poetry cache.
|
|
49
|
+
Install poetry AFTER setup-python so it is anchored to the correct Python.
|
|
50
|
+
This avoids the circular ordering problem entirely.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Workaround — Manual cache + install poetry after setup-python'
|
|
54
|
+
code: |
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/setup-python@v5
|
|
57
|
+
id: setup-python
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.13'
|
|
60
|
+
# Do NOT use cache: 'poetry' here
|
|
61
|
+
|
|
62
|
+
- name: Install poetry
|
|
63
|
+
run: pipx install poetry
|
|
64
|
+
|
|
65
|
+
- name: Cache poetry virtualenv
|
|
66
|
+
uses: actions/cache@v4
|
|
67
|
+
with:
|
|
68
|
+
path: ~/.cache/pypoetry/virtualenvs
|
|
69
|
+
key: ${{ runner.os }}-poetry-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
|
70
|
+
restore-keys: |
|
|
71
|
+
${{ runner.os }}-poetry-${{ steps.setup-python.outputs.python-version }}-
|
|
72
|
+
|
|
73
|
+
- name: Install dependencies
|
|
74
|
+
run: poetry install
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: 'Alternative — pin virtualenv version as a short-term patch'
|
|
77
|
+
code: |
|
|
78
|
+
steps:
|
|
79
|
+
- name: Install poetry (with pinned virtualenv)
|
|
80
|
+
run: |
|
|
81
|
+
pipx install poetry
|
|
82
|
+
pipx inject poetry virtualenv==20.26.6
|
|
83
|
+
|
|
84
|
+
- uses: actions/setup-python@v5
|
|
85
|
+
with:
|
|
86
|
+
python-version: '3.13'
|
|
87
|
+
cache: 'poetry'
|
|
88
|
+
|
|
89
|
+
- name: Install dependencies
|
|
90
|
+
run: poetry install
|
|
91
|
+
prevention:
|
|
92
|
+
- "Never use setup-python cache: poetry in combination with a poetry install step that runs before setup-python."
|
|
93
|
+
- "Use actions/cache manually to avoid the circular dependency between poetry installation and Python version resolution."
|
|
94
|
+
- "Keep poetry updated — poetry 2.1.4+ partially mitigates the virtualenv version mismatch for some project configurations."
|
|
95
|
+
- "Use uv as an alternative to poetry; uv is installed on GitHub-hosted runners and handles Python version resolution differently."
|
|
96
|
+
docs:
|
|
97
|
+
- url: 'https://github.com/actions/setup-python/issues/1167'
|
|
98
|
+
label: 'actions/setup-python #1167 — Issues with poetry caching (Aug 2025)'
|
|
99
|
+
- url: 'https://github.com/python-poetry/poetry/issues/10490'
|
|
100
|
+
label: 'python-poetry/poetry #10490 — virtualenv 20.33.0 compatibility issue'
|
|
101
|
+
- url: 'https://github.com/pypa/virtualenv/issues/2931'
|
|
102
|
+
label: 'pypa/virtualenv #2931 — Python interpreter resolution change'
|
|
103
|
+
- url: 'https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages'
|
|
104
|
+
label: 'actions/setup-python — Caching packages documentation'
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
id: known-unsolved-055
|
|
2
|
+
title: 'github.event.workflow_run.pull_requests is empty for fork PRs — PR context unavailable in triggered workflow'
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_run
|
|
7
|
+
- fork
|
|
8
|
+
- pull-requests
|
|
9
|
+
- event-context
|
|
10
|
+
- security
|
|
11
|
+
- limitation
|
|
12
|
+
- pull_request
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'github\.event\.workflow_run\.pull_requests'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'pull_requests.*\[\]|workflow_run.*pull_requests.*empty'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "# No error thrown — github.event.workflow_run.pull_requests evaluates to [] for fork PRs"
|
|
20
|
+
- "Error: PR number is empty or undefined"
|
|
21
|
+
- "Cannot read properties of undefined (reading 'number')"
|
|
22
|
+
root_cause: |
|
|
23
|
+
When a workflow is triggered by on: workflow_run: and the upstream workflow
|
|
24
|
+
was triggered by a pull_request event from a fork, the
|
|
25
|
+
github.event.workflow_run.pull_requests array is deliberately empty ([]).
|
|
26
|
+
|
|
27
|
+
For same-repository PRs, the pull_requests array is populated with PR
|
|
28
|
+
metadata (number, head.sha, head.ref, base.ref, etc.). For fork PRs, it
|
|
29
|
+
is always an empty array for security reasons: GitHub does not expose PR
|
|
30
|
+
metadata from fork branches through this event payload to prevent
|
|
31
|
+
untrusted code from accessing repository information via the privileged
|
|
32
|
+
workflow_run context.
|
|
33
|
+
|
|
34
|
+
This is a known, by-design limitation (GitHub closed the tracker issue as
|
|
35
|
+
"not planned"). The result is that workflows designed to comment on PRs,
|
|
36
|
+
post status checks, or download artifacts using the PR number silently
|
|
37
|
+
fail or crash when run against fork-originated pull requests because
|
|
38
|
+
github.event.workflow_run.pull_requests[0].number evaluates to undefined.
|
|
39
|
+
|
|
40
|
+
The limitation is easy to miss during development because the workflow
|
|
41
|
+
works correctly for PRs from within the same organization — the failure
|
|
42
|
+
only appears when an external contributor opens a fork PR.
|
|
43
|
+
fix: |
|
|
44
|
+
Pass the required PR information as an artifact from the upstream
|
|
45
|
+
(untrusted) workflow to the downstream (privileged) workflow_run workflow.
|
|
46
|
+
The upstream workflow writes the PR number to a file and uploads it as
|
|
47
|
+
an artifact; the downstream workflow downloads the artifact and reads
|
|
48
|
+
the PR number from it.
|
|
49
|
+
|
|
50
|
+
This is the officially documented pattern for safe fork PR workflows.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Upstream (untrusted) workflow — upload PR number as artifact'
|
|
54
|
+
code: |
|
|
55
|
+
# .github/workflows/pr-checks.yml (runs on pull_request, limited privileges)
|
|
56
|
+
name: PR Checks
|
|
57
|
+
on:
|
|
58
|
+
pull_request:
|
|
59
|
+
|
|
60
|
+
jobs:
|
|
61
|
+
test:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- run: echo "${{ github.event.number }}" > pr_number.txt
|
|
66
|
+
- uses: actions/upload-artifact@v4
|
|
67
|
+
with:
|
|
68
|
+
name: pr-number
|
|
69
|
+
path: pr_number.txt
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: 'Downstream (privileged) workflow — download artifact to get PR number'
|
|
72
|
+
code: |
|
|
73
|
+
# .github/workflows/pr-comment.yml (runs on workflow_run, full privileges)
|
|
74
|
+
name: PR Comment
|
|
75
|
+
on:
|
|
76
|
+
workflow_run:
|
|
77
|
+
workflows: ["PR Checks"]
|
|
78
|
+
types: [completed]
|
|
79
|
+
|
|
80
|
+
jobs:
|
|
81
|
+
comment:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- name: Download PR number artifact
|
|
85
|
+
uses: actions/github-script@v7
|
|
86
|
+
with:
|
|
87
|
+
script: |
|
|
88
|
+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
|
89
|
+
owner: context.repo.owner,
|
|
90
|
+
repo: context.repo.repo,
|
|
91
|
+
run_id: context.payload.workflow_run.id,
|
|
92
|
+
});
|
|
93
|
+
const prArtifact = artifacts.data.artifacts.find(a => a.name === 'pr-number');
|
|
94
|
+
if (!prArtifact) { core.setFailed('No pr-number artifact'); return; }
|
|
95
|
+
const download = await github.rest.actions.downloadArtifact({
|
|
96
|
+
owner: context.repo.owner,
|
|
97
|
+
repo: context.repo.repo,
|
|
98
|
+
artifact_id: prArtifact.id,
|
|
99
|
+
archive_format: 'zip',
|
|
100
|
+
});
|
|
101
|
+
require('fs').writeFileSync('pr_number.zip', Buffer.from(download.data));
|
|
102
|
+
- run: unzip pr_number.zip && echo "PR=$(cat pr_number.txt)" >> $GITHUB_ENV
|
|
103
|
+
- name: Post comment
|
|
104
|
+
uses: actions/github-script@v7
|
|
105
|
+
with:
|
|
106
|
+
script: |
|
|
107
|
+
await github.rest.issues.createComment({
|
|
108
|
+
owner: context.repo.owner,
|
|
109
|
+
repo: context.repo.repo,
|
|
110
|
+
issue_number: Number(process.env.PR),
|
|
111
|
+
body: 'Checks passed!'
|
|
112
|
+
});
|
|
113
|
+
prevention:
|
|
114
|
+
- "Always use the artifact-passing pattern for workflow_run workflows that need PR context — never assume github.event.workflow_run.pull_requests is populated."
|
|
115
|
+
- "Check the triggering event type: if github.event.workflow_run.event == 'pull_request' and the array is empty, the PR came from a fork."
|
|
116
|
+
- "Use github.event.workflow_run.head_branch and head_sha for correlating runs, but not for PR numbers (which are unavailable for forks)."
|
|
117
|
+
- "Consider using the gh-action-pr-number community action which handles the artifact-based lookup pattern automatically."
|
|
118
|
+
docs:
|
|
119
|
+
- url: 'https://github.com/actions/runner/issues/3444'
|
|
120
|
+
label: 'actions/runner #3444 — workflow_run from fork pull_request lacks pull_requests payload'
|
|
121
|
+
- url: 'https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow'
|
|
122
|
+
label: 'GitHub Docs — Using data from the triggering workflow (artifact pattern)'
|
|
123
|
+
- url: 'https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run'
|
|
124
|
+
label: 'GitHub Docs — workflow_run event reference'
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
id: permissions-auth-058
|
|
2
|
+
title: 'actions/checkout does not rewrite ssh:// submodule URLs — only git@github.com: format is rewritten'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- submodules
|
|
8
|
+
- ssh
|
|
9
|
+
- gitmodules
|
|
10
|
+
- URL-rewriting
|
|
11
|
+
- host-key-verification
|
|
12
|
+
- ssh-key
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'Host key verification failed'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'ssh://.*Permission denied|ssh://.*Could not read from remote'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'fatal: clone of .ssh://. into submodule path .* failed'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'git@github\.com.*vs.*ssh://github\.com'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- "Host key verification failed."
|
|
24
|
+
- "fatal: Could not read from remote repository."
|
|
25
|
+
- "Please make sure you have the correct access rights and the repository exists."
|
|
26
|
+
- "fatal: clone of 'ssh://git@github.com/Org/SubRepo.git' into submodule path '<path>' failed"
|
|
27
|
+
root_cause: |
|
|
28
|
+
actions/checkout rewrites submodule URLs in .gitmodules to inject the
|
|
29
|
+
provided ssh-key or GITHUB_TOKEN credentials, but the rewriting logic only
|
|
30
|
+
handles the git@github.com: URL format. URLs using the alternative
|
|
31
|
+
ssh://git@github.com/ scheme are NOT rewritten and are passed to git
|
|
32
|
+
as-is.
|
|
33
|
+
|
|
34
|
+
When git attempts to clone an ssh:// URL without an SSH agent providing
|
|
35
|
+
the key, and without the runner having github.com in ~/.ssh/known_hosts,
|
|
36
|
+
the clone fails with "Host key verification failed." or permission errors.
|
|
37
|
+
|
|
38
|
+
The affected code path is actions/checkout/src/git-auth-helper.ts which
|
|
39
|
+
contains an explicit regex that only matches the git@ style:
|
|
40
|
+
/^git@github\.com:/
|
|
41
|
+
The ssh:// variant does not match, so the credential injection and
|
|
42
|
+
known_hosts population steps are skipped for those URLs.
|
|
43
|
+
|
|
44
|
+
This is a confirmed open bug with a pending fix (PR #2367), but as of
|
|
45
|
+
early 2026 it remains unmerged and unreleased.
|
|
46
|
+
fix: |
|
|
47
|
+
Convert all submodule URLs in .gitmodules from ssh:// to git@github.com:
|
|
48
|
+
format. This ensures actions/checkout's URL rewriting logic applies the
|
|
49
|
+
SSH key and adds the host key to known_hosts.
|
|
50
|
+
|
|
51
|
+
If you cannot change .gitmodules (e.g., the submodule is a third-party
|
|
52
|
+
repo), use a manual git config insteadOf URL rewrite in a pre-checkout
|
|
53
|
+
step to normalize the URL before the checkout action runs.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: bash
|
|
56
|
+
label: 'Option A — Change .gitmodules to use git@ format instead of ssh://'
|
|
57
|
+
code: |
|
|
58
|
+
# .gitmodules — BEFORE (broken)
|
|
59
|
+
[submodule "MySubrepo"]
|
|
60
|
+
path = MySubrepo
|
|
61
|
+
url = ssh://git@github.com/Org/MySubrepo.git
|
|
62
|
+
|
|
63
|
+
# .gitmodules — AFTER (working)
|
|
64
|
+
[submodule "MySubrepo"]
|
|
65
|
+
path = MySubrepo
|
|
66
|
+
url = git@github.com:Org/MySubrepo.git
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Option B — Add git config URL rewrite step before checkout'
|
|
69
|
+
code: |
|
|
70
|
+
steps:
|
|
71
|
+
- name: Rewrite ssh:// submodule URLs to git@ format
|
|
72
|
+
run: |
|
|
73
|
+
git config --global url."git@github.com:".insteadOf "ssh://git@github.com/"
|
|
74
|
+
|
|
75
|
+
- uses: actions/checkout@v4
|
|
76
|
+
with:
|
|
77
|
+
submodules: recursive
|
|
78
|
+
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }}
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: 'Option C — Use HTTPS with token for submodule auth (avoids SSH entirely)'
|
|
81
|
+
code: |
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@v4
|
|
84
|
+
with:
|
|
85
|
+
submodules: recursive
|
|
86
|
+
token: ${{ secrets.PAT_WITH_SUBMODULE_ACCESS }}
|
|
87
|
+
# Requires .gitmodules to use https:// URLs
|
|
88
|
+
prevention:
|
|
89
|
+
- "Standardize on git@github.com: format for all .gitmodules URLs in repositories using actions/checkout with SSH keys."
|
|
90
|
+
- "Use HTTPS URLs with a PAT for submodule access when possible — this is simpler and avoids SSH host key issues entirely."
|
|
91
|
+
- "Add a linting step (e.g., grep .gitmodules for ssh://) to your repo's pre-commit hooks to catch the wrong URL format early."
|
|
92
|
+
- "Track actions/checkout PR #2367 for the fix; upgrade once it is merged and released."
|
|
93
|
+
docs:
|
|
94
|
+
- url: 'https://github.com/actions/checkout/issues/2178'
|
|
95
|
+
label: 'actions/checkout #2178 — ssh:// URLs in .gitmodules do not work'
|
|
96
|
+
- url: 'https://github.com/actions/checkout/pull/2367'
|
|
97
|
+
label: 'actions/checkout PR #2367 — Fix: also rewrite ssh:// URLs'
|
|
98
|
+
- url: 'https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts'
|
|
99
|
+
label: 'actions/checkout git-auth-helper.ts — URL rewriting source code'
|
|
100
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-conditions-to-control-job-execution'
|
|
101
|
+
label: 'GitHub Docs — Checking out submodules'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
id: runner-environment-169
|
|
2
|
+
title: 'actions/runner 2.320.0+ container image removes SSH — "error: cannot run ssh: No such file or directory"'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- runner
|
|
7
|
+
- ssh
|
|
8
|
+
- openssh-client
|
|
9
|
+
- git-submodules
|
|
10
|
+
- self-hosted
|
|
11
|
+
- container-runner
|
|
12
|
+
- 2.320
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'error: cannot run ssh: No such file or directory'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'fatal: unable to fork'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'Unable to locate executable file: ssh'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'error downloading.*ssh://.*No such file or directory'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- "error: cannot run ssh: No such file or directory"
|
|
24
|
+
- "fatal: unable to fork"
|
|
25
|
+
- "Unable to locate executable file: ssh. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable."
|
|
26
|
+
- "error downloading 'ssh://git@github.com/...': /usr/bin/git exited with 128: error: cannot run ssh: No such file or directory"
|
|
27
|
+
root_cause: |
|
|
28
|
+
Starting with actions/runner 2.320.0, the base container image
|
|
29
|
+
(ghcr.io/actions/actions-runner) no longer ships openssh-client by default.
|
|
30
|
+
Workflows that rely on git operations over SSH — including submodule cloning
|
|
31
|
+
via SSH URLs, Terragrunt module downloads over ssh://, or any step that
|
|
32
|
+
calls ssh-keyscan — fail at runtime because the ssh binary is absent from
|
|
33
|
+
the runner's PATH.
|
|
34
|
+
|
|
35
|
+
This affected both self-hosted runners built from the official container
|
|
36
|
+
image and any ephemeral runners (Actions Runner Controller / ARC) that
|
|
37
|
+
derive from ghcr.io/actions/actions-runner:2.320.0+. Hosted runners
|
|
38
|
+
(ubuntu-latest, etc.) were not affected because they use a separate,
|
|
39
|
+
pre-loaded VM image that still includes openssh-client.
|
|
40
|
+
|
|
41
|
+
The regression was introduced when the container image was slimmed down
|
|
42
|
+
between 2.319.1 and 2.320.0 without a corresponding changelog callout,
|
|
43
|
+
leaving self-hosted container runners silently broken on upgrade.
|
|
44
|
+
fix: |
|
|
45
|
+
Install openssh-client in the runner container image before the runner
|
|
46
|
+
process starts. For Dockerfiles extending the official image, add an
|
|
47
|
+
explicit RUN instruction. For ARC runner deployments, add an
|
|
48
|
+
initContainers step or a containerStartupCommand to install the package.
|
|
49
|
+
|
|
50
|
+
If you do not control the image, add an installation step at the top of
|
|
51
|
+
the failing workflow job before any SSH-dependent steps.
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: 'Option A — Add install step in the workflow before any SSH steps'
|
|
55
|
+
code: |
|
|
56
|
+
jobs:
|
|
57
|
+
build:
|
|
58
|
+
runs-on: self-hosted
|
|
59
|
+
steps:
|
|
60
|
+
- name: Install SSH client
|
|
61
|
+
run: |
|
|
62
|
+
apt-get update -qq && apt-get install -y --no-install-recommends openssh-client
|
|
63
|
+
- name: Checkout with submodules
|
|
64
|
+
uses: actions/checkout@v4
|
|
65
|
+
with:
|
|
66
|
+
submodules: recursive
|
|
67
|
+
ssh-key: ${{ secrets.SSH_KEY }}
|
|
68
|
+
- language: dockerfile
|
|
69
|
+
label: 'Option B — Bake openssh-client into a custom runner image'
|
|
70
|
+
code: |
|
|
71
|
+
FROM ghcr.io/actions/actions-runner:latest
|
|
72
|
+
USER root
|
|
73
|
+
RUN apt-get update && apt-get install -y --no-install-recommends openssh-client && rm -rf /var/lib/apt/lists/*
|
|
74
|
+
USER runner
|
|
75
|
+
prevention:
|
|
76
|
+
- "Pin your ARC / self-hosted runner image to a tested version (e.g., ghcr.io/actions/actions-runner:2.319.1) and test upgrades in a staging environment before rolling out."
|
|
77
|
+
- "Add a pre-flight check step that runs 'which ssh || (apt-get install -y openssh-client)' if your workflow requires SSH."
|
|
78
|
+
- "Prefer HTTPS-based submodule URLs and GITHUB_TOKEN for submodule auth in GitHub Actions — this avoids SSH entirely."
|
|
79
|
+
- "Subscribe to the actions/runner GitHub Releases feed to catch breaking changes in container image composition."
|
|
80
|
+
docs:
|
|
81
|
+
- url: 'https://github.com/actions/runner/issues/3490'
|
|
82
|
+
label: 'actions/runner #3490 — Runner 2.320.0 no longer has SSH installed'
|
|
83
|
+
- url: 'https://github.com/actions/runner/issues/3488'
|
|
84
|
+
label: 'actions/runner #3488 — Runner version 2.320.0 breaks custom image'
|
|
85
|
+
- url: 'https://github.com/actions/checkout/issues/1942'
|
|
86
|
+
label: 'actions/checkout #1942 — Unable to locate executable file: ssh'
|
|
87
|
+
- url: 'https://github.com/actions/runner/releases'
|
|
88
|
+
label: 'actions/runner releases'
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: silent-failures-090
|
|
2
|
+
title: '`github.event.pull_request.*` Fields Are Null on Non-PR Events — Comparisons Silently Evaluate Incorrectly'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- github-context
|
|
7
|
+
- pull_request
|
|
8
|
+
- null-context
|
|
9
|
+
- push-event
|
|
10
|
+
- multi-event
|
|
11
|
+
- expression
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'github\.event\.pull_request\.\w+'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Unexpected value '' in expression"
|
|
17
|
+
root_cause: |
|
|
18
|
+
When a workflow is triggered by a non-pull_request event — such as `push`, `schedule`,
|
|
19
|
+
`workflow_dispatch`, `workflow_call`, or `release` — the `github.event.pull_request`
|
|
20
|
+
object is null. Every child field evaluates to empty string `""` in expressions.
|
|
21
|
+
|
|
22
|
+
This silently breaks conditions in multi-event workflows:
|
|
23
|
+
|
|
24
|
+
- `if: github.event.pull_request.draft == false`
|
|
25
|
+
On a push event, `draft` resolves to `""`. The comparison `"" == false` evaluates to
|
|
26
|
+
FALSE (empty string is not boolean false), so the step silently skips.
|
|
27
|
+
|
|
28
|
+
- `if: github.event.pull_request.merged == true`
|
|
29
|
+
Always false on push events, causing steps intended for merged-PR context to silently
|
|
30
|
+
never execute.
|
|
31
|
+
|
|
32
|
+
- `env: PR_NUMBER: ${{ github.event.pull_request.number }}`
|
|
33
|
+
Sets `PR_NUMBER` to empty string on push events. Downstream scripts that require a
|
|
34
|
+
PR number fail with "invalid argument" or use `0` as the number.
|
|
35
|
+
|
|
36
|
+
- `if: github.event.pull_request.head.repo.fork != true`
|
|
37
|
+
Always evaluates to true on push events (empty string != true), bypassing fork guards.
|
|
38
|
+
|
|
39
|
+
The root issue is that workflows triggered by multiple events (e.g., `on: [push,
|
|
40
|
+
pull_request]`) share the same `if:` conditions and `env:` references, but the
|
|
41
|
+
`github.event` object structure differs per event type. Note that `yaml-syntax-060`
|
|
42
|
+
covers the object filter `.*` operator on null — this entry covers field-level null
|
|
43
|
+
comparisons in `if:` and `env:` contexts.
|
|
44
|
+
fix: |
|
|
45
|
+
Guard all `github.event.pull_request.*` access with an event type check:
|
|
46
|
+
|
|
47
|
+
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
|
|
48
|
+
|
|
49
|
+
For env variables that should only apply on PR events, set them conditionally:
|
|
50
|
+
use a step to export the variable only when running under a pull_request trigger,
|
|
51
|
+
or use separate jobs per event type.
|
|
52
|
+
|
|
53
|
+
For the fork guard pattern, use `github.event_name == 'pull_request' &&
|
|
54
|
+
github.event.pull_request.head.repo.fork` as a combined check rather than relying
|
|
55
|
+
on the fork field being non-null.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Guard PR context access with event type check in if: condition"
|
|
59
|
+
code: |
|
|
60
|
+
jobs:
|
|
61
|
+
check-draft:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- name: Skip if draft PR (only meaningful on PR events)
|
|
65
|
+
# Without the event_name guard, draft is "" on push — comparison silently fails
|
|
66
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
|
|
67
|
+
run: echo "Proceeding — not a draft PR"
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Export PR-specific context safely in multi-event workflows"
|
|
70
|
+
code: |
|
|
71
|
+
on: [push, pull_request]
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
deploy:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- name: Export PR metadata (PR events only)
|
|
78
|
+
if: github.event_name == 'pull_request'
|
|
79
|
+
run: |
|
|
80
|
+
echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
|
81
|
+
echo "IS_DRAFT=${{ github.event.pull_request.draft }}" >> $GITHUB_ENV
|
|
82
|
+
|
|
83
|
+
- name: Deploy
|
|
84
|
+
run: |
|
|
85
|
+
echo "Branch: ${{ github.ref_name }}"
|
|
86
|
+
# Use PR_NUMBER only after confirming event_name == pull_request above
|
|
87
|
+
prevention:
|
|
88
|
+
- "In multi-event workflows, always guard `github.event.pull_request.*` access with `github.event_name == 'pull_request'`"
|
|
89
|
+
- "Run actionlint on workflow files — it detects context availability mismatches per event type"
|
|
90
|
+
- "Test workflows manually for both push and pull_request trigger types to verify that conditional logic works as expected"
|
|
91
|
+
- "Prefer separate jobs or separate workflow files per event type rather than a single workflow handling multiple events with shared conditions"
|
|
92
|
+
docs:
|
|
93
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows"
|
|
94
|
+
label: "GitHub Docs: Events that trigger workflows — context availability per event"
|
|
95
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#github-context"
|
|
96
|
+
label: "GitHub Docs: Contexts — github.event object structure"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
id: triggers-066
|
|
2
|
+
title: '`pull_request` Default Event Types Exclude `edited` — PR Title, Body, and Base Branch Changes Do Not Trigger CI'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull_request
|
|
7
|
+
- event-types
|
|
8
|
+
- edited
|
|
9
|
+
- silent
|
|
10
|
+
- title-body
|
|
11
|
+
- default-types
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'types:\s*\[.*edited.*\]'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Workflow not triggered on pull_request edited event"
|
|
17
|
+
root_cause: |
|
|
18
|
+
The `pull_request` event defaults to `types: [opened, synchronize, reopened]`.
|
|
19
|
+
The `edited` type — which fires when the PR title, body (description), base branch,
|
|
20
|
+
or milestone is modified — is NOT included in the defaults.
|
|
21
|
+
|
|
22
|
+
Workflows configured as `on: pull_request:` without an explicit `types:` key do NOT
|
|
23
|
+
run when a developer:
|
|
24
|
+
- Corrects a typo in the PR title
|
|
25
|
+
- Adds required information to the PR description (e.g., issue reference, testing notes)
|
|
26
|
+
- Changes the PR target base branch to a different branch
|
|
27
|
+
|
|
28
|
+
This creates a common silent failure: PR validation workflows that enforce title
|
|
29
|
+
conventions (Conventional Commits, Jira ticket format), required description sections,
|
|
30
|
+
or base branch policies run correctly on initial PR open and on new commits — but they
|
|
31
|
+
silently skip when the developer edits the PR title or description to fix a violation.
|
|
32
|
+
The developer believes CI re-ran and passed, but the workflow never executed.
|
|
33
|
+
|
|
34
|
+
This follows the same pattern as `labeled` and `ready_for_review` not being in
|
|
35
|
+
pull_request defaults — both are documented but frequently overlooked.
|
|
36
|
+
fix: |
|
|
37
|
+
Explicitly declare the types your workflow responds to. Add `edited` to the types list
|
|
38
|
+
for any workflow that validates or reacts to PR metadata:
|
|
39
|
+
|
|
40
|
+
on:
|
|
41
|
+
pull_request:
|
|
42
|
+
types: [opened, synchronize, reopened, edited]
|
|
43
|
+
|
|
44
|
+
Only include the types you actually need. Adding `edited` to workflows that don't use
|
|
45
|
+
PR metadata (title, body, base) causes unnecessary runs.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Include 'edited' in types to trigger on PR title and body changes"
|
|
49
|
+
code: |
|
|
50
|
+
on:
|
|
51
|
+
pull_request:
|
|
52
|
+
types: [opened, synchronize, reopened, edited]
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: "Workflow that validates PR title only on open and edit events"
|
|
55
|
+
code: |
|
|
56
|
+
on:
|
|
57
|
+
pull_request:
|
|
58
|
+
types: [opened, edited] # Only metadata-relevant events — no need for synchronize
|
|
59
|
+
|
|
60
|
+
jobs:
|
|
61
|
+
validate-title:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- name: Check PR title format
|
|
65
|
+
env:
|
|
66
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
67
|
+
run: |
|
|
68
|
+
echo "Validating title: $PR_TITLE"
|
|
69
|
+
if [[ ! "$PR_TITLE" =~ ^(feat|fix|chore|docs|refactor|test|ci)\: ]]; then
|
|
70
|
+
echo "ERROR: PR title must follow Conventional Commits format"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
prevention:
|
|
74
|
+
- "Always explicitly list all required `types` in pull_request triggers — never rely on defaults for validation workflows"
|
|
75
|
+
- "Test PR validation workflows end-to-end by editing the PR title and verifying the workflow re-runs"
|
|
76
|
+
- "Add a comment above the `on:` block documenting which event types the workflow responds to and why"
|
|
77
|
+
- "Refer to the full list of pull_request event types in GitHub docs when creating new PR workflows"
|
|
78
|
+
docs:
|
|
79
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request"
|
|
80
|
+
label: "GitHub Docs: Events that trigger workflows — pull_request"
|
|
81
|
+
- url: "https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request"
|
|
82
|
+
label: "GitHub Docs: Webhook events — pull_request event payload and types"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
id: triggers-065
|
|
2
|
+
title: '`[skip ci]` and `[skip actions]` Commit Message Flags Silently Skip All Triggered Workflows'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- skip-ci
|
|
7
|
+
- commit-message
|
|
8
|
+
- workflow-skip
|
|
9
|
+
- silent
|
|
10
|
+
- push
|
|
11
|
+
- pull_request
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: '\[skip[ -]ci\]'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: '\[ci[ -]skip\]'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: '\[skip actions\]'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "[skip ci]"
|
|
21
|
+
- "[ci skip]"
|
|
22
|
+
- "[no ci]"
|
|
23
|
+
- "[skip actions]"
|
|
24
|
+
- "[actions skip]"
|
|
25
|
+
root_cause: |
|
|
26
|
+
GitHub Actions inspects the HEAD commit message (and all commits in a push batch) for
|
|
27
|
+
skip directives: [skip ci], [ci skip], [no ci], [skip actions], and [actions skip].
|
|
28
|
+
When any directive is found, GitHub skips ALL workflows triggered by that push or its
|
|
29
|
+
associated pull_request events — completely silently.
|
|
30
|
+
|
|
31
|
+
No workflow run is created, no status check is posted to the commit, no email or
|
|
32
|
+
notification is sent, and the PR shows "No checks" or prior checks disappear without
|
|
33
|
+
explanation. There is no indicator in the UI that workflows were intentionally skipped.
|
|
34
|
+
|
|
35
|
+
Common causes of unintended skips:
|
|
36
|
+
- Automated commits from bots (changelog updates, version bumps) that include [skip ci]
|
|
37
|
+
are squash-merged and the directive ends up in the merge commit message
|
|
38
|
+
- Dependabot or Renovate PRs that carry skip flags from upstream commit messages
|
|
39
|
+
- Developers testing locally add [skip ci] to a commit and forget to remove it before
|
|
40
|
+
the final push to a shared branch
|
|
41
|
+
- CI automation that auto-amends commit messages and inadvertently includes the pattern
|
|
42
|
+
fix: |
|
|
43
|
+
Remove the skip directive from the commit message. To re-trigger CI without rewriting
|
|
44
|
+
history, create an empty commit (a commit with no file changes) and upload it — this
|
|
45
|
+
creates a new push event without any skip directive.
|
|
46
|
+
|
|
47
|
+
If an automated process is adding skip flags unintentionally, update the automation's
|
|
48
|
+
commit template to exclude them. If the skip is intentional for one workflow but not
|
|
49
|
+
others, note that there is no per-workflow opt-out — the skip is global across all
|
|
50
|
+
workflows triggered by that push.
|
|
51
|
+
|
|
52
|
+
To control workflow execution without commit message flags, prefer path filters
|
|
53
|
+
(on.push.paths) or branch filters (on.push.branches) for selective CI execution.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: "Use path filters instead of commit-message skip flags to reduce unnecessary runs"
|
|
57
|
+
code: |
|
|
58
|
+
on:
|
|
59
|
+
push:
|
|
60
|
+
branches: [main]
|
|
61
|
+
paths:
|
|
62
|
+
- 'src/**'
|
|
63
|
+
- 'tests/**'
|
|
64
|
+
- '!docs/**' # Ignore documentation-only changes without needing [skip ci]
|
|
65
|
+
- language: yaml
|
|
66
|
+
label: "Add workflow_dispatch as a manual fallback trigger if push was silently skipped"
|
|
67
|
+
code: |
|
|
68
|
+
on:
|
|
69
|
+
push:
|
|
70
|
+
branches: [main]
|
|
71
|
+
workflow_dispatch: # Allows manual re-trigger if a skip-flagged push missed CI
|
|
72
|
+
prevention:
|
|
73
|
+
- "Audit any automation that commits to your repository and verify it does not include [skip ci] unless intentional"
|
|
74
|
+
- "Establish a team convention: [skip ci] skips ALL workflows — prefer path filters for selective skipping"
|
|
75
|
+
- "Review squash-merge commit messages before merging PRs whose titles contain skip directives"
|
|
76
|
+
- "Check the GitHub Actions tab for missing runs rather than assuming a test passed silently"
|
|
77
|
+
- "Use `on: workflow_dispatch` as a manual safety valve to re-run CI on any branch"
|
|
78
|
+
docs:
|
|
79
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs"
|
|
80
|
+
label: "GitHub Docs: Skipping workflow runs"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
id: yaml-syntax-063
|
|
2
|
+
title: 'YAML Syntax Error in Workflow File Silently Hides Workflow from Actions UI and Disables `workflow_dispatch`'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- yaml-syntax
|
|
7
|
+
- workflow-dispatch
|
|
8
|
+
- ui-hidden
|
|
9
|
+
- silent
|
|
10
|
+
- parse-error
|
|
11
|
+
- debugging
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'invalid workflow file'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'You have an error in your yaml syntax'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "You have an error in your YAML syntax on line N"
|
|
19
|
+
- "No workflow file found for event"
|
|
20
|
+
- "invalid workflow file: .github/workflows/X.yml"
|
|
21
|
+
root_cause: |
|
|
22
|
+
When a GitHub Actions workflow file contains a YAML syntax error or schema validation
|
|
23
|
+
error, GitHub silently stops showing the workflow in the Actions tab and removes any
|
|
24
|
+
`workflow_dispatch` "Run workflow" button associated with it. No email, notification,
|
|
25
|
+
annotation, or repository-level banner is surfaced to warn the owner. The workflow
|
|
26
|
+
simply disappears from the UI.
|
|
27
|
+
|
|
28
|
+
Error categories that cause silent hiding:
|
|
29
|
+
- Standard YAML parse errors: incorrect indentation, unquoted colons in values,
|
|
30
|
+
duplicate keys, or tab characters used in place of spaces
|
|
31
|
+
- Missing required workflow keys: `runs-on` on a job, `steps` on a job, `on:` trigger
|
|
32
|
+
- Unrecognized keys that fail schema validation (e.g., typos in top-level keys)
|
|
33
|
+
- Invalid expression syntax inside `${{ }}` blocks
|
|
34
|
+
- Workflow exceeding GitHub's size limit (~500 KB)
|
|
35
|
+
|
|
36
|
+
This is distinct from the "workflow_dispatch button missing because the workflow file
|
|
37
|
+
is not on the default branch" issue. That error requires the file to be absent from
|
|
38
|
+
the default branch; this error occurs even when the file IS on the default branch but
|
|
39
|
+
is syntactically or structurally invalid.
|
|
40
|
+
|
|
41
|
+
Developers spend significant time investigating why CI "stopped running" or why the
|
|
42
|
+
dispatch button "disappeared" without realizing the workflow file has an error — because
|
|
43
|
+
GitHub provides no proactive notification.
|
|
44
|
+
fix: |
|
|
45
|
+
Validate workflow YAML before every change:
|
|
46
|
+
|
|
47
|
+
1. Use actionlint locally to catch both YAML parse errors and GitHub Actions-specific
|
|
48
|
+
schema errors before the change reaches the default branch.
|
|
49
|
+
2. Use the GitHub web editor — it highlights YAML parse errors inline as you type.
|
|
50
|
+
3. Check the repository Actions tab: workflows with parse errors may show a yellow
|
|
51
|
+
warning triangle, or they may simply be absent from the list.
|
|
52
|
+
4. Query the REST API to verify workflow state:
|
|
53
|
+
GET /repos/{owner}/{repo}/actions/workflows
|
|
54
|
+
Workflows missing from the response or with state "disabled_manually" may indicate
|
|
55
|
+
a parse failure on the default branch.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Add actionlint to CI to catch workflow YAML errors before they reach the default branch"
|
|
59
|
+
code: |
|
|
60
|
+
name: Lint Workflows
|
|
61
|
+
on:
|
|
62
|
+
pull_request:
|
|
63
|
+
paths:
|
|
64
|
+
- '.github/workflows/**'
|
|
65
|
+
jobs:
|
|
66
|
+
lint:
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
- name: Run actionlint
|
|
71
|
+
uses: rhysd/actionlint@latest
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "Correct structure for a minimal valid workflow file"
|
|
74
|
+
code: |
|
|
75
|
+
name: My Workflow # Optional but recommended
|
|
76
|
+
on: # Required: at least one trigger
|
|
77
|
+
push:
|
|
78
|
+
branches: [main]
|
|
79
|
+
jobs: # Required: at least one job
|
|
80
|
+
build: # Job ID
|
|
81
|
+
runs-on: ubuntu-latest # Required: runner label
|
|
82
|
+
steps: # Required: at least one step
|
|
83
|
+
- uses: actions/checkout@v4
|
|
84
|
+
prevention:
|
|
85
|
+
- "Install the actionlint VS Code extension to get inline YAML validation while editing workflow files"
|
|
86
|
+
- "Add a pull_request workflow that runs actionlint on all files under .github/workflows/ to catch errors before merging"
|
|
87
|
+
- "If workflow_dispatch button disappears, immediately check the Actions tab for parse error indicators"
|
|
88
|
+
- "Use GitHub's web editor for quick edits — it provides inline YAML validation"
|
|
89
|
+
- "Never use tab characters for indentation in YAML files — use spaces (2-space indent is standard)"
|
|
90
|
+
docs:
|
|
91
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-syntax-for-github-actions"
|
|
92
|
+
label: "GitHub Docs: Workflow syntax for GitHub Actions"
|
|
93
|
+
- url: "https://github.com/rhysd/actionlint"
|
|
94
|
+
label: "actionlint — Static checker for GitHub Actions workflow files"
|
package/package.json
CHANGED