@htekdev/actions-debugger 1.0.118 → 1.0.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/caching-artifacts/caching-artifacts-070.yml +94 -0
- package/errors/concurrency-timing/concurrency-timing-056.yml +127 -0
- package/errors/concurrency-timing/concurrency-timing-057.yml +115 -0
- package/errors/concurrency-timing/concurrency-timing-058.yml +121 -0
- package/errors/known-unsolved/known-unsolved-067.yml +117 -0
- package/errors/known-unsolved/known-unsolved-068.yml +124 -0
- package/errors/known-unsolved/known-unsolved-069.yml +127 -0
- package/errors/permissions-auth/permissions-auth-070.yml +136 -0
- package/errors/runner-environment/runner-environment-214.yml +107 -0
- package/errors/runner-environment/runner-environment-215.yml +93 -0
- package/errors/runner-environment/runner-environment-216.yml +82 -0
- package/errors/runner-environment/runner-environment-217.yml +99 -0
- package/errors/runner-environment/runner-environment-218.yml +111 -0
- package/errors/silent-failures/silent-failures-109.yml +119 -0
- package/errors/silent-failures/silent-failures-110.yml +91 -0
- package/errors/silent-failures/silent-failures-111.yml +107 -0
- package/errors/yaml-syntax/yaml-syntax-072.yml +93 -0
- package/errors/yaml-syntax/yaml-syntax-073.yml +103 -0
- package/package.json +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
id: known-unsolved-068
|
|
2
|
+
title: "Step outcome cannot distinguish timeout from failure — both report as 'failure' in steps context"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- timeout-minutes
|
|
7
|
+
- outcome
|
|
8
|
+
- conclusion
|
|
9
|
+
- continue-on-error
|
|
10
|
+
- steps-context
|
|
11
|
+
- retry
|
|
12
|
+
- known-limitation
|
|
13
|
+
- no-fix
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'steps\.\w+\.outcome\s*==\s*.failure.'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'timeout-minutes.*continue-on-error|continue-on-error.*timeout-minutes'
|
|
18
|
+
flags: 'im'
|
|
19
|
+
- regex: 'The process.*timed out after \d+ minutes'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
error_messages:
|
|
22
|
+
- "Error: The process '/usr/bin/bash' failed with exit code 1"
|
|
23
|
+
- 'Error: Process completed with exit code 1'
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub Actions exposes two result fields for completed steps in the steps context:
|
|
26
|
+
|
|
27
|
+
- steps.<id>.outcome: the raw result before continue-on-error is applied.
|
|
28
|
+
Possible values: success, failure, cancelled, skipped.
|
|
29
|
+
- steps.<id>.conclusion: the final result after continue-on-error is applied.
|
|
30
|
+
When continue-on-error: true is set on a failed step, conclusion becomes 'success'
|
|
31
|
+
even if outcome is 'failure'.
|
|
32
|
+
|
|
33
|
+
Neither field distinguishes between a step that failed because the process exited with a
|
|
34
|
+
non-zero code and a step that failed because it hit its timeout-minutes limit. Both
|
|
35
|
+
scenarios set outcome to 'failure'. There is no 'timed_out' value, no
|
|
36
|
+
steps.<id>.timed_out boolean, and no built-in expression function to query the reason
|
|
37
|
+
for failure.
|
|
38
|
+
|
|
39
|
+
This means workflows cannot natively:
|
|
40
|
+
- Retry only on timeout while failing fast on real errors
|
|
41
|
+
- Alert with different severity for timeouts vs application failures
|
|
42
|
+
- Auto-escalate timeout-minutes only when a timeout (not a logic error) occurred
|
|
43
|
+
|
|
44
|
+
The limitation has been a known open request in the GitHub Actions community since at
|
|
45
|
+
least 2022 with no current implementation timeline from GitHub.
|
|
46
|
+
fix: |
|
|
47
|
+
No native fix exists within GitHub Actions expressions. Two manual workarounds are
|
|
48
|
+
available in bash-based steps:
|
|
49
|
+
|
|
50
|
+
1. Record start time and compute elapsed duration at the next step to infer timeout:
|
|
51
|
+
Compare elapsed seconds against the timeout-minutes threshold. A step that used
|
|
52
|
+
approximately 100% of its time budget likely timed out.
|
|
53
|
+
|
|
54
|
+
2. Write a sentinel file just before the critical work; check for its absence afterward.
|
|
55
|
+
A timed-out step never reaches the sentinel-write line after the long-running command,
|
|
56
|
+
while a normally-failing step (which exits immediately on error) may or may not.
|
|
57
|
+
|
|
58
|
+
Neither workaround is exact — both have race conditions and edge cases. The most
|
|
59
|
+
reliable approach is to implement timeout detection inside the script itself using
|
|
60
|
+
shell signals or test-framework timeout flags.
|
|
61
|
+
fix_code:
|
|
62
|
+
- language: yaml
|
|
63
|
+
label: 'Workaround 1: Infer timeout via elapsed time'
|
|
64
|
+
code: |
|
|
65
|
+
- name: Start timer
|
|
66
|
+
id: timer
|
|
67
|
+
run: echo "start=$(date +%s)" >> "$GITHUB_OUTPUT"
|
|
68
|
+
|
|
69
|
+
- name: Run slow tests
|
|
70
|
+
id: tests
|
|
71
|
+
timeout-minutes: 10
|
|
72
|
+
continue-on-error: true
|
|
73
|
+
run: npm test
|
|
74
|
+
|
|
75
|
+
- name: Classify failure type
|
|
76
|
+
if: steps.tests.outcome == 'failure'
|
|
77
|
+
env:
|
|
78
|
+
START: ${{ steps.timer.outputs.start }}
|
|
79
|
+
run: |
|
|
80
|
+
elapsed=$(( $(date +%s) - START ))
|
|
81
|
+
timeout_secs=600 # 10 minutes in seconds
|
|
82
|
+
threshold=$(( timeout_secs - 30 )) # within 30s of limit → likely timeout
|
|
83
|
+
if [ "$elapsed" -ge "$threshold" ]; then
|
|
84
|
+
echo "::warning::Step likely timed out (elapsed ${elapsed}s, limit ${timeout_secs}s)"
|
|
85
|
+
# Handle timeout-specific logic here (e.g., don't fail, just warn)
|
|
86
|
+
else
|
|
87
|
+
echo "::error::Step failed (exit code, not timeout — elapsed ${elapsed}s)"
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
- language: yaml
|
|
91
|
+
label: 'Workaround 2: Sentinel file to detect timeout vs normal failure'
|
|
92
|
+
code: |
|
|
93
|
+
- name: Run tests with sentinel
|
|
94
|
+
id: tests
|
|
95
|
+
timeout-minutes: 10
|
|
96
|
+
continue-on-error: true
|
|
97
|
+
run: |
|
|
98
|
+
# The long-running command:
|
|
99
|
+
npm test
|
|
100
|
+
# Only reached on clean exit (not timeout, not error):
|
|
101
|
+
touch /tmp/test-completed
|
|
102
|
+
|
|
103
|
+
- name: Check failure reason
|
|
104
|
+
if: steps.tests.outcome == 'failure'
|
|
105
|
+
run: |
|
|
106
|
+
if [ ! -f /tmp/test-completed ]; then
|
|
107
|
+
echo "Step timed out or failed before completing"
|
|
108
|
+
# Inspect logs for timeout keyword:
|
|
109
|
+
# If the runner log shows "The process timed out after N minutes" → it was timeout
|
|
110
|
+
else
|
|
111
|
+
echo "Step completed but exited non-zero — application failure"
|
|
112
|
+
exit 1
|
|
113
|
+
fi
|
|
114
|
+
prevention:
|
|
115
|
+
- 'Log test durations inside the script itself; test framework flags like --testTimeout (Jest) or --timeout (Mocha) provide per-test granularity inside logs.'
|
|
116
|
+
- 'Use separate jobs for steps with different timeout characteristics — a dedicated integration-test job with a high timeout-minutes and a unit-test job with a low one makes failures easier to categorize.'
|
|
117
|
+
- 'If the step runs a single long command, wrap it in a shell timeout with a slightly shorter duration than timeout-minutes; the shell timeout exit code (124) is detectable inside the same step.'
|
|
118
|
+
docs:
|
|
119
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#steps-context'
|
|
120
|
+
label: 'GitHub Docs: steps context — outcome and conclusion fields'
|
|
121
|
+
- url: 'https://stackoverflow.com/questions/78233438/github-action-cannot-get-timeout-status-from-previous-step'
|
|
122
|
+
label: 'SO: Cannot get timeout status from previous step (Mar 2024)'
|
|
123
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-conditions-to-control-job-execution'
|
|
124
|
+
label: 'GitHub Docs: Status check functions (failure, success, cancelled, always)'
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
id: known-unsolved-069
|
|
2
|
+
title: "Matrix Strategy Hard Limit: 256 Jobs Per Workflow Run"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- strategy
|
|
8
|
+
- job-limit
|
|
9
|
+
- dynamic-matrix
|
|
10
|
+
- fromjson
|
|
11
|
+
- scalability
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'matrix.*generates.*too many|too many.*entries.*matrix'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'limited to 256|exceeding.*allowed maximum.*256|256.*jobs.*per.*workflow'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Matrix generates too many entries. Limited to 256."
|
|
19
|
+
root_cause: |
|
|
20
|
+
GitHub Actions enforces a hard limit of 256 jobs per workflow run across
|
|
21
|
+
all matrix dimensions. This is calculated as the total Cartesian product of
|
|
22
|
+
all matrix axes after applying include/exclude entries.
|
|
23
|
+
|
|
24
|
+
Common triggers:
|
|
25
|
+
- Dynamic matrices from fromJSON output that grow over time as new targets
|
|
26
|
+
are added to a test matrix config file
|
|
27
|
+
- Multi-dimensional matrices (os x language x version) where the product
|
|
28
|
+
of all axis sizes exceeds 256
|
|
29
|
+
- Adding include: entries on top of an already-large base matrix, each
|
|
30
|
+
include: adds one additional job that counts against the 256 limit
|
|
31
|
+
- Monorepo CI matrices that test all services x all supported runtimes
|
|
32
|
+
|
|
33
|
+
The error fires immediately when the workflow is queued and prevents any
|
|
34
|
+
jobs from running. GitHub reports the total generated count in the error
|
|
35
|
+
message: "Matrix generates N entries. Limited to 256."
|
|
36
|
+
|
|
37
|
+
This limit applies equally to GitHub-hosted and self-hosted runners and
|
|
38
|
+
cannot be increased by contacting support.
|
|
39
|
+
fix: |
|
|
40
|
+
The 256-job-per-run limit is hard and cannot be increased. Options:
|
|
41
|
+
|
|
42
|
+
1. Split across multiple workflow files, each handling a subset of targets.
|
|
43
|
+
Trigger them from a parent coordinator workflow or via separate triggers.
|
|
44
|
+
|
|
45
|
+
2. Reduce matrix dimensions: test only meaningful combinations using
|
|
46
|
+
explicit include: lists rather than full Cartesian products. Not every
|
|
47
|
+
OS x language x version triplet needs to be tested.
|
|
48
|
+
|
|
49
|
+
3. Chunk using workflow_dispatch fan-out: a generator job creates N chunks
|
|
50
|
+
of at most 256 items and dispatches sub-workflow runs per chunk via
|
|
51
|
+
repository_dispatch or workflow_dispatch API calls.
|
|
52
|
+
|
|
53
|
+
4. Use a single job with a shell loop for some dimensions: instead of
|
|
54
|
+
separate matrix jobs per version, loop over versions inside one job step.
|
|
55
|
+
Sacrifices parallelism but avoids the limit entirely.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Broken — Cartesian product silently grows past 256 as versions are added"
|
|
59
|
+
code: |
|
|
60
|
+
strategy:
|
|
61
|
+
matrix:
|
|
62
|
+
os: [ubuntu-22.04, ubuntu-24.04, windows-2022, macos-14, macos-15]
|
|
63
|
+
node: [18, 20, 22, 23]
|
|
64
|
+
python: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
|
65
|
+
# 5 x 4 x 5 = 100 jobs today — fine, but adding node 24 pushes to 125,
|
|
66
|
+
# adding python 3.14 pushes to 150, and so on until 256 is silently hit
|
|
67
|
+
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Fixed — explicit include list tests only meaningful combinations"
|
|
70
|
+
code: |
|
|
71
|
+
strategy:
|
|
72
|
+
matrix:
|
|
73
|
+
include:
|
|
74
|
+
# Test LTS node on primary OS with latest stable Python
|
|
75
|
+
- os: ubuntu-24.04
|
|
76
|
+
node: 20
|
|
77
|
+
python: '3.12'
|
|
78
|
+
- os: ubuntu-24.04
|
|
79
|
+
node: 22
|
|
80
|
+
python: '3.12'
|
|
81
|
+
# Windows only gets the most recent LTS
|
|
82
|
+
- os: windows-2022
|
|
83
|
+
node: 20
|
|
84
|
+
python: '3.11'
|
|
85
|
+
# macOS gets one combination
|
|
86
|
+
- os: macos-15
|
|
87
|
+
node: 20
|
|
88
|
+
python: '3.12'
|
|
89
|
+
# Total: 4 jobs instead of 100+ from full Cartesian product
|
|
90
|
+
|
|
91
|
+
- language: yaml
|
|
92
|
+
label: "Defensive — validate dynamic matrix size before passing to strategy"
|
|
93
|
+
code: |
|
|
94
|
+
jobs:
|
|
95
|
+
generate-matrix:
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
outputs:
|
|
98
|
+
matrix: ${{ steps.gen.outputs.matrix }}
|
|
99
|
+
steps:
|
|
100
|
+
- id: gen
|
|
101
|
+
run: |
|
|
102
|
+
MATRIX=$(cat test-matrix.json)
|
|
103
|
+
COUNT=$(echo "$MATRIX" | jq '.include | length')
|
|
104
|
+
if [ "$COUNT" -gt 256 ]; then
|
|
105
|
+
echo "::error::Matrix has $COUNT entries (limit: 256). Split into multiple workflows."
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
|
|
109
|
+
|
|
110
|
+
test:
|
|
111
|
+
needs: generate-matrix
|
|
112
|
+
strategy:
|
|
113
|
+
matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
|
|
114
|
+
runs-on: ${{ matrix.os }}
|
|
115
|
+
steps:
|
|
116
|
+
- run: echo "Testing ${{ matrix.os }} / ${{ matrix.version }}"
|
|
117
|
+
prevention:
|
|
118
|
+
- "Before adding a new matrix axis, calculate the new total job count — it must stay at or below 256."
|
|
119
|
+
- "For dynamic matrices (fromJSON), validate the output list length in the generator job and fail fast if > 256."
|
|
120
|
+
- "Prefer explicit include: lists over full Cartesian products when not all dimension combinations are meaningful."
|
|
121
|
+
- "Monitor matrix job counts over time: a previously-safe matrix silently crosses 256 as new versions are added."
|
|
122
|
+
- "Document the current job count in a comment next to the matrix block so reviewers notice when PRs push it higher."
|
|
123
|
+
docs:
|
|
124
|
+
- url: "https://docs.github.com/en/actions/reference/limits"
|
|
125
|
+
label: "GitHub Actions Limits — Job Matrix: 256 jobs per workflow run"
|
|
126
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow"
|
|
127
|
+
label: "GitHub Docs — Using a matrix for your jobs"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
id: permissions-auth-070
|
|
2
|
+
title: "GITHUB_TOKEN packages:write Cannot Delete GitHub Container Registry Packages"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-packages
|
|
7
|
+
- ghcr
|
|
8
|
+
- container-registry
|
|
9
|
+
- packages-delete
|
|
10
|
+
- permissions
|
|
11
|
+
- pat
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'delete.*package.*403|package.*delete.*forbidden'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Resource not accessible by integration'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Resource not accessible by integration"
|
|
19
|
+
- "HttpError: Resource not accessible by integration"
|
|
20
|
+
- "403 Forbidden"
|
|
21
|
+
root_cause: |
|
|
22
|
+
GitHub Container Registry (ghcr.io) and other granular-permission package
|
|
23
|
+
registries (npm, NuGet, RubyGems on ghpr) use package-level access control
|
|
24
|
+
that is separate from repository permissions. The GITHUB_TOKEN workflow
|
|
25
|
+
permission block supports packages: write, which grants the ability to:
|
|
26
|
+
- Publish (push) new package versions
|
|
27
|
+
- Update package metadata and visibility
|
|
28
|
+
|
|
29
|
+
However, packages: write does NOT grant the ability to DELETE package
|
|
30
|
+
versions. Deletion of granular-permission packages requires the delete:packages
|
|
31
|
+
scope, which exists only on classic personal access tokens (PATs) — there is
|
|
32
|
+
no equivalent permission in the GITHUB_TOKEN fine-grained model.
|
|
33
|
+
|
|
34
|
+
As a result, workflows that use actions/delete-package-versions (or make
|
|
35
|
+
direct REST API calls to DELETE /user/packages/{type}/{name}/versions/{id}
|
|
36
|
+
or DELETE /orgs/{org}/packages/{type}/{package_name}/versions/{id}) always
|
|
37
|
+
fail with 403 "Resource not accessible by integration" even with:
|
|
38
|
+
permissions:
|
|
39
|
+
packages: write
|
|
40
|
+
|
|
41
|
+
Note: Repository-scoped packages (Apache Maven, legacy Gradle) that inherit
|
|
42
|
+
repository permissions may behave differently — deletion can work via the
|
|
43
|
+
repo admin role. Container Registry packages always use granular permissions.
|
|
44
|
+
fix: |
|
|
45
|
+
Replace GITHUB_TOKEN with a token that carries delete:packages authority.
|
|
46
|
+
|
|
47
|
+
Option 1 (recommended for orgs): GitHub App token
|
|
48
|
+
- Create a GitHub App with "Packages: Read & Write" and "Packages: Admin"
|
|
49
|
+
permissions
|
|
50
|
+
- Install the app on the org or repo
|
|
51
|
+
- Use actions/create-github-app-token to generate a token in the workflow
|
|
52
|
+
- Pass the token to the deletion step
|
|
53
|
+
|
|
54
|
+
Option 2 (simpler for personal/small-team repos): Classic PAT
|
|
55
|
+
- Generate a classic PAT (Settings > Developer settings > Personal access
|
|
56
|
+
tokens > Tokens (classic)) with the delete:packages scope
|
|
57
|
+
- Store as a repository or organization secret
|
|
58
|
+
- Reference the secret in the deletion step
|
|
59
|
+
|
|
60
|
+
Option 3 (repo-scoped packages only — Apache Maven, Gradle):
|
|
61
|
+
- Ensure the package uses repository-scoped permissions (not granular)
|
|
62
|
+
- Grant the workflow admin access to the repository
|
|
63
|
+
- This does NOT apply to ghcr.io container images, which always require
|
|
64
|
+
a PAT or App approach for deletion
|
|
65
|
+
fix_code:
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: "Broken — GITHUB_TOKEN packages:write cannot delete container images"
|
|
68
|
+
code: |
|
|
69
|
+
permissions:
|
|
70
|
+
packages: write # Grants publish access but NOT delete access
|
|
71
|
+
|
|
72
|
+
jobs:
|
|
73
|
+
cleanup:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- name: Delete old versions
|
|
77
|
+
uses: actions/delete-package-versions@v5
|
|
78
|
+
with:
|
|
79
|
+
package-name: my-image
|
|
80
|
+
package-type: container
|
|
81
|
+
min-versions-to-keep: 5
|
|
82
|
+
token: ${{ secrets.GITHUB_TOKEN }} # Fails with 403 for container packages
|
|
83
|
+
|
|
84
|
+
- language: yaml
|
|
85
|
+
label: "Fixed — classic PAT with delete:packages scope"
|
|
86
|
+
code: |
|
|
87
|
+
permissions:
|
|
88
|
+
packages: write
|
|
89
|
+
|
|
90
|
+
jobs:
|
|
91
|
+
cleanup:
|
|
92
|
+
runs-on: ubuntu-latest
|
|
93
|
+
steps:
|
|
94
|
+
- name: Delete old versions
|
|
95
|
+
uses: actions/delete-package-versions@v5
|
|
96
|
+
with:
|
|
97
|
+
package-name: my-image
|
|
98
|
+
package-type: container
|
|
99
|
+
min-versions-to-keep: 5
|
|
100
|
+
# Classic PAT must have: write:packages + delete:packages scopes
|
|
101
|
+
token: ${{ secrets.PAT_DELETE_PACKAGES }}
|
|
102
|
+
|
|
103
|
+
- language: yaml
|
|
104
|
+
label: "Fixed — GitHub App token (recommended for organizations)"
|
|
105
|
+
code: |
|
|
106
|
+
jobs:
|
|
107
|
+
cleanup:
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
steps:
|
|
110
|
+
- name: Generate GitHub App token
|
|
111
|
+
id: app-token
|
|
112
|
+
uses: actions/create-github-app-token@v1
|
|
113
|
+
with:
|
|
114
|
+
app-id: ${{ vars.PACKAGE_CLEANUP_APP_ID }}
|
|
115
|
+
private-key: ${{ secrets.PACKAGE_CLEANUP_APP_KEY }}
|
|
116
|
+
|
|
117
|
+
- name: Delete old container image versions
|
|
118
|
+
uses: actions/delete-package-versions@v5
|
|
119
|
+
with:
|
|
120
|
+
package-name: my-image
|
|
121
|
+
package-type: container
|
|
122
|
+
min-versions-to-keep: 5
|
|
123
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
124
|
+
prevention:
|
|
125
|
+
- "Never assume packages:write grants deletion rights — it only covers publish and metadata operations."
|
|
126
|
+
- "Provision a dedicated classic PAT or GitHub App with package admin access for all cleanup/retention workflows."
|
|
127
|
+
- "Document which secrets are needed for deletion workflows so maintainers do not accidentally replace them with GITHUB_TOKEN."
|
|
128
|
+
- "Audit all uses of actions/delete-package-versions in your org to confirm each workflow uses an appropriate token."
|
|
129
|
+
- "Prefer GitHub App tokens over classic PATs for org workflows — PAT credentials are tied to a specific user account."
|
|
130
|
+
docs:
|
|
131
|
+
- url: "https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages"
|
|
132
|
+
label: "GitHub Docs — About permissions for GitHub Packages (granular vs repository-scoped)"
|
|
133
|
+
- url: "https://github.com/actions/delete-package-versions"
|
|
134
|
+
label: "actions/delete-package-versions — token requirements"
|
|
135
|
+
- url: "https://docs.github.com/en/rest/packages/packages"
|
|
136
|
+
label: "GitHub REST API — Packages DELETE endpoints"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
id: runner-environment-214
|
|
2
|
+
title: 'setup-java Fails with "Invalid Version" for JetBrains Runtime 4-Part Semver (e.g. 17.0.8.1+1080.1)'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-java
|
|
7
|
+
- jbr
|
|
8
|
+
- jetbrains-runtime
|
|
9
|
+
- java
|
|
10
|
+
- semver
|
|
11
|
+
- version-parsing
|
|
12
|
+
- windows
|
|
13
|
+
- ubuntu
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'Java setup process failed due to:\s*Invalid Version:\s*\d+\.\d+\.\d+\.\d+\+'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'Invalid Version:\s*\d+\.\d+\.\d+\.\d+'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'java setup.*failed.*Invalid Version'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
error_messages:
|
|
22
|
+
- 'Error: Java setup process failed due to: Invalid Version: 17.0.8.1+1080.1'
|
|
23
|
+
- 'Error: Java setup process failed due to: Invalid Version: 25.0.3+480.61'
|
|
24
|
+
- 'Error: Java setup process failed due to: Invalid Version: 21.0.5.1+1100.2'
|
|
25
|
+
root_cause: |
|
|
26
|
+
JetBrains Runtime (JBR) version strings use a 4-part numeric format:
|
|
27
|
+
`major.minor.patch.update+build.sub-build` (e.g. `17.0.8.1+1080.1`).
|
|
28
|
+
This does NOT conform to standard semantic versioning (3-part numeric).
|
|
29
|
+
|
|
30
|
+
The `actions/setup-java` version resolver uses a semver parser internally.
|
|
31
|
+
When the user passes a full pinned JBR version string containing a 4-part
|
|
32
|
+
numeric prefix (the `.1` after the patch component), the parser throws:
|
|
33
|
+
|
|
34
|
+
"Invalid Version: 17.0.8.1+1080.1"
|
|
35
|
+
|
|
36
|
+
Affected distributions: `jetbrains` (JBR) distribution only.
|
|
37
|
+
Affected platforms: Ubuntu and Windows (both throw the same error).
|
|
38
|
+
Unaffected: Oracle, Temurin, Adopt, Corretto and other distributions whose
|
|
39
|
+
version strings use standard 3-part semver (e.g. `21.0.3+9`).
|
|
40
|
+
|
|
41
|
+
The root cause is that JBR independently versions patch updates
|
|
42
|
+
(the `.1` suffix), meaning a JBR patch release version is 4 integers deep,
|
|
43
|
+
which the bundled semver parser does not accept.
|
|
44
|
+
|
|
45
|
+
Tracked upstream: https://github.com/actions/setup-java/issues/1008 (open May 2026)
|
|
46
|
+
Fix PR: https://github.com/actions/setup-java/pull/1009 (isVersionSatisfies patch — pending merge)
|
|
47
|
+
fix: |
|
|
48
|
+
Until https://github.com/actions/setup-java/pull/1009 is merged and released,
|
|
49
|
+
do NOT pin the full 4-part JBR version string. Use either:
|
|
50
|
+
|
|
51
|
+
1. The 3-part version prefix (major.minor.patch) — setup-java will resolve the
|
|
52
|
+
latest available JBR sub-build for that patch:
|
|
53
|
+
|
|
54
|
+
- uses: actions/setup-java@v5
|
|
55
|
+
with:
|
|
56
|
+
distribution: 'jetbrains'
|
|
57
|
+
java-version: '17.0.8' # omit the .1 suffix
|
|
58
|
+
|
|
59
|
+
2. Just the major version for the latest JBR:
|
|
60
|
+
|
|
61
|
+
- uses: actions/setup-java@v5
|
|
62
|
+
with:
|
|
63
|
+
distribution: 'jetbrains'
|
|
64
|
+
java-version: '17'
|
|
65
|
+
|
|
66
|
+
3. The pre-release format accepted by the current parser (major.minor.patch
|
|
67
|
+
with the build metadata only, omitting the update digit):
|
|
68
|
+
|
|
69
|
+
- uses: actions/setup-java@v5
|
|
70
|
+
with:
|
|
71
|
+
distribution: 'jetbrains'
|
|
72
|
+
java-version: '17.0.8+1080.1' # skip the 4th integer; use build metadata
|
|
73
|
+
fix_code:
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: 'Use 3-part version prefix to avoid "Invalid Version" on JBR'
|
|
76
|
+
code: |
|
|
77
|
+
- name: Set up JBR 17 (JetBrains Runtime)
|
|
78
|
+
uses: actions/setup-java@v5
|
|
79
|
+
with:
|
|
80
|
+
distribution: 'jetbrains'
|
|
81
|
+
java-version: '17.0.8' # NOT '17.0.8.1+1080.1' — 4-part fails the parser
|
|
82
|
+
- language: yaml
|
|
83
|
+
label: 'Major-version pin (simplest, always resolves latest JBR for that major)'
|
|
84
|
+
code: |
|
|
85
|
+
- name: Set up JBR 25
|
|
86
|
+
uses: actions/setup-java@v5
|
|
87
|
+
with:
|
|
88
|
+
distribution: 'jetbrains'
|
|
89
|
+
java-version: '25'
|
|
90
|
+
prevention:
|
|
91
|
+
- 'Check the JBR releases page to understand the version format before pinning.
|
|
92
|
+
JBR versions are `major.minor.patch.update+build.sub-build`; only use the
|
|
93
|
+
3-part prefix (major.minor.patch) or major-version-only with setup-java.'
|
|
94
|
+
- 'Watch https://github.com/actions/setup-java/pull/1009 for when the fix merges
|
|
95
|
+
so full 4-part JBR version strings become accepted.'
|
|
96
|
+
- 'For reproducibility without pinning the exact JBR sub-build, consider
|
|
97
|
+
using a `.java-version` file with a 3-part version string and referencing it
|
|
98
|
+
via `java-version-file:` in setup-java.'
|
|
99
|
+
docs:
|
|
100
|
+
- url: 'https://github.com/actions/setup-java/issues/1008'
|
|
101
|
+
label: 'setup-java #1008 — Error parsing JBR version 17.0.8.1+1080.1 (open, May 2026)'
|
|
102
|
+
- url: 'https://github.com/actions/setup-java/pull/1009'
|
|
103
|
+
label: 'setup-java PR #1009 — fix: reject non-semver candidate versions in isVersionSatisfies (pending)'
|
|
104
|
+
- url: 'https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions'
|
|
105
|
+
label: 'setup-java README — Supported distributions (JetBrains / JBR)'
|
|
106
|
+
- url: 'https://github.com/JetBrains/JetBrainsRuntime/releases'
|
|
107
|
+
label: 'JetBrains Runtime releases — version format reference'
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: runner-environment-215
|
|
2
|
+
title: 'setup-node fails with EBADDEVENGINES when devEngines.packageManager.version does not match initial npm'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-node
|
|
7
|
+
- npm
|
|
8
|
+
- devEngines
|
|
9
|
+
- EBADDEVENGINES
|
|
10
|
+
- package-manager
|
|
11
|
+
- npm-version
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'npm error code EBADDEVENGINES'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'npm error EBADDEVENGINES.*Invalid devEngines\.packageManager'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'npm error EBADDEVENGINES.*Invalid semver version.*does not match.*for "packageManager"'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: '/npm config get cache\s*npm error code EBADDEVENGINES'
|
|
20
|
+
flags: 'is'
|
|
21
|
+
error_messages:
|
|
22
|
+
- 'npm error code EBADDEVENGINES'
|
|
23
|
+
- 'npm error EBADDEVENGINES The developer of this package has specified the following through devEngines'
|
|
24
|
+
- 'npm error EBADDEVENGINES Invalid devEngines.packageManager'
|
|
25
|
+
- 'npm error EBADDEVENGINES Invalid semver version "^11.10.0" does not match "10.9.7" for "packageManager"'
|
|
26
|
+
root_cause: |
|
|
27
|
+
actions/setup-node@v6 runs `npm config get cache` immediately after installing Node.js to
|
|
28
|
+
determine the npm cache path before activating the package-manager-cache feature. If
|
|
29
|
+
package.json declares a `devEngines.packageManager.version` constraint (Node.js 22.6+
|
|
30
|
+
feature) that the bundled npm does NOT satisfy, npm exits with EBADDEVENGINES before
|
|
31
|
+
returning the cache path.
|
|
32
|
+
|
|
33
|
+
The failure happens before setup-node has a chance to upgrade npm to the required version,
|
|
34
|
+
creating a catch-22: the action needs the cache path to set up, but npm won't answer
|
|
35
|
+
because the version constraint is not yet met.
|
|
36
|
+
|
|
37
|
+
Common scenario: project requires `npm@^11.10.0` (for `min-release-age` or other features
|
|
38
|
+
added in npm 11.x), but Node 22 ships with npm 10.x. The first `npm` invocation fails
|
|
39
|
+
immediately when devEngines enforcement is active.
|
|
40
|
+
fix: |
|
|
41
|
+
Option 1 (recommended) — add `"onFail": "warn"` to the devEngines.packageManager block.
|
|
42
|
+
This downgrades version mismatches to warnings so npm commands still complete, letting
|
|
43
|
+
setup-node finish. Install the required npm version in a subsequent step.
|
|
44
|
+
|
|
45
|
+
Option 2 — pin Node.js to a version whose bundled npm satisfies devEngines (often
|
|
46
|
+
impractical).
|
|
47
|
+
|
|
48
|
+
Option 3 — set `package-manager-cache: false` in setup-node to skip the cache-path probe
|
|
49
|
+
entirely and manage npm caching separately.
|
|
50
|
+
fix_code:
|
|
51
|
+
- language: yaml
|
|
52
|
+
label: 'Option 1 — add onFail: warn to package.json (fix the root cause)'
|
|
53
|
+
code: |
|
|
54
|
+
# In package.json:
|
|
55
|
+
# {
|
|
56
|
+
# "devEngines": {
|
|
57
|
+
# "packageManager": {
|
|
58
|
+
# "name": "npm",
|
|
59
|
+
# "version": "^11.10.0",
|
|
60
|
+
# "onFail": "warn" <-- add this
|
|
61
|
+
# }
|
|
62
|
+
# }
|
|
63
|
+
# }
|
|
64
|
+
|
|
65
|
+
# In workflow — install the required npm version after setup-node succeeds:
|
|
66
|
+
- uses: actions/setup-node@v6
|
|
67
|
+
with:
|
|
68
|
+
node-version-file: package.json
|
|
69
|
+
- run: npm install -g npm@^11.10.0
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: 'Option 3 — disable package-manager-cache in setup-node'
|
|
72
|
+
code: |
|
|
73
|
+
- uses: actions/setup-node@v6
|
|
74
|
+
with:
|
|
75
|
+
node-version: '22'
|
|
76
|
+
package-manager-cache: false # skip npm config get cache probe
|
|
77
|
+
- run: npm install -g npm@^11.10.0
|
|
78
|
+
- uses: actions/cache@v4
|
|
79
|
+
with:
|
|
80
|
+
path: ~/.npm
|
|
81
|
+
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
|
82
|
+
prevention:
|
|
83
|
+
- 'Always pair devEngines.packageManager.version with "onFail": "warn" unless the project must hard-fail on CI for version mismatches.'
|
|
84
|
+
- 'Use package-manager-cache: false in setup-node and handle npm caching manually when devEngines constraints are strict.'
|
|
85
|
+
- 'Track the Node.js release schedule to choose a version that ships with an npm satisfying devEngines requirements from day one.'
|
|
86
|
+
- 'Pin action version in workflow (e.g., actions/setup-node@v6.4.0) and test upgrades in a branch before rolling out.'
|
|
87
|
+
docs:
|
|
88
|
+
- url: 'https://github.com/actions/setup-node/issues/1553'
|
|
89
|
+
label: 'setup-node#1553 — EBADDEVENGINES blocks cache-path probe (May 2026)'
|
|
90
|
+
- url: 'https://docs.npmjs.com/cli/v11/configuring-npm/package-json#devengines'
|
|
91
|
+
label: 'npm docs — devEngines field and onFail option'
|
|
92
|
+
- url: 'https://github.com/nodejs/node/issues/62425'
|
|
93
|
+
label: 'Node.js#62425 — npm installation behavior on Node v22.22.2 with devEngines'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
id: runner-environment-216
|
|
2
|
+
title: 'ubuntu-26.04 ships Helm 4 — `helm repo add --no-update` removed, causes unknown flag error'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- helm
|
|
7
|
+
- helm-4
|
|
8
|
+
- ubuntu-26
|
|
9
|
+
- tool-upgrade
|
|
10
|
+
- flag-removed
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Error: unknown flag: --no-update'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'unknown flag.*--no-update'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
error_messages:
|
|
17
|
+
- 'Error: unknown flag: --no-update'
|
|
18
|
+
- 'Error: flag provided but not defined: --no-update'
|
|
19
|
+
root_cause: |
|
|
20
|
+
ubuntu-26.04 runner images install Helm 4 (via the `get-helm-4` installer script)
|
|
21
|
+
instead of Helm 3 used on ubuntu-22.04 and ubuntu-24.04. In Helm 4, the
|
|
22
|
+
`--no-update` flag was permanently removed from `helm repo add`
|
|
23
|
+
(breaking change: helm/helm#13614, released in Helm v4.0.0, November 2025).
|
|
24
|
+
|
|
25
|
+
In Helm 3, `helm repo add <name> <url> --no-update` was used to add a repo
|
|
26
|
+
without triggering a refresh of all configured repos. This was common in CI
|
|
27
|
+
pipelines to speed up `helm repo add` when many repos are configured. The
|
|
28
|
+
flag was first deprecated in Helm 3.7 and has been removed entirely in Helm 4
|
|
29
|
+
with no deprecated alias retained.
|
|
30
|
+
|
|
31
|
+
The runner-images commit 9e3319d (May 2026) introduced Helm 4 for ubuntu-26.04.
|
|
32
|
+
Any workflow using `runs-on: ubuntu-26.04` (or eventually `ubuntu-latest` when
|
|
33
|
+
it migrates to 26.04) that calls `helm repo add --no-update` will fail
|
|
34
|
+
immediately with `Error: unknown flag: --no-update`.
|
|
35
|
+
fix: |
|
|
36
|
+
Remove the `--no-update` flag entirely. In Helm 4, `helm repo add` does not
|
|
37
|
+
update existing repos by default — the behavior the flag used to enforce is
|
|
38
|
+
now the default behavior.
|
|
39
|
+
|
|
40
|
+
If you need to force-update all repos after adding, use `helm repo update`.
|
|
41
|
+
|
|
42
|
+
To avoid Helm version surprises on ubuntu-26.04, pin a specific Helm version
|
|
43
|
+
using `azure/setup-helm` and pass the version explicitly.
|
|
44
|
+
fix_code:
|
|
45
|
+
- language: yaml
|
|
46
|
+
label: 'Remove --no-update flag (Helm 4 default behavior)'
|
|
47
|
+
code: |
|
|
48
|
+
- name: Add Helm repo
|
|
49
|
+
run: |
|
|
50
|
+
# Helm 4 on ubuntu-26.04: --no-update flag has been removed.
|
|
51
|
+
# Helm 4 does not update existing repos by default (old --no-update behavior).
|
|
52
|
+
# Simply omit the flag:
|
|
53
|
+
helm repo add bitnami https://charts.bitnami.com/bitnami
|
|
54
|
+
# If you still need to update all repos afterwards:
|
|
55
|
+
# helm repo update
|
|
56
|
+
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: 'Pin Helm version with azure/setup-helm to avoid version surprises'
|
|
59
|
+
code: |
|
|
60
|
+
- name: Set up Helm
|
|
61
|
+
uses: azure/setup-helm@v5
|
|
62
|
+
with:
|
|
63
|
+
version: '3.17.x' # Pin to Helm 3 if your workflow isn't Helm 4 ready
|
|
64
|
+
# Or pin to a specific Helm 4 version:
|
|
65
|
+
# version: '4.2.x'
|
|
66
|
+
|
|
67
|
+
- name: Add Helm repo
|
|
68
|
+
run: helm repo add bitnami https://charts.bitnami.com/bitnami
|
|
69
|
+
prevention:
|
|
70
|
+
- "Pin your Helm version with `azure/setup-helm` rather than relying on the pre-installed Helm on the runner."
|
|
71
|
+
- "When migrating to ubuntu-26.04, audit all `helm repo add` calls and remove any `--no-update` flags."
|
|
72
|
+
- "Check the Helm 4 migration guide at https://helm.sh/docs/topics/v4_migration/ before adopting ubuntu-26.04."
|
|
73
|
+
- "Add a CI step that prints `helm version` so version surprises are immediately visible in logs."
|
|
74
|
+
docs:
|
|
75
|
+
- url: 'https://github.com/helm/helm/blob/main/CHANGELOG.md'
|
|
76
|
+
label: 'Helm 4 Changelog — breaking changes'
|
|
77
|
+
- url: 'https://helm.sh/docs/topics/v4_migration/'
|
|
78
|
+
label: 'Helm v4 Migration Guide'
|
|
79
|
+
- url: 'https://github.com/actions/runner-images/commit/9e3319d6b4acc306925295853d0ff41ddd5c40f0'
|
|
80
|
+
label: 'runner-images commit: ubuntu-26 ships Helm 4 (get-helm-4)'
|
|
81
|
+
- url: 'https://github.com/Azure/setup-helm'
|
|
82
|
+
label: 'azure/setup-helm action — pin Helm version'
|