@htekdev/actions-debugger 1.0.99 → 1.0.101
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/caching-artifacts/caching-artifacts-056.yml +113 -0
- package/errors/caching-artifacts/caching-artifacts-057.yml +98 -0
- package/errors/known-unsolved/known-unsolved-055.yml +124 -0
- package/errors/permissions-auth/permissions-auth-058.yml +101 -0
- package/errors/permissions-auth/permissions-auth-059.yml +136 -0
- package/errors/permissions-auth/permissions-auth-060.yml +115 -0
- package/errors/runner-environment/runner-environment-169.yml +88 -0
- package/errors/runner-environment/runner-environment-170.yml +93 -0
- package/errors/silent-failures/github-event-pull-request-null-on-non-pr-events.yml +96 -0
- package/errors/silent-failures/silent-failures-091.yml +94 -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/triggers/triggers-067.yml +134 -0
- package/errors/yaml-syntax/yaml-syntax-064.yml +119 -0
- package/errors/yaml-syntax/yaml-syntax-error-hides-workflow-from-actions-ui.yml +94 -0
- package/package.json +1 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
id: permissions-auth-059
|
|
2
|
+
title: 'GITHUB_TOKEN expires after 24 hours on self-hosted runners — long-running jobs fail with "GITHUB_TOKEN has expired"'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- GITHUB_TOKEN
|
|
7
|
+
- token-expiry
|
|
8
|
+
- self-hosted-runner
|
|
9
|
+
- long-running-jobs
|
|
10
|
+
- 24-hour-limit
|
|
11
|
+
- authentication
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'GITHUB_TOKEN has expired'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Unable to extend GITHUB_TOKEN expiration'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'token.*expired.*self.hosted'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Unable to extend GITHUB_TOKEN expiration time due to: GITHUB_TOKEN has expired."
|
|
21
|
+
- "Error: fatal: unable to access 'https://github.com/...': The requested URL returned error: 403"
|
|
22
|
+
root_cause: |
|
|
23
|
+
`GITHUB_TOKEN` is an installation access token issued at the START of each job. Its
|
|
24
|
+
lifetime is tied to the job execution, with a hard cap determined by the runner type:
|
|
25
|
+
|
|
26
|
+
- **GitHub-hosted runners**: `GITHUB_TOKEN` lives for up to 6 hours (matching the maximum
|
|
27
|
+
job execution time). On hosted runners this limit is never the issue because the job
|
|
28
|
+
itself can't run longer than 6 hours.
|
|
29
|
+
|
|
30
|
+
- **Self-hosted runners**: Jobs can run for up to 5 days, but `GITHUB_TOKEN` can only be
|
|
31
|
+
refreshed for up to **24 hours**. If a job on a self-hosted runner exceeds 24 hours of
|
|
32
|
+
runtime, any subsequent GitHub API call, `git push`, `gh` CLI invocation, or action that
|
|
33
|
+
uses `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}` will fail with a 401/403
|
|
34
|
+
authentication error.
|
|
35
|
+
|
|
36
|
+
The specific error message is:
|
|
37
|
+
"Unable to extend GITHUB_TOKEN expiration time due to: GITHUB_TOKEN has expired."
|
|
38
|
+
|
|
39
|
+
This is particularly common in:
|
|
40
|
+
- Large test suites or build pipelines that process massive monorepos
|
|
41
|
+
- ML/data pipelines that process large datasets sequentially
|
|
42
|
+
- Long-running deployment or migration jobs that need GitHub API access at the end
|
|
43
|
+
- Jobs with retries that collectively exceed 24 hours
|
|
44
|
+
|
|
45
|
+
Note: `actions/create-github-app-token` GitHub App tokens have a similar but shorter
|
|
46
|
+
1-hour expiry — see permissions-auth-046 for that pattern.
|
|
47
|
+
fix: |
|
|
48
|
+
Several approaches, in order of recommendation:
|
|
49
|
+
|
|
50
|
+
1. **Split the job into smaller jobs** — Break the long-running job into multiple shorter
|
|
51
|
+
jobs connected by `needs:` dependencies. Each job gets its own fresh `GITHUB_TOKEN`.
|
|
52
|
+
|
|
53
|
+
2. **Use a GitHub App token** — Use `actions/create-github-app-token` to generate tokens
|
|
54
|
+
mid-job as needed, or generate a fresh token at the point in the job where you need it.
|
|
55
|
+
|
|
56
|
+
3. **Use a PAT (Personal Access Token)** — Store a long-lived PAT in repository or
|
|
57
|
+
organization secrets and use it in place of `GITHUB_TOKEN` for the API calls at the
|
|
58
|
+
end of the long-running job. PATs do not expire in 24 hours (they expire based on the
|
|
59
|
+
PAT expiration date you set). Drawback: PATs are tied to a specific user account.
|
|
60
|
+
|
|
61
|
+
4. **Use a machine user PAT** — Create a dedicated machine account and use its PAT.
|
|
62
|
+
This decouples the secret from any individual developer's account.
|
|
63
|
+
fix_code:
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: 'Problem: long-running self-hosted job uses GITHUB_TOKEN after 24+ hours'
|
|
66
|
+
code: |
|
|
67
|
+
jobs:
|
|
68
|
+
long-build:
|
|
69
|
+
runs-on: [self-hosted, large-runner]
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- name: Run 26-hour data processing
|
|
73
|
+
run: ./scripts/process_all_data.sh # takes ~26 hours
|
|
74
|
+
|
|
75
|
+
# FAILS: GITHUB_TOKEN has expired by the time this step runs
|
|
76
|
+
- name: Upload results to GitHub
|
|
77
|
+
env:
|
|
78
|
+
GH_TOKEN: ${{ github.token }}
|
|
79
|
+
run: gh release upload v1.0 ./output/*.tar.gz
|
|
80
|
+
- language: yaml
|
|
81
|
+
label: 'Fix: split into jobs so each gets a fresh GITHUB_TOKEN'
|
|
82
|
+
code: |
|
|
83
|
+
jobs:
|
|
84
|
+
process-data:
|
|
85
|
+
runs-on: [self-hosted, large-runner]
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
- run: ./scripts/process_all_data.sh
|
|
89
|
+
- uses: actions/upload-artifact@v4
|
|
90
|
+
with:
|
|
91
|
+
name: output-files
|
|
92
|
+
path: ./output/
|
|
93
|
+
|
|
94
|
+
upload-release:
|
|
95
|
+
needs: process-data
|
|
96
|
+
runs-on: ubuntu-latest # hosted runner with fresh token
|
|
97
|
+
permissions:
|
|
98
|
+
contents: write
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/download-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: output-files
|
|
103
|
+
path: ./output/
|
|
104
|
+
# GITHUB_TOKEN is fresh — just issued for this new job
|
|
105
|
+
- name: Upload to release
|
|
106
|
+
env:
|
|
107
|
+
GH_TOKEN: ${{ github.token }}
|
|
108
|
+
run: gh release upload v1.0 ./output/*.tar.gz
|
|
109
|
+
- language: yaml
|
|
110
|
+
label: 'Fix: use stored PAT when splitting jobs is not feasible'
|
|
111
|
+
code: |
|
|
112
|
+
jobs:
|
|
113
|
+
long-build:
|
|
114
|
+
runs-on: [self-hosted, large-runner]
|
|
115
|
+
steps:
|
|
116
|
+
- uses: actions/checkout@v4
|
|
117
|
+
- run: ./scripts/process_all_data.sh
|
|
118
|
+
|
|
119
|
+
# Use a PAT stored in secrets — does not expire in 24 hours
|
|
120
|
+
- name: Upload results (using PAT)
|
|
121
|
+
env:
|
|
122
|
+
GH_TOKEN: ${{ secrets.MACHINE_USER_PAT }} # PAT, not github.token
|
|
123
|
+
run: gh release upload v1.0 ./output/*.tar.gz
|
|
124
|
+
prevention:
|
|
125
|
+
- "Design self-hosted runner jobs to complete within 24 hours, or split them into smaller jobs connected by `needs:`."
|
|
126
|
+
- "If a job genuinely requires >24 hours of runtime, use a PAT or GitHub App token for API calls instead of `GITHUB_TOKEN`."
|
|
127
|
+
- "Add a monitoring step that logs elapsed job time — alert if a job approaches 20+ hours."
|
|
128
|
+
- "Avoid using `GITHUB_TOKEN` for API calls near the end of jobs that are known to run close to the 24-hour limit."
|
|
129
|
+
- "For GitHub-hosted runners this is not an issue — the job execution limit (6 hours) is shorter than the token lifetime."
|
|
130
|
+
docs:
|
|
131
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#about-the-github_token-secret'
|
|
132
|
+
label: 'GitHub Docs: GITHUB_TOKEN — effective maximum lifetime'
|
|
133
|
+
- url: 'https://stackoverflow.com/questions/75602556/how-can-i-use-a-github-token-for-more-than-24-hours'
|
|
134
|
+
label: 'Stack Overflow: How to use GITHUB_TOKEN for more than 24 hours'
|
|
135
|
+
- url: 'https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits'
|
|
136
|
+
label: 'GitHub Docs: Self-hosted runner usage limits (5-day job limit)'
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: permissions-auth-060
|
|
2
|
+
title: 'Fork Pull Request Workflows Cannot Access Repository Secrets — Secrets Context Is Empty'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- fork
|
|
7
|
+
- pull-request
|
|
8
|
+
- secrets
|
|
9
|
+
- security
|
|
10
|
+
- workflow-permissions
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'secrets\.[\w_]+.*empty|secrets context.*fork'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
error_messages:
|
|
15
|
+
- "Error: Input required and not supplied: token"
|
|
16
|
+
- "HttpError: Bad credentials"
|
|
17
|
+
- "Error: Resource not accessible by integration"
|
|
18
|
+
root_cause: |
|
|
19
|
+
GitHub's security model prevents workflows triggered by `pull_request` events from
|
|
20
|
+
fork repositories from accessing repository secrets. This applies to ALL non-Dependabot
|
|
21
|
+
external contributor forks — not just Dependabot.
|
|
22
|
+
|
|
23
|
+
When a fork PR workflow runs:
|
|
24
|
+
- All `secrets.*` values resolve to empty string `''`
|
|
25
|
+
- `secrets.GITHUB_TOKEN` is replaced with a read-only token scoped only to the fork
|
|
26
|
+
repository (cannot write to the upstream repo, cannot access packages, etc.)
|
|
27
|
+
- Environment secrets are also unavailable
|
|
28
|
+
- Fine-grained PATs stored as secrets are unavailable
|
|
29
|
+
|
|
30
|
+
This is intentional — allowing forks to read secrets would enable malicious PRs to
|
|
31
|
+
exfiltrate credentials.
|
|
32
|
+
|
|
33
|
+
Common failure patterns:
|
|
34
|
+
- `actions/setup-node` with `registry-url` + `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` → 401 Unauthorized
|
|
35
|
+
- `docker/login-action` with `${{ secrets.DOCKERHUB_TOKEN }}` → login fails silently, image push 403
|
|
36
|
+
- `aws-actions/configure-aws-credentials` with role ARN from secrets → empty role, OIDC fallback
|
|
37
|
+
- Custom actions reading a `token:` input wired to `${{ secrets.MY_TOKEN }}` → action receives ''
|
|
38
|
+
and may throw "Input required and not supplied: token"
|
|
39
|
+
|
|
40
|
+
The fork PR restriction also applies to `workflow_dispatch` when triggered from a fork,
|
|
41
|
+
and to `check_suite`/`check_run` events from fork PRs.
|
|
42
|
+
|
|
43
|
+
Note: `pull_request_target` DOES have access to secrets because it runs in the context
|
|
44
|
+
of the BASE repository — but this introduces a different security risk (running untrusted
|
|
45
|
+
code with secret access) that requires careful mitigation.
|
|
46
|
+
fix: |
|
|
47
|
+
For CI checks that don't need secrets (lint, unit tests, build validation), no change
|
|
48
|
+
is needed — the read-only GITHUB_TOKEN is sufficient.
|
|
49
|
+
|
|
50
|
+
For steps that require secrets:
|
|
51
|
+
|
|
52
|
+
1. Gate secret-requiring steps on `github.event.pull_request.head.repo.fork == false`
|
|
53
|
+
to skip them on fork PRs gracefully.
|
|
54
|
+
2. Use `pull_request_target` + a separate privileged job for publishing/deploying,
|
|
55
|
+
but ALWAYS check out from `github.event.pull_request.head.sha` explicitly and
|
|
56
|
+
never run untrusted code in the same job.
|
|
57
|
+
3. For package publishing, only publish from base branch pushes (not from PRs at all).
|
|
58
|
+
fix_code:
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: "Skip secret-requiring steps on fork PRs gracefully"
|
|
61
|
+
code: |
|
|
62
|
+
jobs:
|
|
63
|
+
build:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
|
|
68
|
+
- name: Run tests (works on fork PRs)
|
|
69
|
+
run: npm test
|
|
70
|
+
|
|
71
|
+
- name: Publish coverage report (skip on fork PRs)
|
|
72
|
+
if: github.event.pull_request.head.repo.fork == false
|
|
73
|
+
env:
|
|
74
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
75
|
+
run: npx codecov
|
|
76
|
+
- language: yaml
|
|
77
|
+
label: "Split fork-safe CI from privileged deployment using workflow_run"
|
|
78
|
+
code: |
|
|
79
|
+
# workflow: ci.yml — runs on all PRs including forks (no secrets needed)
|
|
80
|
+
on:
|
|
81
|
+
pull_request:
|
|
82
|
+
jobs:
|
|
83
|
+
test:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- run: npm ci && npm test
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
# workflow: publish-coverage.yml — runs after ci.yml completes (has secrets)
|
|
91
|
+
on:
|
|
92
|
+
workflow_run:
|
|
93
|
+
workflows: ['CI']
|
|
94
|
+
types: [completed]
|
|
95
|
+
jobs:
|
|
96
|
+
coverage:
|
|
97
|
+
if: github.event.workflow_run.conclusion == 'success'
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
steps:
|
|
100
|
+
- name: Publish coverage
|
|
101
|
+
env:
|
|
102
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
103
|
+
run: echo "Publishing coverage for run ${{ github.event.workflow_run.id }}"
|
|
104
|
+
prevention:
|
|
105
|
+
- "Design CI workflows to not require secrets for the build/test steps — secrets should only be needed for publish/deploy"
|
|
106
|
+
- "Check `github.event.pull_request.head.repo.fork` before any step that uses secrets to provide a clear skip message"
|
|
107
|
+
- "Do not use `pull_request_target` as a shortcut to get secrets — it runs untrusted fork code with base repo privileges, creating a severe injection risk"
|
|
108
|
+
- "Use `workflow_run` to chain a privileged follow-up workflow that runs in base repo context after fork CI succeeds"
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections"
|
|
111
|
+
label: "GitHub Docs — Security hardening: fork PR and secret access"
|
|
112
|
+
- url: "https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request"
|
|
113
|
+
label: "GitHub Docs — pull_request event: fork limitations"
|
|
114
|
+
- url: "https://securitylab.github.com/research/github-actions-preventing-pwn-requests/"
|
|
115
|
+
label: "GitHub Security Lab — Preventing pwn requests (pull_request_target risks)"
|
|
@@ -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,93 @@
|
|
|
1
|
+
id: runner-environment-170
|
|
2
|
+
title: 'windows-latest Migration to Windows Server 2025 Removes MSVC v142 (VS 2019) Toolchain'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- windows
|
|
7
|
+
- msvc
|
|
8
|
+
- cmake
|
|
9
|
+
- msbuild
|
|
10
|
+
- toolchain
|
|
11
|
+
- migration
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'MSB8020.*v142.*cannot be found'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'build tools for v142.*Platform Toolset.*v142.*cannot be found'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'CMAKE_GENERATOR_TOOLSET.*v142.*not found'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "MSBUILD : error MSB8020: The build tools for v142 (Platform Toolset = 'v142') cannot be found."
|
|
21
|
+
- "CMake Error: The CMAKE_CXX_COMPILER: cl.exe is not able to compile a simple test program."
|
|
22
|
+
- "error MSB8020: The build tools for v142 (Platform Toolset = 'v142') cannot be found."
|
|
23
|
+
root_cause: |
|
|
24
|
+
Starting November 2024, `windows-latest` was migrated to Windows Server 2025 with
|
|
25
|
+
Visual Studio 2022 17.12 as the default build environment. Windows Server 2025 runner
|
|
26
|
+
images do not include the Visual Studio 2019 build tools (MSVC v142 / toolset 14.2).
|
|
27
|
+
|
|
28
|
+
Projects that explicitly request the VS 2019 toolchain via any of these mechanisms fail:
|
|
29
|
+
- MSBuild `<PlatformToolset>v142</PlatformToolset>` in .vcxproj files
|
|
30
|
+
- CMake `-T v142` flag or `set(CMAKE_GENERATOR_TOOLSET v142)` in CMakeLists.txt
|
|
31
|
+
- MSBuild command-line flag `/p:PlatformToolset=v142`
|
|
32
|
+
- Visual Studio solution files pinned to VS 2019 (toolset version 142)
|
|
33
|
+
|
|
34
|
+
Windows Server 2022 runners (`windows-2022`) continue to offer both VS 2019
|
|
35
|
+
(v142) and VS 2022 (v143) toolchains in parallel. After the `windows-latest`
|
|
36
|
+
migration, workflows that did not pin to `windows-2022` and relied on v142
|
|
37
|
+
break silently on runners already migrated while appearing to work in older
|
|
38
|
+
runner pools.
|
|
39
|
+
|
|
40
|
+
The migration timeline:
|
|
41
|
+
- Nov 2024: windows-latest begins pointing to Windows Server 2025 (partial rollout)
|
|
42
|
+
- Early 2025: Full rollout; all new `windows-latest` queue jobs use WS 2025
|
|
43
|
+
|
|
44
|
+
Reference: https://github.com/actions/runner-images/issues/10751
|
|
45
|
+
fix: |
|
|
46
|
+
Choose one of the following approaches:
|
|
47
|
+
|
|
48
|
+
1. Upgrade to the v143 (VS 2022) toolchain — recommended for long-term compatibility.
|
|
49
|
+
2. Pin to `windows-2022` runner if you cannot migrate immediately.
|
|
50
|
+
3. Install the VS 2019 build tools component manually (slow, increases job time).
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: "Option A: upgrade toolchain to v143 (VS 2022) in CMakeLists.txt"
|
|
54
|
+
code: |
|
|
55
|
+
# In CMakeLists.txt — remove explicit toolset pinning
|
|
56
|
+
# cmake_minimum_required(VERSION 3.20)
|
|
57
|
+
# project(MyProject)
|
|
58
|
+
# Previously had: set(CMAKE_GENERATOR_TOOLSET "v142")
|
|
59
|
+
# Remove or update to:
|
|
60
|
+
# set(CMAKE_GENERATOR_TOOLSET "v143") # or omit to use default
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: "Option B: pin to windows-2022 to keep v142 support"
|
|
63
|
+
code: |
|
|
64
|
+
jobs:
|
|
65
|
+
build:
|
|
66
|
+
runs-on: windows-2022 # has both v142 and v143 available
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
- name: Build with CMake
|
|
70
|
+
run: cmake -B build -T v142 && cmake --build build
|
|
71
|
+
- language: yaml
|
|
72
|
+
label: "Option C: install VS 2019 build tools on windows-latest (slow)"
|
|
73
|
+
code: |
|
|
74
|
+
jobs:
|
|
75
|
+
build:
|
|
76
|
+
runs-on: windows-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- name: Install VS 2019 Build Tools
|
|
80
|
+
run: |
|
|
81
|
+
choco install visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Component.VC.v142.x86.x64" -y
|
|
82
|
+
prevention:
|
|
83
|
+
- "Audit all .vcxproj and CMakeLists.txt files for explicit v142 toolset references before migrating to windows-latest"
|
|
84
|
+
- "Pin to `windows-2022` in CI to preserve VS 2019 toolchain availability while planning an upgrade"
|
|
85
|
+
- "Watch https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md for current pre-installed toolchain versions"
|
|
86
|
+
- "Use `vswhere` in a pre-build step to detect installed VS components and fail fast with a descriptive error"
|
|
87
|
+
docs:
|
|
88
|
+
- url: "https://github.com/actions/runner-images/issues/10751"
|
|
89
|
+
label: "runner-images #10751 — windows-latest migration to Windows Server 2025"
|
|
90
|
+
- url: "https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md"
|
|
91
|
+
label: "Windows Server 2025 runner image README — pre-installed software"
|
|
92
|
+
- url: "https://learn.microsoft.com/en-us/cpp/build/cmake-presets-vs?view=msvc-170"
|
|
93
|
+
label: "Microsoft Docs — CMake toolset configuration"
|
|
@@ -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,94 @@
|
|
|
1
|
+
id: silent-failures-091
|
|
2
|
+
title: '`github.ref_name` Returns Ephemeral Merge Ref (`123/merge`) on `pull_request` Trigger, Not Branch Name'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- github-context
|
|
7
|
+
- ref-name
|
|
8
|
+
- pull-request
|
|
9
|
+
- docker
|
|
10
|
+
- branch-name
|
|
11
|
+
- deployment
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: '\d+/merge'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "invalid reference format"
|
|
17
|
+
- "invalid tag format"
|
|
18
|
+
root_cause: |
|
|
19
|
+
When a workflow is triggered by a `pull_request` event, GitHub creates an ephemeral
|
|
20
|
+
merge commit that merges the PR head into the base branch. The context variables for
|
|
21
|
+
this run reflect the merge ref, not the source branch:
|
|
22
|
+
|
|
23
|
+
- `github.ref` = `refs/pull/123/merge`
|
|
24
|
+
- `github.ref_name` = `123/merge`
|
|
25
|
+
- `github.sha` = SHA of the ephemeral merge commit
|
|
26
|
+
- `github.head_ref` = `feature-branch-name` (the actual PR branch)
|
|
27
|
+
- `github.base_ref` = `main` (the target branch)
|
|
28
|
+
|
|
29
|
+
Using `github.ref_name` in contexts that expect a branch name silently produces
|
|
30
|
+
the merge ref string `123/merge` instead:
|
|
31
|
+
|
|
32
|
+
- **Docker image tags**: `docker.io/myapp:123/merge` — Docker rejects the forward slash
|
|
33
|
+
as an invalid tag character (`invalid reference format` error), OR worse, interprets
|
|
34
|
+
`123` as a registry name.
|
|
35
|
+
- **Deployment environment names**: The environment is created as `123/merge` rather
|
|
36
|
+
than the branch name, causing routing rules to miss the deployment.
|
|
37
|
+
- **Branch-based conditional logic**: `if: github.ref_name == 'feature-foo'` always
|
|
38
|
+
evaluates to false on pull_request events.
|
|
39
|
+
- **Artifact naming**: Artifacts uploaded with the ref_name contain forward slashes,
|
|
40
|
+
causing path traversal issues on some download handlers.
|
|
41
|
+
|
|
42
|
+
`github.head_ref` is only populated for `pull_request` and `pull_request_target`
|
|
43
|
+
events, making branch detection logic that works on push but silently fails on PR
|
|
44
|
+
events harder to diagnose.
|
|
45
|
+
fix: |
|
|
46
|
+
Use the correct context variable for each event type:
|
|
47
|
+
|
|
48
|
+
- For the PR's source branch name: `github.head_ref` (only set on pull_request events)
|
|
49
|
+
- For push events' branch name: `github.ref_name`
|
|
50
|
+
- For a branch name that works across both event types, use a conditional expression.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: "Unified branch name across push and pull_request events"
|
|
54
|
+
code: |
|
|
55
|
+
jobs:
|
|
56
|
+
build:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- name: Determine branch name
|
|
60
|
+
id: branch
|
|
61
|
+
run: |
|
|
62
|
+
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
63
|
+
echo "name=${{ github.head_ref }}" >> "$GITHUB_OUTPUT"
|
|
64
|
+
else
|
|
65
|
+
echo "name=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
- name: Build and tag Docker image
|
|
69
|
+
run: |
|
|
70
|
+
# Sanitize slashes for use in image tags
|
|
71
|
+
TAG=$(echo "${{ steps.branch.outputs.name }}" | tr '/' '-')
|
|
72
|
+
docker build -t "myapp:${TAG}" .
|
|
73
|
+
- language: yaml
|
|
74
|
+
label: "Expression-based branch name (no shell step needed)"
|
|
75
|
+
code: |
|
|
76
|
+
env:
|
|
77
|
+
BRANCH_NAME: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}
|
|
78
|
+
|
|
79
|
+
jobs:
|
|
80
|
+
deploy:
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
environment: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}
|
|
83
|
+
steps:
|
|
84
|
+
- run: echo "Deploying branch ${{ env.BRANCH_NAME }}"
|
|
85
|
+
prevention:
|
|
86
|
+
- "Never use `github.ref_name` directly in Docker image tags — always sanitize forward slashes with `tr '/' '-'` or similar"
|
|
87
|
+
- "Test branch-detection expressions with both `push` and `pull_request` triggers in a dry-run workflow"
|
|
88
|
+
- "Use `github.head_ref` for PR branch name and `github.ref_name` for push branch name — they are NOT interchangeable"
|
|
89
|
+
- "Add an explicit check: if `github.ref_name` contains a `/`, the workflow is running on a pull_request or merge ref, not a regular branch"
|
|
90
|
+
docs:
|
|
91
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context"
|
|
92
|
+
label: "GitHub Docs — github context reference"
|
|
93
|
+
- url: "https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request"
|
|
94
|
+
label: "GitHub Docs — pull_request event"
|