@htekdev/actions-debugger 1.0.100 → 1.0.102

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.
@@ -0,0 +1,113 @@
1
+ id: caching-artifacts-056
2
+ title: 'actions/cache v1 and v2 deprecated — workflows fail hard with "automatically failed" error'
3
+ category: caching-artifacts
4
+ severity: error
5
+ tags:
6
+ - actions/cache
7
+ - deprecated
8
+ - v1
9
+ - v2
10
+ - hard-failure
11
+ - breaking-change
12
+ - upgrade
13
+ patterns:
14
+ - regex: 'automatically failed.*deprecated version.*actions/cache'
15
+ flags: 'i'
16
+ - regex: 'deprecated version of.{0,30}actions/cache'
17
+ flags: 'i'
18
+ - regex: 'actions/cache@v[12]\b'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "This request has been automatically failed because it uses a deprecated version of `actions/cache: 6849a6489940f00c2f30c0fb92c6274307ccb58a`"
22
+ - "Error: This request has been automatically failed because it uses a deprecated version of `actions/cache`"
23
+ root_cause: |
24
+ GitHub announced in December 2024 (GitHub Changelog) that `actions/cache` versions 1 and 2
25
+ were deprecated and would be hard-failed starting February 1, 2025. Any workflow still
26
+ pinned to `actions/cache@v1` or `actions/cache@v2` (or to a full SHA that resolves to
27
+ those versions) now fails immediately with a hard error — no cache operation is attempted
28
+ and the entire step fails.
29
+
30
+ The error message includes the SHA of the deprecated action version:
31
+ "This request has been automatically failed because it uses a deprecated version
32
+ of `actions/cache: <SHA>`"
33
+
34
+ This is a hard failure, not a graceful degradation — the step exit code is non-zero and
35
+ causes the job to fail unless `continue-on-error: true` is set on the step (which is
36
+ not a recommended fix).
37
+
38
+ Common reasons workflows are still on v1/v2:
39
+ 1. The workflow was written years ago and never updated
40
+ 2. The action is referenced by a full commit SHA from before v3 was released
41
+ 3. A reusable workflow or composite action calls an outdated dependency that pins v1/v2
42
+ 4. `Dependabot` is not enabled on the repo for GitHub Actions
43
+
44
+ Note: `actions/cache@v3` and `actions/cache@v4` are both supported. V4 is the current
45
+ recommended version.
46
+ fix: |
47
+ Upgrade all `actions/cache` references from v1 or v2 to v4 (recommended) or v3.
48
+ Search your repository for `actions/cache@v1` and `actions/cache@v2` references,
49
+ including in nested composite actions and reusable workflows.
50
+
51
+ If you are migrating from v3 to v4, note the breaking changes documented in the
52
+ v3-to-v4 migration guide (see caching-artifacts-009). The v1/v2 to v4 migration
53
+ follows the same v3-to-v4 guidance since v3 and v4 share the same input API except
54
+ for `save-always`.
55
+
56
+ Enable Dependabot for GitHub Actions updates to automatically receive future
57
+ deprecation PRs.
58
+ fix_code:
59
+ - language: yaml
60
+ label: 'Problem: workflow pinned to deprecated actions/cache v1 or v2'
61
+ code: |
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+
65
+ # FAILS: v1 and v2 are deprecated — hard failure since Feb 1, 2025
66
+ - uses: actions/cache@v1
67
+ with:
68
+ path: ~/.npm
69
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
70
+
71
+ # Also fails — v2 is deprecated
72
+ - uses: actions/cache@v2
73
+ with:
74
+ path: ~/.npm
75
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
76
+ - language: yaml
77
+ label: 'Fix: upgrade to actions/cache v4 (recommended)'
78
+ code: |
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+
82
+ # CORRECT: use v4 (or v3 if v3-to-v4 migration is not yet done)
83
+ - uses: actions/cache@v4
84
+ with:
85
+ path: ~/.npm
86
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
87
+ restore-keys: |
88
+ ${{ runner.os }}-npm-
89
+ - language: yaml
90
+ label: 'Dependabot config to automatically receive Actions version updates'
91
+ code: |
92
+ # .github/dependabot.yml
93
+ version: 2
94
+ updates:
95
+ - package-ecosystem: github-actions
96
+ directory: /
97
+ schedule:
98
+ interval: weekly
99
+ # Dependabot will open PRs to upgrade actions/cache@v1/v2 → v4 automatically
100
+ prevention:
101
+ - "Enable Dependabot for GitHub Actions in `.github/dependabot.yml` to receive automated upgrade PRs."
102
+ - "Search your `.github/workflows/` directory for `actions/cache@v1` and `actions/cache@v2` before the deprecation deadline."
103
+ - "Check composite actions and reusable workflows — they may internally call deprecated cache versions."
104
+ - "Audit SHA-pinned action references to ensure the SHA does not point to a v1/v2 release."
105
+ docs:
106
+ - url: 'https://github.blog/changelog/2024-12-05-notice-of-upcoming-releases-and-breaking-changes-for-github-actions/#actions-cache-v1-v2-and-actions-toolkit-cache-package-closing-down'
107
+ label: 'GitHub Changelog Dec 2024: actions/cache v1/v2 deprecation notice'
108
+ - url: 'https://github.com/actions/cache/discussions/1510'
109
+ label: 'actions/cache Discussion #1510: deprecated version hard failure reports'
110
+ - url: 'https://github.com/actions/cache'
111
+ label: 'actions/cache repository — upgrade instructions'
112
+ - url: 'https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot'
113
+ label: 'GitHub Docs: Keeping your actions up to date with Dependabot'
@@ -0,0 +1,98 @@
1
+ id: caching-artifacts-057
2
+ title: '`actions/cache` Never Refreshes Cached Content — Cache Keys Are Immutable Once Stored'
3
+ category: caching-artifacts
4
+ severity: silent-failure
5
+ tags:
6
+ - cache
7
+ - stale
8
+ - immutable
9
+ - cache-hit
10
+ - refresh
11
+ - node-modules
12
+ patterns:
13
+ - regex: 'cache-hit.*true.*post.*skip'
14
+ flags: 'i'
15
+ error_messages:
16
+ - "Cache hit occurred on the primary key, not saving cache."
17
+ - "Cache hit occurred on the primary key runner-os-node-"
18
+ root_cause: |
19
+ `actions/cache` uses immutable cache keys. Once a cache entry is stored for a given
20
+ key, it cannot be updated or overwritten — any subsequent run that produces an exact
21
+ key match will restore the cached content and the post step will NOT save a new cache
22
+ entry because one already exists for that key.
23
+
24
+ This means cached content (node_modules, pip packages, Maven artifacts, etc.) is
25
+ frozen at the point of first creation and will not reflect package updates until:
26
+ - The cache entry expires (GitHub-hosted caches expire after 7 days of no access)
27
+ - The cache key changes (e.g., lockfile hash changes)
28
+ - The cache entry is manually deleted via the GitHub UI or API
29
+
30
+ Common misconception: developers expect that running `npm ci` inside a cached
31
+ node_modules hit will update the cache if packages changed. It won't. The cache key
32
+ (usually `${{ hashFiles('package-lock.json') }}`) must change for a new cache to be
33
+ saved.
34
+
35
+ Related gotcha: if restore-keys produce a partial hit (different key prefix), the
36
+ post step DOES save a new cache — but for the full key, not the partial restore key.
37
+ This creates unexpected cache churn from the developer's perspective where cache hit
38
+ rate is high but actual content is outdated.
39
+
40
+ This is intentional GitHub design but frequently misunderstood, leading to:
41
+ - Stale node_modules with old transitive dependencies
42
+ - pip packages that don't receive security patches within 7 days
43
+ - Build artifacts compiled against old toolchain versions
44
+ fix: |
45
+ To force periodic cache refresh without changing your lockfile, use one of these patterns:
46
+
47
+ 1. Include a date-based component in the cache key (weekly rotation).
48
+ 2. Use `actions/cache/restore` + `actions/cache/save` with explicit control of when
49
+ to save (allows saving even on hits).
50
+ 3. Delete the cache manually via GitHub UI (Actions > Caches) or the API when you
51
+ need an immediate refresh.
52
+ fix_code:
53
+ - language: yaml
54
+ label: "Weekly-rotating cache key to prevent indefinitely stale caches"
55
+ code: |
56
+ - name: Get week number for cache rotation
57
+ id: date
58
+ run: echo "week=$(date +'%Y-%U')" >> "$GITHUB_OUTPUT"
59
+
60
+ - name: Cache node_modules (refreshes weekly)
61
+ uses: actions/cache@v4
62
+ with:
63
+ path: ~/.npm
64
+ key: ${{ runner.os }}-node-${{ steps.date.outputs.week }}-${{ hashFiles('**/package-lock.json') }}
65
+ restore-keys: |
66
+ ${{ runner.os }}-node-${{ steps.date.outputs.week }}-
67
+ ${{ runner.os }}-node-
68
+ - language: yaml
69
+ label: "Separate restore + save for explicit cache update control"
70
+ code: |
71
+ - name: Restore cache
72
+ id: cache-restore
73
+ uses: actions/cache/restore@v4
74
+ with:
75
+ path: ~/.npm
76
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
77
+ restore-keys: ${{ runner.os }}-node-
78
+
79
+ - run: npm ci
80
+
81
+ - name: Save updated cache (always saves, even on hit)
82
+ uses: actions/cache/save@v4
83
+ if: always()
84
+ with:
85
+ path: ~/.npm
86
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ github.run_id }}
87
+ prevention:
88
+ - "Understand that GitHub cache keys are immutable — a key that already exists in the cache will never be overwritten"
89
+ - "Use time-based or run-id-based cache key suffixes when cached content must be refreshed more frequently than lockfile changes"
90
+ - "Monitor cache age in the GitHub UI (Actions > Caches) and manually purge entries that appear stale"
91
+ - "For security-sensitive dependencies, prefer short cache TTLs via run-id keys or disable caching entirely"
92
+ docs:
93
+ - url: "https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows"
94
+ label: "GitHub Docs — Caching dependencies to speed up workflows"
95
+ - url: "https://github.com/actions/cache/blob/main/tips-and-workarounds.md"
96
+ label: "actions/cache tips and workarounds"
97
+ - url: "https://github.com/actions/cache/issues/1380"
98
+ label: "actions/cache #1380 — cache never updates on key hit"
@@ -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,84 @@
1
+ id: runner-environment-172
2
+ title: 'Homebrew 4.x triggers auto-update on every brew install, adding 1-3 minutes to macOS CI'
3
+ category: runner-environment
4
+ severity: warning
5
+ tags:
6
+ - macos
7
+ - homebrew
8
+ - brew
9
+ - slow-ci
10
+ - performance
11
+ - HOMEBREW_NO_AUTO_UPDATE
12
+ patterns:
13
+ - regex: '==> Auto-updated Homebrew!|Updating Homebrew\.\.\.|Auto-update took'
14
+ flags: 'i'
15
+ - regex: 'Fetching https://formulae\.brew\.sh/api/(formula|cask)\.jws\.json'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "==> Auto-updated Homebrew!"
19
+ - "Updating Homebrew..."
20
+ - "Fetching https://formulae.brew.sh/api/formula.jws.json"
21
+ - "This operation has taken more than 5 minutes"
22
+ root_cause: |
23
+ Homebrew 4.0 (released February 2023) replaced its previous approach of cloning the
24
+ homebrew-core and homebrew-cask Git repositories with downloading pre-computed JSON API
25
+ responses from formulae.brew.sh. While this reduced the on-disk size of the Homebrew
26
+ installation, Homebrew retained its auto-update behavior: by default, any brew install,
27
+ brew upgrade, or brew reinstall command triggers an auto-update check if the local
28
+ formula cache is older than HOMEBREW_AUTO_UPDATE_SECS (default: 300 seconds = 5 minutes).
29
+
30
+ On GitHub-hosted macOS runners, every fresh runner starts with a Homebrew installation
31
+ that has not been updated recently, so the first brew install in any job always triggers
32
+ a full API fetch from formulae.brew.sh. This fetch downloads large JSON manifests for
33
+ formula and cask databases and typically adds 1-3 minutes to the job. For workflows with
34
+ multiple parallel matrix jobs or multiple brew install calls, this overhead compounds.
35
+
36
+ Homebrew 4.x also added HOMEBREW_AUTO_UPDATE_SECS and related env vars to control this
37
+ behavior, but the default settings cause every fresh runner to update on first use.
38
+ fix: |
39
+ Set HOMEBREW_NO_AUTO_UPDATE=1 as a workflow environment variable to disable automatic
40
+ updates entirely. The macOS runner image ships a recent-enough Homebrew installation for
41
+ most use cases. If you need the absolute latest formula versions for a specific tool,
42
+ run a single explicit brew update at the start of the job.
43
+
44
+ Also set HOMEBREW_NO_INSTALL_CLEANUP=1 to prevent cleanup passes that extend install time.
45
+ fix_code:
46
+ - language: yaml
47
+ label: 'Disable Homebrew auto-update at workflow level (recommended)'
48
+ code: |
49
+ env:
50
+ HOMEBREW_NO_AUTO_UPDATE: '1'
51
+ HOMEBREW_NO_INSTALL_CLEANUP: '1'
52
+
53
+ jobs:
54
+ build:
55
+ runs-on: macos-latest
56
+ steps:
57
+ - name: Install build dependencies
58
+ run: brew install ninja cmake
59
+ - language: yaml
60
+ label: 'Run one explicit update then suppress auto-update per job'
61
+ code: |
62
+ jobs:
63
+ build:
64
+ runs-on: macos-latest
65
+ env:
66
+ HOMEBREW_NO_AUTO_UPDATE: '1'
67
+ HOMEBREW_NO_INSTALL_CLEANUP: '1'
68
+ steps:
69
+ - name: Update Homebrew (once, explicit)
70
+ run: brew update
71
+ - name: Install tools
72
+ run: brew install ninja cmake
73
+ prevention:
74
+ - 'Set HOMEBREW_NO_AUTO_UPDATE=1 and HOMEBREW_NO_INSTALL_CLEANUP=1 as top-level workflow env vars'
75
+ - 'Use actions/setup-* official actions instead of brew install when available (setup-python, setup-node, etc.)'
76
+ - 'Cache brew downloads using actions/cache with a key derived from a Brewfile or explicit package list'
77
+ - 'Set HOMEBREW_AUTO_UPDATE_SECS=86400 to limit auto-updates to at most once per day if you need periodic updates'
78
+ docs:
79
+ - url: 'https://docs.brew.sh/Manpage#environment'
80
+ label: 'Homebrew environment variables — HOMEBREW_NO_AUTO_UPDATE'
81
+ - url: 'https://brew.sh/2023/02/16/homebrew-4.0.0/'
82
+ label: 'Homebrew 4.0.0 release notes — JSON API migration'
83
+ - url: 'https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md'
84
+ label: 'macOS 15 runner image — preinstalled Homebrew version'
@@ -0,0 +1,74 @@
1
+ id: runner-environment-171
2
+ title: 'Python 3.12 removes deprecated ast.Str, ast.Num, ast.NameConstant on ubuntu-24.04 — older linters crash'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - ubuntu-24.04
7
+ - python
8
+ - python-3.12
9
+ - ast
10
+ - linting
11
+ - pylint
12
+ - bandit
13
+ patterns:
14
+ - regex: 'AttributeError: module ''ast'' has no attribute ''(Str|Num|NameConstant|Bytes)'''
15
+ flags: 'i'
16
+ - regex: 'ImportError.*pylint|cannot import name.*astroid|AttributeError.*ast\.(Str|Num)'
17
+ flags: 'i'
18
+ error_messages:
19
+ - "AttributeError: module 'ast' has no attribute 'Str'"
20
+ - "AttributeError: module 'ast' has no attribute 'Num'"
21
+ - "AttributeError: module 'ast' has no attribute 'NameConstant'"
22
+ - "AttributeError: module 'ast' has no attribute 'Bytes'"
23
+ root_cause: |
24
+ Ubuntu 24.04 ships Python 3.12 as the default system Python (/usr/bin/python3).
25
+ Python 3.12 permanently removed several deprecated AST node types that were soft-deprecated
26
+ since Python 3.8 and slated for removal in 3.12: ast.Str, ast.Num, ast.NameConstant,
27
+ ast.Bytes, and the legacy constant-value wrapper nodes.
28
+
29
+ Many popular static analysis tools directly referenced these internal AST nodes for
30
+ backward compatibility with Python 2 and early Python 3 code. Affected tools include:
31
+ - pylint < 3.0 (uses astroid which references ast.Str/ast.Num for constant folding)
32
+ - astroid < 3.0 (core dependency of pylint; crash on import with Python 3.12)
33
+ - bandit < 1.8.0 (security linter; uses ast.Str for string literal detection)
34
+ - flake8-bugbear < 23.x (some plugin versions reference ast.Num directly)
35
+ - pyflakes < 3.0 (uses ast.Str for format string analysis)
36
+
37
+ Workflows that install these linters without pinning minimum versions fail immediately
38
+ when the runner migrates from ubuntu-22.04 (Python 3.10) to ubuntu-24.04 (Python 3.12).
39
+ The error surfaces as an AttributeError during tool import, before any code is analyzed.
40
+ fix: |
41
+ Upgrade the affected analysis tools to Python 3.12-compatible versions:
42
+ - pylint: upgrade to 3.0+ (requires astroid >= 3.0 simultaneously)
43
+ - bandit: upgrade to 1.8.0+
44
+ - flake8: upgrade to 7.0+ with compatible plugin versions
45
+
46
+ If an immediate upgrade is not feasible, pin the runner to ubuntu-22.04 (Python 3.10)
47
+ temporarily while the upgrade is planned. Ubuntu 22.04 runner support continues until
48
+ at least April 2027.
49
+ fix_code:
50
+ - language: yaml
51
+ label: 'Upgrade linting tools to Python 3.12-compatible versions'
52
+ code: |
53
+ - name: Install Python linters (3.12 compatible)
54
+ run: pip install 'pylint>=3.0' 'astroid>=3.0' 'bandit>=1.8.0' 'flake8>=7.0'
55
+ - language: yaml
56
+ label: 'Temporary workaround — pin to ubuntu-22.04 (Python 3.10)'
57
+ code: |
58
+ jobs:
59
+ lint:
60
+ runs-on: ubuntu-22.04 # Python 3.10 — avoids ast.Str removal in 3.12
61
+ prevention:
62
+ - 'Pin minimum versions for linting tools in requirements-dev.txt or pyproject.toml'
63
+ - 'Use actions/setup-python to control the Python version explicitly rather than relying on system Python'
64
+ - 'Test your CI toolchain against Python 3.12 before migrating to ubuntu-24.04'
65
+ - 'Review the Python 3.12 changelog for all removed deprecated features before upgrading'
66
+ docs:
67
+ - url: 'https://docs.python.org/3.12/whatsnew/3.12.html#removed'
68
+ label: 'Python 3.12 Removed Features'
69
+ - url: 'https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md'
70
+ label: 'Ubuntu 24.04 runner image software listing'
71
+ - url: 'https://pylint.readthedocs.io/en/stable/whatsnew/3.0/summary.html'
72
+ label: 'Pylint 3.0 migration and Python 3.12 compatibility notes'
73
+ - url: 'https://github.com/PyCQA/bandit/releases/tag/1.8.0'
74
+ label: 'bandit 1.8.0 release notes — Python 3.12 AST compatibility'
@@ -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,78 @@
1
+ id: runner-environment-173
2
+ title: 'actions/setup-go with EOL Go versions (1.21, 1.22) not in runner tool cache — slow downloads or failures'
3
+ category: runner-environment
4
+ severity: warning
5
+ tags:
6
+ - setup-go
7
+ - go
8
+ - golang
9
+ - eol
10
+ - tool-cache
11
+ - go-version
12
+ patterns:
13
+ - regex: 'go version [\d.]+ is not in the tool-cache.*Falling back to download|go\d+\.\d+\.?\d*.*not found.*tool.cache'
14
+ flags: 'i'
15
+ - regex: 'Downloading go[\d.]+.*from https://dl\.google\.com/go'
16
+ flags: 'i'
17
+ - regex: 'Failed to download Go from|Unable to find Go version.*in cache'
18
+ flags: 'i'
19
+ error_messages:
20
+ - "go version 1.21.x is not in the tool-cache. Falling back to downloading from https://dl.google.com/go"
21
+ - "go version 1.22.x is not in the tool-cache. Falling back to downloading from https://dl.google.com/go"
22
+ - "Failed to download Go from https://dl.google.com/go"
23
+ - "Unable to find Go version 1.21 in cache"
24
+ root_cause: |
25
+ GitHub removes end-of-life Go versions from the runner image tool cache after their
26
+ official support window closes. Go follows a two-release support policy: only the two
27
+ most recent major releases receive security updates. Once a version is EOL, it is no
28
+ longer pre-installed on new runner images.
29
+
30
+ Affected versions as of 2025-2026:
31
+ - Go 1.21 — EOL February 6, 2024 (removed from runner tool cache ~Q2 2024)
32
+ - Go 1.22 — EOL August 6, 2025 (removed from runner tool cache ~Q4 2025)
33
+
34
+ When actions/setup-go requests a version not in the tool cache, it falls back to
35
+ downloading the Go toolchain from dl.google.com/go. This fallback:
36
+ 1. Adds 30-90 seconds of download time per job (depending on network speed)
37
+ 2. Fails entirely in self-hosted runners without internet access or strict outbound
38
+ firewall rules blocking dl.google.com
39
+ 3. May fail intermittently if the Google CDN experiences transient issues
40
+
41
+ Workflows with many parallel matrix jobs (e.g., matrix of Go versions or OS targets)
42
+ multiply this overhead: 20 parallel jobs each downloading Go = 20 concurrent CDN requests.
43
+ fix: |
44
+ Upgrade to a currently-supported Go version. As of 2025-2026, the two supported releases
45
+ are Go 1.23 and Go 1.24. Both are pre-cached in the runner tool cache and resolve
46
+ instantly without any network download.
47
+
48
+ Use the go-version-file input to read the version from go.mod, ensuring your CI always
49
+ matches your project's declared Go version.
50
+ fix_code:
51
+ - language: yaml
52
+ label: 'Pin to a supported Go version (no tool-cache miss)'
53
+ code: |
54
+ - name: Set up Go
55
+ uses: actions/setup-go@v5
56
+ with:
57
+ go-version: '1.24' # or '1.23' — both are in the runner tool cache
58
+ cache: true
59
+ - language: yaml
60
+ label: 'Use go-version-file to read version from go.mod'
61
+ code: |
62
+ - name: Set up Go
63
+ uses: actions/setup-go@v5
64
+ with:
65
+ go-version-file: 'go.mod' # reads `go 1.24` directive from go.mod
66
+ cache: true
67
+ prevention:
68
+ - 'Keep go-version in your workflows aligned with the go directive in go.mod'
69
+ - 'Upgrade Go versions proactively before EOL — the Go team announces EOL dates 6 months in advance'
70
+ - 'Use go-version-file: go.mod so your CI automatically follows your module declaration'
71
+ - 'Monitor https://go.dev/doc/devel/release for maintenance status of Go releases'
72
+ docs:
73
+ - url: 'https://go.dev/doc/devel/release'
74
+ label: 'Go release history and maintenance policy'
75
+ - url: 'https://github.com/actions/setup-go'
76
+ label: 'actions/setup-go — go-version and go-version-file inputs'
77
+ - url: 'https://github.com/actions/runner-images'
78
+ label: 'GitHub runner images — preinstalled tool versions'
@@ -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"
@@ -0,0 +1,134 @@
1
+ id: triggers-067
2
+ title: 'workflow_run types:[completed] fires prematurely with conclusion:null before referenced workflow finishes'
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - workflow_run
7
+ - conclusion
8
+ - null
9
+ - premature-trigger
10
+ - race-condition
11
+ - completed
12
+ patterns:
13
+ - regex: 'workflow_run\.conclusion.*null'
14
+ flags: 'i'
15
+ - regex: 'github\.event\.workflow_run\.conclusion'
16
+ flags: 'i'
17
+ - regex: 'conclusion.*randomly.*fail'
18
+ flags: 'i'
19
+ error_messages:
20
+ - "github.event.workflow_run.conclusion returned null — job skipped unexpectedly"
21
+ - "Unable to extend GITHUB_TOKEN expiration time due to: GITHUB_TOKEN has expired."
22
+ root_cause: |
23
+ The `workflow_run` event with `types: [completed]` is documented to fire only after the
24
+ referenced workflow finishes running. However, a known race condition in GitHub Actions'
25
+ event delivery (tracked in actions/runner#4035) causes the event to fire a second time
26
+ BEFORE the workflow has fully completed — specifically before GitHub has written the final
27
+ `conclusion` value to the workflow run record.
28
+
29
+ When this premature trigger fires, `github.event.workflow_run.conclusion` evaluates to
30
+ `null` (or an empty string `""`). Any job guarded with:
31
+
32
+ if: github.event.workflow_run.conclusion == 'success'
33
+
34
+ will evaluate to `false` and be skipped, producing a confusing pattern where the
35
+ downstream workflow runs twice — once skipped, once (correctly) executed.
36
+
37
+ GitHub Support confirmed this as a platform-side delay: the `conclusion` field remains
38
+ `null` while GitHub's backend is still updating the workflow run record. The event
39
+ is dispatched before the record is fully consistent.
40
+
41
+ This affects all workflows that use `on.workflow_run` with `types: [completed]` and
42
+ check `github.event.workflow_run.conclusion` to gate job execution.
43
+ fix: |
44
+ Two mitigations are available:
45
+
46
+ 1. **Guard with `conclusion != ''`** — Add a condition that explicitly rejects null/empty
47
+ `conclusion` values before checking for `'success'`:
48
+
49
+ if: >-
50
+ github.event.workflow_run.conclusion != '' &&
51
+ github.event.workflow_run.conclusion == 'success'
52
+
53
+ 2. **Re-query via REST API** — Instead of trusting `github.event.workflow_run.conclusion`,
54
+ use the Actions REST API to fetch the current workflow run conclusion at step runtime:
55
+
56
+ gh api /repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} \
57
+ --jq '.conclusion'
58
+
59
+ This always returns the up-to-date value, bypassing the event payload race condition.
60
+
61
+ Note: adding a `sleep` between trigger and conclusion check is NOT a reliable fix, as
62
+ the delay between trigger and backend consistency is variable and can exceed seconds.
63
+ fix_code:
64
+ - language: yaml
65
+ label: 'Problem: conclusion check fails on premature null trigger'
66
+ code: |
67
+ on:
68
+ workflow_run:
69
+ workflows: ['CI Build']
70
+ types: [completed]
71
+
72
+ jobs:
73
+ deploy:
74
+ # This randomly evaluates to false when conclusion is null
75
+ if: github.event.workflow_run.conclusion == 'success'
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - run: echo "Deploying..."
79
+ - language: yaml
80
+ label: 'Fix: guard against null conclusion before equality check'
81
+ code: |
82
+ on:
83
+ workflow_run:
84
+ workflows: ['CI Build']
85
+ types: [completed]
86
+
87
+ jobs:
88
+ deploy:
89
+ # Explicitly reject null/empty conclusion to avoid premature-trigger skips
90
+ if: >-
91
+ github.event.workflow_run.conclusion != '' &&
92
+ github.event.workflow_run.conclusion == 'success'
93
+ runs-on: ubuntu-latest
94
+ steps:
95
+ - run: echo "Deploying..."
96
+ - language: yaml
97
+ label: 'Fix: re-query conclusion via REST API for reliable value'
98
+ code: |
99
+ on:
100
+ workflow_run:
101
+ workflows: ['CI Build']
102
+ types: [completed]
103
+
104
+ jobs:
105
+ deploy:
106
+ runs-on: ubuntu-latest
107
+ steps:
108
+ - name: Check conclusion via API
109
+ id: check
110
+ env:
111
+ GH_TOKEN: ${{ github.token }}
112
+ run: |
113
+ conclusion=$(gh api \
114
+ /repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} \
115
+ --jq '.conclusion')
116
+ echo "conclusion=$conclusion" >> "$GITHUB_OUTPUT"
117
+
118
+ - name: Deploy
119
+ if: steps.check.outputs.conclusion == 'success'
120
+ run: echo "Deploying..."
121
+ prevention:
122
+ - "Never rely solely on `github.event.workflow_run.conclusion == 'success'` without a null guard."
123
+ - "Add `github.event.workflow_run.conclusion != ''` as an explicit first condition to reject premature triggers."
124
+ - "If downstream jobs must be fully reliable, re-query conclusion via the REST API rather than reading from the event payload."
125
+ - "Monitor the Actions tab for unexpectedly skipped `workflow_run` triggered runs — they indicate premature triggers."
126
+ docs:
127
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run'
128
+ label: 'GitHub Docs: workflow_run event'
129
+ - url: 'https://github.com/actions/runner/issues/4035'
130
+ label: 'actions/runner#4035: workflow_run triggers before referenced workflow completes'
131
+ - url: 'https://stackoverflow.com/questions/73107307/any-workaround-for-github-actions-workflow-run-conclusion-randomly-failing'
132
+ label: 'Stack Overflow: workflow_run.conclusion randomly failing'
133
+ - url: 'https://github.com/orgs/community/discussions/58929'
134
+ label: 'GitHub Community #58929: github.event.workflow_run.conclusion is always null'
@@ -0,0 +1,113 @@
1
+ id: yaml-syntax-065
2
+ title: 'env context unavailable in defaults.run.working-directory — "Unrecognized named-value: env"'
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - env
7
+ - context
8
+ - defaults
9
+ - working-directory
10
+ - expression
11
+ - context-availability
12
+ patterns:
13
+ - regex: 'Unrecognized named-value: ''env''.*defaults|defaults.*working.directory.*env'
14
+ flags: 'i'
15
+ - regex: 'The workflow is not valid.*defaults\.run\.working-directory.*env\.'
16
+ flags: 'i'
17
+ - regex: 'Unrecognized named-value: ''env''.*position.*working.directory'
18
+ flags: 'i'
19
+ error_messages:
20
+ - "The workflow is not valid. .github/workflows/<workflow>.yml (Line: X, Col: Y): Unrecognized named-value: 'env'. Located at position 1 within expression: env.WORKING_DIR"
21
+ - "Unrecognized named-value: 'env'"
22
+ root_cause: |
23
+ The `env` context is only available during step execution — not at job evaluation time.
24
+ `defaults.run.working-directory` is a job-level field evaluated before any steps run,
25
+ so GitHub Actions rejects references to `env.*` inside it with "Unrecognized named-value: 'env'".
26
+
27
+ This is the same fundamental limitation that affects other job-level fields (if:,
28
+ runs-on, timeout-minutes, continue-on-error) but is less documented for defaults.run.
29
+
30
+ Common patterns that fail:
31
+ - Setting a shared working directory from a workflow-level env variable:
32
+ env:
33
+ APP_DIR: './packages/app'
34
+ jobs:
35
+ build:
36
+ defaults:
37
+ run:
38
+ working-directory: ${{ env.APP_DIR }} # FAILS — env not available here
39
+
40
+ - Reading an env var set in another job:
41
+ jobs:
42
+ setup:
43
+ ...
44
+ build:
45
+ defaults:
46
+ run:
47
+ working-directory: ${{ env.BUILD_DIR }} # FAILS — env from another job not visible
48
+
49
+ The limitation applies to all expressions in defaults.run.working-directory and
50
+ defaults.run.shell at both the workflow level and the job level.
51
+ fix: |
52
+ Replace env context references in defaults.run.working-directory with contexts that are
53
+ available at job evaluation time:
54
+
55
+ 1. Use vars.* (repository or environment variables configured in Settings) for static paths
56
+ 2. Use inputs.* if inside a reusable workflow called with workflow_call
57
+ 3. Set working-directory on each individual step instead of at defaults level
58
+ 4. Use an outputs chain from a prior job if the path is computed dynamically
59
+ fix_code:
60
+ - language: yaml
61
+ label: 'WRONG — env context in defaults.run.working-directory (fails)'
62
+ code: |
63
+ env:
64
+ APP_DIR: './packages/app'
65
+
66
+ jobs:
67
+ build:
68
+ defaults:
69
+ run:
70
+ working-directory: ${{ env.APP_DIR }} # Error: Unrecognized named-value 'env'
71
+ - language: yaml
72
+ label: 'FIX option 1 — use vars context (repository variable)'
73
+ code: |
74
+ # Configure APP_DIR in Settings > Secrets and variables > Variables
75
+ jobs:
76
+ build:
77
+ defaults:
78
+ run:
79
+ working-directory: ${{ vars.APP_DIR }} # repository variable — available at job level
80
+ - language: yaml
81
+ label: 'FIX option 2 — set working-directory on each step directly'
82
+ code: |
83
+ jobs:
84
+ build:
85
+ env:
86
+ APP_DIR: './packages/app'
87
+ steps:
88
+ - name: Build
89
+ run: npm run build
90
+ working-directory: ${{ env.APP_DIR }} # env IS available at step level
91
+ - name: Test
92
+ run: npm test
93
+ working-directory: ${{ env.APP_DIR }}
94
+ - language: yaml
95
+ label: 'FIX option 3 — hardcode the path in defaults.run'
96
+ code: |
97
+ jobs:
98
+ build:
99
+ defaults:
100
+ run:
101
+ working-directory: ./packages/app # literal path, no context expression needed
102
+ prevention:
103
+ - 'Only use vars.*, github.*, inputs.*, or needs.*.outputs.* in job-level expressions — env.* is never available at job level'
104
+ - 'For shared working directories, prefer literal paths in defaults.run.working-directory over expressions'
105
+ - 'If the path must be dynamic, use step-level working-directory instead of defaults.run'
106
+ - 'Use actionlint to validate workflows locally — it catches env context misuse in job-level fields'
107
+ docs:
108
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/setting-default-values-for-jobs'
109
+ label: 'GitHub Actions — Setting default values for jobs (defaults.run)'
110
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability'
111
+ label: 'GitHub Actions context availability by workflow key'
112
+ - url: 'https://github.com/rhysd/actionlint'
113
+ label: 'actionlint — static checker that validates context availability'
@@ -0,0 +1,119 @@
1
+ id: yaml-syntax-064
2
+ title: 'strategy.matrix.include/exclude entry value specified as array causes "A sequence was not expected"'
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - matrix
7
+ - include
8
+ - exclude
9
+ - array-value
10
+ - sequence
11
+ - validation-error
12
+ - strategy
13
+ patterns:
14
+ - regex: 'A sequence was not expected'
15
+ flags: 'i'
16
+ - regex: 'matrix\.(include|exclude).*\[.*\]'
17
+ flags: 'i'
18
+ - regex: 'strategy.*matrix.*include.*sequence'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "Error: The template is not valid. .github/workflows/workflow.yml (Line: 12, Col: 18): A sequence was not expected"
22
+ - "A sequence was not expected"
23
+ root_cause: |
24
+ In a `strategy.matrix`, the top-level matrix dimensions accept arrays of scalars:
25
+
26
+ php: ['8.2', '8.3', '8.4'] # valid — array of scalars
27
+
28
+ However, inside `include:` and `exclude:` entries, ALL values must be scalars
29
+ (string, number, boolean). Arrays are not allowed:
30
+
31
+ include:
32
+ - os: ubuntu-latest
33
+ php: ['8.2'] # INVALID — array not allowed in include/exclude entries
34
+
35
+ This is a common mistake when a developer copies the variable name from the main
36
+ matrix definition (where it IS an array) and pastes it into an `include:` or
37
+ `exclude:` entry without unwrapping it.
38
+
39
+ GitHub Actions' expression evaluator validates the template before execution and
40
+ throws an immediate hard failure:
41
+
42
+ Error: The template is not valid. .github/workflows/X.yml (Line: N, Col: M):
43
+ A sequence was not expected
44
+
45
+ The error points to the line where the array value starts (the `[` character),
46
+ which can be confusing because arrays are valid YAML and valid in other matrix
47
+ contexts. The fix is simply to remove the brackets and use a scalar value.
48
+
49
+ Tracked in rhysd/actionlint#630 (actionlint static analysis issue requesting
50
+ detection of this pattern).
51
+ fix: |
52
+ Replace any array-valued entries inside `include:` or `exclude:` with scalar values.
53
+ Each `include:`/`exclude:` entry is a single-combination filter — it selects or adds
54
+ one specific combination, so the value must be a scalar that matches exactly one item.
55
+
56
+ If you need to include multiple values for a key, write separate `include:` entries,
57
+ one per value.
58
+ fix_code:
59
+ - language: yaml
60
+ label: 'Problem: array value inside include entry — triggers "A sequence was not expected"'
61
+ code: |
62
+ strategy:
63
+ matrix:
64
+ os: [ubuntu-latest, macos-latest]
65
+ php: ['8.3', '8.4', '8.5']
66
+
67
+ include:
68
+ # ERROR: php value is an array — not allowed inside include entries
69
+ - os: ubuntu-latest
70
+ php: ['8.2'] # ← "A sequence was not expected" on this line
71
+
72
+ exclude:
73
+ # ERROR: os value is an array — not allowed inside exclude entries
74
+ - os: [macos-latest]
75
+ php: '8.3' # ← "A sequence was not expected"
76
+ - language: yaml
77
+ label: 'Fix: use scalar values inside include/exclude entries'
78
+ code: |
79
+ strategy:
80
+ matrix:
81
+ os: [ubuntu-latest, macos-latest]
82
+ php: ['8.3', '8.4', '8.5']
83
+
84
+ include:
85
+ # CORRECT: scalar value — adds ubuntu-latest × 8.2 as an extra job
86
+ - os: ubuntu-latest
87
+ php: '8.2' # ← scalar, not array
88
+
89
+ exclude:
90
+ # CORRECT: scalar value — excludes macos-latest × 8.3 combination
91
+ - os: macos-latest
92
+ php: '8.3' # ← scalar, not array
93
+ - language: yaml
94
+ label: 'Multiple include entries for multiple specific combinations'
95
+ code: |
96
+ strategy:
97
+ matrix:
98
+ os: [ubuntu-latest, macos-latest]
99
+ node: [18, 20]
100
+
101
+ include:
102
+ # To include multiple specific combinations, write separate entries:
103
+ - os: ubuntu-latest
104
+ node: 22 # one entry per extra combination
105
+ - os: ubuntu-latest
106
+ node: 23 # another entry for a different combination
107
+ # NOT: node: [22, 23] ← this would fail with "A sequence was not expected"
108
+ prevention:
109
+ - "Values inside `include:` and `exclude:` entries must be scalars — never use array syntax `[...]` there."
110
+ - "When copying matrix variable names from the top-level dimensions to include/exclude entries, remove the brackets."
111
+ - "Use actionlint (github.com/rhysd/actionlint) to statically validate your workflow files before pushing."
112
+ - "If you need multiple combinations, write multiple separate `include:` entries instead of one entry with an array value."
113
+ docs:
114
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#expanding-or-adding-matrix-configurations'
115
+ label: 'GitHub Docs: Expanding or adding matrix configurations'
116
+ - url: 'https://github.com/rhysd/actionlint/issues/630'
117
+ label: 'rhysd/actionlint#630: Detect array values in matrix include/exclude entries'
118
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude'
119
+ label: 'GitHub Docs: jobs.<job_id>.strategy.matrix.include syntax reference'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.100",
3
+ "version": "1.0.102",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. CLI + MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",