@htekdev/actions-debugger 1.0.101 → 1.0.103
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/cache-key-missing-runner-arch-x64-arm64-bleed.yml +101 -0
- package/errors/known-unsolved/matrix-job-limit-256-silent-truncation.yml +116 -0
- package/errors/runner-environment/homebrew-auto-update-macos-ci-slow.yml +84 -0
- package/errors/runner-environment/python312-ast-str-num-removed-ubuntu24.yml +74 -0
- package/errors/runner-environment/setup-go-eol-version-not-in-tool-cache.yml +78 -0
- package/errors/silent-failures/github-event-before-zeros-new-branch.yml +99 -0
- package/errors/silent-failures/pull-request-target-checks-out-base-not-head.yml +97 -0
- package/errors/yaml-syntax/env-context-unavailable-defaults-run-working-directory.yml +113 -0
- package/package.json +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
id: caching-artifacts-058
|
|
2
|
+
title: 'actions/cache key using runner.os bleeds between x64 and ARM64 Linux runners — wrong architecture binaries restored'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- cache-key
|
|
7
|
+
- runner-arch
|
|
8
|
+
- arm64
|
|
9
|
+
- x64
|
|
10
|
+
- architecture
|
|
11
|
+
- cross-arch
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'exec format error'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'cannot execute binary file.*Exec format error'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'ELF.*wrong ELF class'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "bash: /path/to/binary: cannot execute binary file: Exec format error"
|
|
21
|
+
- "error while loading shared libraries: wrong ELF class: ELFCLASS32"
|
|
22
|
+
- "qemu: uncaught target signal 11 (Segmentation fault)"
|
|
23
|
+
root_cause: |
|
|
24
|
+
runner.os returns "Linux" for both ubuntu-24.04 (x86_64) and ubuntu-24.04-arm
|
|
25
|
+
(arm64/aarch64) runners. A cache key built with only ${{ runner.os }} will match
|
|
26
|
+
caches created on EITHER architecture, silently restoring the wrong binaries.
|
|
27
|
+
|
|
28
|
+
Example: a workflow compiles a Rust binary or installs native npm packages
|
|
29
|
+
(esbuild, rollup, sharp) on an x86_64 runner, then the same cache key is used on
|
|
30
|
+
an ARM64 runner. The cached x86_64 ELF binary is restored and immediately fails
|
|
31
|
+
with "Exec format error" — or worse, a segfault with no obvious cause.
|
|
32
|
+
|
|
33
|
+
This affects:
|
|
34
|
+
- Compiled binaries cached in the build layer
|
|
35
|
+
- Native npm modules (esbuild, @swc/core, sharp, canvas)
|
|
36
|
+
- Python packages with C extensions (.so files)
|
|
37
|
+
- Go, Rust, C++ build artifacts
|
|
38
|
+
- Any runner.os-keyed cache shared across an architecture matrix
|
|
39
|
+
|
|
40
|
+
The silent aspect: the cache HITS (green cache-hit output), no error is reported
|
|
41
|
+
until the cached binary is actually executed, often many steps later.
|
|
42
|
+
fix: |
|
|
43
|
+
Add runner.arch to any cache key that stores architecture-dependent content.
|
|
44
|
+
runner.arch returns "X64" or "ARM64" on Linux runners, disambiguating them.
|
|
45
|
+
|
|
46
|
+
Change:
|
|
47
|
+
key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}
|
|
48
|
+
|
|
49
|
+
To:
|
|
50
|
+
key: ${{ runner.os }}-${{ runner.arch }}-build-${{ hashFiles('**/Cargo.lock') }}
|
|
51
|
+
|
|
52
|
+
This applies to all cache keys for compiled artifacts, native modules, or
|
|
53
|
+
any platform-specific binary output. Pure source-code or platform-agnostic
|
|
54
|
+
caches (e.g., Go module downloads, pip source distributions) do not need runner.arch.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'WRONG — runner.os only, bleeds between x64 and ARM64'
|
|
58
|
+
code: |
|
|
59
|
+
- uses: actions/cache@v4
|
|
60
|
+
with:
|
|
61
|
+
path: target/release
|
|
62
|
+
# BAD: runner.os = "Linux" for both x64 and ARM64
|
|
63
|
+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: 'FIX — include runner.arch in cache key'
|
|
66
|
+
code: |
|
|
67
|
+
- uses: actions/cache@v4
|
|
68
|
+
with:
|
|
69
|
+
path: target/release
|
|
70
|
+
# GOOD: runner.arch = "X64" or "ARM64", prevents cross-arch cache hits
|
|
71
|
+
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: 'Full matrix example with architecture-aware cache keys'
|
|
74
|
+
code: |
|
|
75
|
+
jobs:
|
|
76
|
+
build:
|
|
77
|
+
strategy:
|
|
78
|
+
matrix:
|
|
79
|
+
runner: [ubuntu-24.04, ubuntu-24.04-arm]
|
|
80
|
+
runs-on: ${{ matrix.runner }}
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/checkout@v4
|
|
83
|
+
- uses: actions/cache@v4
|
|
84
|
+
with:
|
|
85
|
+
path: |
|
|
86
|
+
~/.cargo/registry
|
|
87
|
+
target/
|
|
88
|
+
key: ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
89
|
+
- run: cargo build --release
|
|
90
|
+
prevention:
|
|
91
|
+
- 'Always include runner.arch in cache keys when the cached content contains compiled or architecture-specific binaries'
|
|
92
|
+
- 'Audit existing cache keys whenever adding ARM64 runners to a workflow that already runs on x86_64'
|
|
93
|
+
- 'Use a restore-key that does NOT include arch only as a fallback, not as a primary key'
|
|
94
|
+
- 'Test fresh (clear cache) runs on each architecture to verify binaries execute correctly'
|
|
95
|
+
docs:
|
|
96
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#runner-context'
|
|
97
|
+
label: 'GitHub Actions runner context — runner.os and runner.arch'
|
|
98
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows'
|
|
99
|
+
label: 'Caching dependencies to speed up workflows'
|
|
100
|
+
- url: 'https://github.com/actions/cache'
|
|
101
|
+
label: 'actions/cache — cache key examples and best practices'
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
id: known-unsolved-056
|
|
2
|
+
title: 'GitHub Actions matrix is silently capped at 256 jobs — excess jobs are dropped with no warning'
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- job-limit
|
|
8
|
+
- 256
|
|
9
|
+
- large-matrix
|
|
10
|
+
- silent-truncation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'matrix.*256|256.*matrix.*job'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'some jobs.*not.*generated.*matrix'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "The matrix contains more jobs than the 256 job limit"
|
|
18
|
+
root_cause: |
|
|
19
|
+
GitHub Actions enforces a hard limit of 256 jobs per matrix expansion. When a
|
|
20
|
+
matrix generates more than 256 combinations, GitHub silently drops all jobs beyond
|
|
21
|
+
the first 256 — no error is raised, no warning appears in the workflow run UI,
|
|
22
|
+
and no notification is sent to the repository owner.
|
|
23
|
+
|
|
24
|
+
The truncation is silent and deterministic: GitHub processes matrix entries in
|
|
25
|
+
the order they are defined and stops at 256. If your matrix has 300 combinations,
|
|
26
|
+
jobs 257–300 simply do not run, but the workflow completes successfully.
|
|
27
|
+
|
|
28
|
+
Common scenarios that hit this limit:
|
|
29
|
+
- Large monorepo test suites that fan out one job per package/service (50+ packages
|
|
30
|
+
× multiple OS/version combinations)
|
|
31
|
+
- Parameterized test sharding where both shard-count and OS are in the matrix
|
|
32
|
+
- Dynamic matrices built from directory listings or API calls
|
|
33
|
+
- Security scanning jobs that matrix over many dependency versions
|
|
34
|
+
|
|
35
|
+
GitHub Actions documentation mentions the 256 limit but does not surface it as
|
|
36
|
+
an error at runtime, making it easy to miss for months.
|
|
37
|
+
|
|
38
|
+
Note: Enterprise plans have the same 256-job limit per matrix. There is no paid
|
|
39
|
+
tier that raises this cap.
|
|
40
|
+
fix: |
|
|
41
|
+
There is no way to raise the 256-job limit. Workarounds:
|
|
42
|
+
|
|
43
|
+
1. Restructure the matrix to combine dimensions:
|
|
44
|
+
Instead of os × version × shard (3×4×30=360), use a pre-computed list of
|
|
45
|
+
only the combinations you actually need (often far fewer than the full product).
|
|
46
|
+
|
|
47
|
+
2. Shard within a single job instead of across jobs:
|
|
48
|
+
Use a single "test" job that runs a subset of tests in each runner process,
|
|
49
|
+
controlled by TOTAL_SHARDS / SHARD_INDEX environment variables, without
|
|
50
|
+
a separate matrix job per shard.
|
|
51
|
+
|
|
52
|
+
3. Split into multiple workflows:
|
|
53
|
+
Use workflow_call to invoke sub-workflows, each handling a subset of jobs.
|
|
54
|
+
Each workflow has its own 256-job budget.
|
|
55
|
+
|
|
56
|
+
4. Use a dynamic include-only matrix:
|
|
57
|
+
Build the matrix list externally (e.g., a script) and output it as JSON,
|
|
58
|
+
then ensure the JSON has at most 256 entries. Add a CI check that fails if
|
|
59
|
+
the list exceeds 255 (leaving headroom for include additions).
|
|
60
|
+
fix_code:
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: 'Pre-compute matrix list to stay under 256 (dynamic matrix pattern)'
|
|
63
|
+
code: |
|
|
64
|
+
jobs:
|
|
65
|
+
setup:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
outputs:
|
|
68
|
+
matrix: ${{ steps.build-matrix.outputs.matrix }}
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
- id: build-matrix
|
|
72
|
+
run: |
|
|
73
|
+
# Build matrix, enforce limit
|
|
74
|
+
MATRIX=$(find packages -name package.json -maxdepth 2 | \
|
|
75
|
+
head -256 | jq -R -s -c 'split("\n") | map(select(. != ""))')
|
|
76
|
+
echo "matrix={\"pkg\":$MATRIX}" >> "$GITHUB_OUTPUT"
|
|
77
|
+
|
|
78
|
+
test:
|
|
79
|
+
needs: setup
|
|
80
|
+
strategy:
|
|
81
|
+
matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- run: echo "Testing ${{ matrix.pkg }}"
|
|
85
|
+
- language: yaml
|
|
86
|
+
label: 'Split large matrix across two workflows via workflow_call'
|
|
87
|
+
code: |
|
|
88
|
+
# .github/workflows/test-batch-a.yml
|
|
89
|
+
on:
|
|
90
|
+
workflow_call:
|
|
91
|
+
jobs:
|
|
92
|
+
test:
|
|
93
|
+
strategy:
|
|
94
|
+
matrix:
|
|
95
|
+
pkg: [svc-a, svc-b, svc-c] # First 128 services
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
steps:
|
|
98
|
+
- run: echo "Testing ${{ matrix.pkg }}"
|
|
99
|
+
|
|
100
|
+
# .github/workflows/ci.yml
|
|
101
|
+
on: [push]
|
|
102
|
+
jobs:
|
|
103
|
+
batch-a:
|
|
104
|
+
uses: ./.github/workflows/test-batch-a.yml
|
|
105
|
+
batch-b:
|
|
106
|
+
uses: ./.github/workflows/test-batch-b.yml
|
|
107
|
+
prevention:
|
|
108
|
+
- 'Monitor your matrix job count as the codebase grows — add a pre-flight check that fails CI if the computed matrix exceeds 255'
|
|
109
|
+
- 'Prefer horizontal test sharding within a single job over per-package matrix expansion for large monorepos'
|
|
110
|
+
- 'Document the matrix budget in your CONTRIBUTING.md so contributors understand the constraint'
|
|
111
|
+
- 'Use include: sparingly — each include entry adds to the 256-job budget'
|
|
112
|
+
docs:
|
|
113
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#about-matrix-strategies'
|
|
114
|
+
label: 'GitHub Actions — About matrix strategies (256-job limit documented)'
|
|
115
|
+
- url: 'https://docs.github.com/en/actions/administering-github-actions/usage-limits-billing-and-administration#usage-limits'
|
|
116
|
+
label: 'GitHub Actions usage limits — job matrix limits'
|
|
@@ -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,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,99 @@
|
|
|
1
|
+
id: silent-failures-092
|
|
2
|
+
title: 'github.event.before is all zeros on first push to a new branch — git diff fails with unknown revision'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- push-event
|
|
7
|
+
- before-sha
|
|
8
|
+
- new-branch
|
|
9
|
+
- git-diff
|
|
10
|
+
- changed-files
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'fatal: ambiguous argument.*0{40}'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'unknown revision.*0{10,}.*working tree'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'fatal: bad object 0{40}'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "fatal: ambiguous argument '0000000000000000000000000000000000000000': unknown revision or path not in the working tree"
|
|
20
|
+
- "fatal: bad object 0000000000000000000000000000000000000000"
|
|
21
|
+
root_cause: |
|
|
22
|
+
When a branch is pushed to GitHub for the first time, there is no previous state.
|
|
23
|
+
GitHub sets github.event.before to the all-zeros SHA (40 zeros) as a sentinel
|
|
24
|
+
meaning "this is a brand-new branch with no prior history."
|
|
25
|
+
|
|
26
|
+
Any workflow step that uses this SHA in git commands fails because 40 zeros is not
|
|
27
|
+
a valid Git object reference. Common patterns that break:
|
|
28
|
+
git diff ${{ github.event.before }} ${{ github.sha }} --name-only
|
|
29
|
+
git log ${{ github.event.before }}..${{ github.sha }}
|
|
30
|
+
|
|
31
|
+
This silently breaks:
|
|
32
|
+
- Changed-file detection scripts that decide which jobs to run
|
|
33
|
+
- Automated changelog generators that compare before→after SHAs
|
|
34
|
+
- CI selective-run logic based on modified paths
|
|
35
|
+
- Commit-message linters that inspect newly added commits only
|
|
36
|
+
|
|
37
|
+
The failure is non-obvious because the workflow may succeed on subsequent pushes
|
|
38
|
+
to the same branch (where before is a real SHA), leading developers to believe
|
|
39
|
+
the issue was transient or environmental.
|
|
40
|
+
fix: |
|
|
41
|
+
Guard against the all-zeros SHA before performing git operations:
|
|
42
|
+
|
|
43
|
+
Option 1 — Skip the step/job on new-branch pushes with an if: condition:
|
|
44
|
+
if: github.event.before != '0000000000000000000000000000000000000000'
|
|
45
|
+
|
|
46
|
+
Option 2 — Fall back to the initial commit when before is zeros:
|
|
47
|
+
BEFORE="${{ github.event.before }}"
|
|
48
|
+
if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
|
|
49
|
+
BEFORE=$(git rev-list --max-parents=0 HEAD)
|
|
50
|
+
fi
|
|
51
|
+
git diff "$BEFORE" "${{ github.sha }}" --name-only
|
|
52
|
+
|
|
53
|
+
Option 3 — Use tj-actions/changed-files which handles this case automatically.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: 'Guard with if: to skip diff on new-branch first push'
|
|
57
|
+
code: |
|
|
58
|
+
jobs:
|
|
59
|
+
diff-check:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
# Skip when there is no before commit (first push to new branch)
|
|
62
|
+
if: github.event.before != '0000000000000000000000000000000000000000'
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
with:
|
|
66
|
+
fetch-depth: 0
|
|
67
|
+
- name: Get changed files
|
|
68
|
+
run: git diff ${{ github.event.before }} ${{ github.sha }} --name-only
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: 'Shell fallback — use initial commit when before SHA is zeros'
|
|
71
|
+
code: |
|
|
72
|
+
- name: Get changed files (handles new branch)
|
|
73
|
+
run: |
|
|
74
|
+
BEFORE="${{ github.event.before }}"
|
|
75
|
+
if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
|
|
76
|
+
# New branch — fall back to root commit
|
|
77
|
+
BEFORE=$(git rev-list --max-parents=0 HEAD)
|
|
78
|
+
fi
|
|
79
|
+
git diff "$BEFORE" "${{ github.sha }}" --name-only
|
|
80
|
+
- language: yaml
|
|
81
|
+
label: 'Use tj-actions/changed-files (handles new-branch automatically)'
|
|
82
|
+
code: |
|
|
83
|
+
- uses: tj-actions/changed-files@v44
|
|
84
|
+
id: changed
|
|
85
|
+
with:
|
|
86
|
+
since_last_remote_commit: true
|
|
87
|
+
- run: echo "${{ steps.changed.outputs.all_changed_files }}"
|
|
88
|
+
prevention:
|
|
89
|
+
- 'Always check github.event.before against the 40-zero sentinel before using it in git commands'
|
|
90
|
+
- 'Use maintained actions such as tj-actions/changed-files that already handle new-branch edge cases'
|
|
91
|
+
- 'Add fetch-depth: 0 to actions/checkout so full git history is available for diff operations'
|
|
92
|
+
- 'Test workflows on a branch that has never had CI run before to catch first-push failures early'
|
|
93
|
+
docs:
|
|
94
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push'
|
|
95
|
+
label: 'GitHub Actions — push event payload (before/after SHA fields)'
|
|
96
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#github-context'
|
|
97
|
+
label: 'GitHub context reference — github.event.before'
|
|
98
|
+
- url: 'https://github.com/tj-actions/changed-files'
|
|
99
|
+
label: 'tj-actions/changed-files — handles new-branch and force-push edge cases'
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
id: silent-failures-093
|
|
2
|
+
title: 'pull_request_target workflow checks out base branch by default — PR head changes are not tested'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull-request-target
|
|
7
|
+
- checkout
|
|
8
|
+
- base-branch
|
|
9
|
+
- head-ref
|
|
10
|
+
- fork-pr
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'pull_request_target.*actions/checkout'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'HEAD is now at.*base.*not.*head'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- "Actions attempted to checkout a merge commit for a pull request but the merge commit was unavailable"
|
|
18
|
+
root_cause: |
|
|
19
|
+
When a workflow is triggered by pull_request_target, the GITHUB_SHA and the default
|
|
20
|
+
checkout ref point to the BASE branch of the PR — not the PR head commit.
|
|
21
|
+
|
|
22
|
+
This is intentional security behavior: pull_request_target runs in the context of
|
|
23
|
+
the base repository (with full secrets access), so GitHub deliberately avoids
|
|
24
|
+
running untrusted code from fork PRs by defaulting to the safe base branch state.
|
|
25
|
+
|
|
26
|
+
The result is a silent failure: CI appears to run and pass, but it is actually
|
|
27
|
+
testing the base branch code, not the contributor's changes. No error is emitted.
|
|
28
|
+
|
|
29
|
+
Developers commonly hit this when:
|
|
30
|
+
- Migrating from pull_request to pull_request_target for Dependabot secrets access
|
|
31
|
+
- Setting up labeler/auto-assign bots that also want to run tests
|
|
32
|
+
- Building required-status checks that use pull_request_target for fork support
|
|
33
|
+
|
|
34
|
+
Even with actions/checkout@v4, the default ref for pull_request_target is
|
|
35
|
+
github.sha which resolves to the base branch HEAD — not the merge commit.
|
|
36
|
+
fix: |
|
|
37
|
+
Explicitly check out the PR head ref when you need to test the contributor's code:
|
|
38
|
+
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
42
|
+
|
|
43
|
+
WARNING: This executes untrusted code from fork contributors in an environment
|
|
44
|
+
with full repository secrets. Only do this after careful security review:
|
|
45
|
+
- Gate on github.event.pull_request.head.repo.fork to distinguish fork vs internal
|
|
46
|
+
- Never expose secrets to steps that run contributor code
|
|
47
|
+
- Consider using a separate workflow_run trigger instead for fork PRs that need secrets
|
|
48
|
+
|
|
49
|
+
For safe automation (labelers, assignment bots), use pull_request_target but do NOT
|
|
50
|
+
check out the head ref — these actions only need the event payload, not the code.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Check out PR head for internal PRs only (safe pattern)'
|
|
54
|
+
code: |
|
|
55
|
+
on:
|
|
56
|
+
pull_request_target:
|
|
57
|
+
types: [opened, synchronize]
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
test:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
# Only run contributor code for PRs from the same repo, not forks
|
|
63
|
+
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@v4
|
|
66
|
+
with:
|
|
67
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
68
|
+
- run: npm test
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: 'Safe bot usage — do NOT check out head (labeler example)'
|
|
71
|
+
code: |
|
|
72
|
+
on:
|
|
73
|
+
pull_request_target:
|
|
74
|
+
types: [opened]
|
|
75
|
+
|
|
76
|
+
jobs:
|
|
77
|
+
label:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
permissions:
|
|
80
|
+
pull-requests: write
|
|
81
|
+
steps:
|
|
82
|
+
# No checkout needed — labeler only reads the event payload
|
|
83
|
+
- uses: actions/labeler@v5
|
|
84
|
+
with:
|
|
85
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
86
|
+
prevention:
|
|
87
|
+
- 'Always explicitly set ref: when using actions/checkout with pull_request_target triggers'
|
|
88
|
+
- 'Never check out the PR head commit in a pull_request_target workflow that has access to secrets unless you have verified the source repo'
|
|
89
|
+
- 'Prefer pull_request for standard CI — use pull_request_target only when secrets access is required from fork workflows'
|
|
90
|
+
- 'Read the GitHub security hardening guide before using pull_request_target with any code execution steps'
|
|
91
|
+
docs:
|
|
92
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target'
|
|
93
|
+
label: 'GitHub Actions — pull_request_target event (security context and checkout behavior)'
|
|
94
|
+
- url: 'https://securitylab.github.com/research/github-actions-preventing-pwn-requests/'
|
|
95
|
+
label: 'GitHub Security Lab — Preventing pwn requests with pull_request_target'
|
|
96
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections'
|
|
97
|
+
label: 'GitHub Actions security hardening guide'
|
|
@@ -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'
|
package/package.json
CHANGED