@htekdev/actions-debugger 1.0.31 → 1.0.33
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/download-artifact-v5-single-artifact-no-subdirectory.yml +106 -0
- package/errors/permissions-auth/ghs-token-new-jwt-format-breaks-length-pattern-validation.yml +95 -0
- package/errors/runner-environment/macos-15-arm64-homebrew-rustup-init-argv-rejected.yml +114 -0
- package/errors/runner-environment/ubuntu-24-04-iproute2-kernel-614-vxlan-segfault.yml +102 -0
- package/errors/runner-environment/ubuntu-slim-setup-node-semver-range-toolcache-miss.yml +108 -0
- package/errors/silent-failures/download-artifact-merge-multiple-parallel-file-clobber.yml +119 -0
- package/errors/silent-failures/step-summary-content-silently-dropped-near-limit.yml +115 -0
- package/errors/triggers/copilot-coding-agent-workflows-require-approval.yml +104 -0
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
id: 'caching-artifacts-029'
|
|
2
|
+
title: '`download-artifact` v5+ omits artifact subdirectory when only one artifact exists — dynamic workflows break intermittently'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- download-artifact
|
|
7
|
+
- v5-breaking-change
|
|
8
|
+
- path-behavior
|
|
9
|
+
- single-artifact
|
|
10
|
+
- subdirectory
|
|
11
|
+
- dynamic-matrix
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'uses:\s*actions/download-artifact@v[5-9]'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'An extra directory with the artifact name will be created for each download'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'No input name, artifact-ids or pattern filtered specified, downloading all artifacts'
|
|
19
|
+
- 'An extra directory with the artifact name will be created for each download'
|
|
20
|
+
root_cause: |
|
|
21
|
+
In actions/download-artifact v5 (released August 2025) and later (v6, v7), when
|
|
22
|
+
downloading ALL artifacts without specifying name: or id:, and only ONE artifact exists
|
|
23
|
+
in the workflow run, the action does NOT create a subdirectory for that artifact.
|
|
24
|
+
Files are extracted directly into the destination path (default ./):
|
|
25
|
+
|
|
26
|
+
Expected (v4 and v5+ with >=2 artifacts): ./{artifact-name}/file.ext
|
|
27
|
+
Actual (v5+ with exactly 1 artifact): ./file.ext
|
|
28
|
+
|
|
29
|
+
When two or more artifacts exist, subdirectories are created correctly. This
|
|
30
|
+
inconsistency breaks workflows where artifact count varies dynamically — e.g., matrix
|
|
31
|
+
strategies that produce 0, 1, or N artifacts based on test conditions.
|
|
32
|
+
|
|
33
|
+
The action logs "An extra directory with the artifact name will be created for each
|
|
34
|
+
download" even when it will NOT do this — making the bug invisible in logs.
|
|
35
|
+
|
|
36
|
+
Bug tracked in actions/download-artifact#455 (December 2025, still open).
|
|
37
|
+
Affected versions: v5.0.0+, v6.0.0+, v7.0.0+
|
|
38
|
+
v4 behavior (correct): always creates a named subdirectory, regardless of artifact count.
|
|
39
|
+
|
|
40
|
+
This is a regression from a path behavior fix introduced in v5 for single-artifact
|
|
41
|
+
downloads by ID (PR #416), which had an unintended side effect on the "all artifacts"
|
|
42
|
+
code path when n=1.
|
|
43
|
+
fix: |
|
|
44
|
+
Option 1 (simplest): Pin to actions/download-artifact@v4, which always creates a
|
|
45
|
+
subdirectory regardless of artifact count. v4 remains maintained for GHES compatibility.
|
|
46
|
+
|
|
47
|
+
Option 2: Always specify name: for each download to avoid the "all artifacts" code path.
|
|
48
|
+
|
|
49
|
+
Option 3: In downstream steps, detect the n=1 case at runtime and normalize the
|
|
50
|
+
directory structure before relying on subdirectory paths.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Pin to download-artifact@v4 for consistent subdirectory behavior'
|
|
54
|
+
code: |
|
|
55
|
+
jobs:
|
|
56
|
+
deploy:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- name: Download all artifacts
|
|
60
|
+
uses: actions/download-artifact@v4 # v4: always creates subdirs
|
|
61
|
+
with:
|
|
62
|
+
path: artifacts/
|
|
63
|
+
# Result: artifacts/{name}/... for every artifact, even when n=1
|
|
64
|
+
|
|
65
|
+
- name: Deploy each artifact directory
|
|
66
|
+
run: |
|
|
67
|
+
for dir in artifacts/*/; do
|
|
68
|
+
name=$(basename "$dir")
|
|
69
|
+
echo "Deploying $name from $dir"
|
|
70
|
+
done
|
|
71
|
+
- language: yaml
|
|
72
|
+
label: 'Workaround for v5+: download by name into named subdirectories explicitly'
|
|
73
|
+
code: |
|
|
74
|
+
jobs:
|
|
75
|
+
deploy:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- name: List artifact names for this run
|
|
79
|
+
id: list
|
|
80
|
+
uses: actions/github-script@v7
|
|
81
|
+
with:
|
|
82
|
+
script: |
|
|
83
|
+
const { data } = await github.rest.actions.listWorkflowRunArtifacts({
|
|
84
|
+
owner: context.repo.owner,
|
|
85
|
+
repo: context.repo.repo,
|
|
86
|
+
run_id: context.runId
|
|
87
|
+
});
|
|
88
|
+
return data.artifacts.map(a => a.name);
|
|
89
|
+
|
|
90
|
+
- name: Download artifact into named subdirectory
|
|
91
|
+
uses: actions/download-artifact@v5
|
|
92
|
+
with:
|
|
93
|
+
name: my-artifact
|
|
94
|
+
path: artifacts/my-artifact/ # Explicit subdir — consistent always
|
|
95
|
+
prevention:
|
|
96
|
+
- 'Pin to download-artifact@v4 if your deployment logic assumes each artifact lands in its own named subdirectory'
|
|
97
|
+
- 'Test your workflow with exactly one artifact in CI — the n=1 edge case is easy to miss in standard multi-run testing'
|
|
98
|
+
- 'Avoid relying on implicit subdirectory creation; always specify explicit path: values that include the artifact name'
|
|
99
|
+
- 'Track actions/download-artifact#455 for a fix in v5+ that restores consistent subdirectory behavior'
|
|
100
|
+
docs:
|
|
101
|
+
- url: 'https://github.com/actions/download-artifact/issues/455'
|
|
102
|
+
label: 'actions/download-artifact#455: Single artifact omits subdirectory in v5+ (open, December 2025)'
|
|
103
|
+
- url: 'https://github.com/actions/download-artifact/releases/tag/v5.0.0'
|
|
104
|
+
label: 'download-artifact v5.0.0 release notes: path behavior breaking change'
|
|
105
|
+
- url: 'https://github.com/actions/download-artifact/blob/main/docs/MIGRATION.md'
|
|
106
|
+
label: 'download-artifact migration guide: v3/v4 to v5+'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
id: 'permissions-auth-031'
|
|
2
|
+
title: 'GitHub App installation token new stateless JWT format (~520 chars) breaks hardcoded length and regex validation'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-app-token
|
|
7
|
+
- ghs-token
|
|
8
|
+
- jwt-format
|
|
9
|
+
- token-validation
|
|
10
|
+
- installation-token
|
|
11
|
+
- breaking-change-2026
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'ghs_[A-Za-z0-9]{36}\b'
|
|
14
|
+
flags: ''
|
|
15
|
+
- regex: 'Data too long for column|token.*length.*40|varchar\s*\(\s*40\s*\)'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Data too long for column 'access_token' at row 1"
|
|
19
|
+
- 'Token validation failed: token length 521 does not match expected 40'
|
|
20
|
+
- 'Invalid token format: expected 40 characters, got 523'
|
|
21
|
+
- 'ghs_[A-Za-z0-9]{36} pattern did not match token starting with ghs_'
|
|
22
|
+
root_cause: |
|
|
23
|
+
Starting April 27, 2026, GitHub began a staged rollout that changes the format of all
|
|
24
|
+
GitHub App installation tokens (ghs_ prefixed tokens), including the GITHUB_TOKEN
|
|
25
|
+
issued to Actions workflows.
|
|
26
|
+
|
|
27
|
+
Old format: ghs_<36 alphanumeric chars> — exactly 40 characters, opaque string
|
|
28
|
+
New format: ghs_<AppID>_<JWT> — approximately 520 characters (variable length),
|
|
29
|
+
JWT payload is signed by a GitHub-internal issuer
|
|
30
|
+
|
|
31
|
+
Any code that validates or stores tokens with the old format assumption WILL break:
|
|
32
|
+
- Regex: ghs_[A-Za-z0-9]{36} — the {36} quantifier rejects the new variable-length JWT
|
|
33
|
+
- Length checks: len(token) == 40, token.length === 40, strlen($token) == 40
|
|
34
|
+
- Database columns: VARCHAR(40) or CHAR(40) — the new ~520-char token truncates or errors
|
|
35
|
+
- Secret scanning rules with exact-length matchers for ghs_ tokens miss leaked tokens
|
|
36
|
+
|
|
37
|
+
The rollout timeline:
|
|
38
|
+
- April 27 – mid-May 2026: GITHUB_TOKEN and first-party tokens (Dependabot, Slack, Teams)
|
|
39
|
+
- Mid-May to late-June 2026: staged rollout to all GitHub App installation tokens
|
|
40
|
+
|
|
41
|
+
Despite the new internal JWT structure, the token prefix ghs_ is unchanged. Clients
|
|
42
|
+
must treat the entire token as an opaque string and must NOT parse or validate the JWT.
|
|
43
|
+
fix: |
|
|
44
|
+
Treat all ghs_ tokens as variable-length opaque strings. Remove any assumptions about
|
|
45
|
+
exact length or character structure after the ghs_ prefix:
|
|
46
|
+
|
|
47
|
+
1. Update regex patterns: replace ghs_[A-Za-z0-9]{36} with ghs_[A-Za-z0-9._-]+
|
|
48
|
+
2. Increase database columns: VARCHAR(40) or CHAR(40) → TEXT or VARCHAR(1024)
|
|
49
|
+
3. Remove hardcoded length checks: len(token) == 40 → len(token) > 3
|
|
50
|
+
4. Update secret scanning rules to match variable-length ghs_ tokens
|
|
51
|
+
|
|
52
|
+
To test your tooling against the new format BEFORE the rollout reaches your integration,
|
|
53
|
+
use the X-GitHub-Stateless-S2S-Token: enabled header on access token API requests.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: 'Verify GITHUB_TOKEN length in a workflow — expect ~520 chars with new format'
|
|
57
|
+
code: |
|
|
58
|
+
jobs:
|
|
59
|
+
check-token-format:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- name: Check GITHUB_TOKEN length
|
|
63
|
+
env:
|
|
64
|
+
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
run: |
|
|
66
|
+
echo "Token length: ${#TOKEN}"
|
|
67
|
+
# Old format: exactly 40 chars
|
|
68
|
+
# New format (post-April 2026 rollout): ~520 chars (variable)
|
|
69
|
+
# If your tooling uses this token, ensure it handles both lengths
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: 'Correct regex for ghs_ tokens in secret scanning custom patterns'
|
|
72
|
+
code: |
|
|
73
|
+
# WRONG — only matches old 40-char opaque tokens:
|
|
74
|
+
# ghs_[A-Za-z0-9]{36}
|
|
75
|
+
#
|
|
76
|
+
# CORRECT — matches old and new JWT format:
|
|
77
|
+
# ghs_[A-Za-z0-9._-]+
|
|
78
|
+
#
|
|
79
|
+
# .github/secret_scanning.yml custom pattern (if scanning for leaked tokens):
|
|
80
|
+
custom_patterns:
|
|
81
|
+
- name: GitHub App installation token (all formats)
|
|
82
|
+
regex: 'ghs_[A-Za-z0-9._-]+'
|
|
83
|
+
prevention:
|
|
84
|
+
- 'Treat all GitHub tokens as variable-length opaque strings — never hardcode expected length'
|
|
85
|
+
- 'Use TEXT or VARCHAR(1024) for any database column that stores GitHub App installation tokens'
|
|
86
|
+
- 'Validate ghs_ tokens by prefix only, not by exact length or post-prefix character class'
|
|
87
|
+
- 'Subscribe to the GitHub Changelog (github.blog/changelog) to catch token format changes early'
|
|
88
|
+
- 'Use X-GitHub-Stateless-S2S-Token: enabled header to test your tooling before the rollout reaches your repos'
|
|
89
|
+
docs:
|
|
90
|
+
- url: 'https://github.blog/changelog/2026-04-24-notice-about-upcoming-new-format-for-github-app-installation-tokens/'
|
|
91
|
+
label: 'GitHub Changelog: Notice about upcoming new format for GitHub App installation tokens (April 24, 2026)'
|
|
92
|
+
- url: 'https://github.blog/changelog/2026-05-15-github-app-installation-tokens-per-request-override-header/'
|
|
93
|
+
label: 'GitHub Changelog: Per-request override header for testing the new token format (May 15, 2026)'
|
|
94
|
+
- url: 'https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app'
|
|
95
|
+
label: 'GitHub Docs: Generating an installation access token for a GitHub App'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
id: 'runner-environment-086'
|
|
2
|
+
title: 'macOS 15 arm64 Homebrew `rustup-init` rejects `+stable` toolchain specifier and subcommands — Rust builds fail with ~50% probability'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- macos
|
|
7
|
+
- arm64
|
|
8
|
+
- homebrew
|
|
9
|
+
- rust
|
|
10
|
+
- rustup
|
|
11
|
+
- maturin
|
|
12
|
+
- napi-rs
|
|
13
|
+
- image-regression
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'error: unexpected argument .+stable. found'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'error: unexpected argument .metadata. found'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'Usage: rustup-init\[EXE\] \[OPTIONS\]'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
- regex: 'macos-15-arm64.*20260511'
|
|
22
|
+
flags: 'i'
|
|
23
|
+
error_messages:
|
|
24
|
+
- "Error: error: error: unexpected argument '+stable' found"
|
|
25
|
+
- "error: error: unexpected argument 'metadata' found"
|
|
26
|
+
- "Usage: rustup-init[EXE] [OPTIONS]"
|
|
27
|
+
- "info: self-update is disabled for this build of rustup"
|
|
28
|
+
- "Could not parse the Cargo.toml: Error: Command failed: cargo metadata"
|
|
29
|
+
root_cause: |
|
|
30
|
+
The macos-15-arm64 runner image version 20260511.0048 ships a broken Homebrew rustup
|
|
31
|
+
formula at /opt/homebrew/bin/rustup (version 1.29.0) whose bundled rustup-init binary
|
|
32
|
+
rejects standard argv that the outer rustup CLI forwards internally.
|
|
33
|
+
|
|
34
|
+
When rustup receives a command like `rustup component add llvm-tools-preview` or when
|
|
35
|
+
cargo shims invoke `cargo metadata`, the outer rustup binary calls rustup-init with
|
|
36
|
+
forwarded arguments (+stable, metadata, etc.). On this image version, rustup-init
|
|
37
|
+
treats these as unknown positional arguments and immediately exits with an error.
|
|
38
|
+
|
|
39
|
+
The failure is intermittent because GitHub's runner scheduler assigns jobs to different
|
|
40
|
+
image versions: 20260511.0048 (broken) and 20260427.0018 (working). A single workflow
|
|
41
|
+
run with multiple matrix entries can produce a mix of passing and failing jobs depending
|
|
42
|
+
on which image version each job lands on. Reruns do not reliably fix the issue because
|
|
43
|
+
rerun jobs may again be scheduled on the broken image version.
|
|
44
|
+
|
|
45
|
+
Affected downstream tools (all work on 20260427.0018, all fail on 20260511.0048):
|
|
46
|
+
- PyO3/maturin-action@v1 — calls `rustup component add llvm-tools-preview`
|
|
47
|
+
- @napi-rs/cli — calls `cargo metadata --format-version 1`
|
|
48
|
+
- Any workflow that calls `rustup component add` with a toolchain specifier
|
|
49
|
+
- Any Rust build that uses the Homebrew rustup shim for internal rustup-init calls
|
|
50
|
+
|
|
51
|
+
Tracked in actions/runner-images#14097 (May 2026, open).
|
|
52
|
+
fix: |
|
|
53
|
+
Option 1 (recommended): Install rustup from the upstream installer (sh.rustup.rs)
|
|
54
|
+
before any Rust build steps. This replaces the broken Homebrew binary at
|
|
55
|
+
/opt/homebrew/bin/rustup with the upstream version and resolves the argv issue.
|
|
56
|
+
|
|
57
|
+
Option 2: Pin the runner to macos-15-arm64 explicitly and avoid the image lottery.
|
|
58
|
+
Note: this only temporarily avoids the issue; broken images will eventually be the
|
|
59
|
+
only version served.
|
|
60
|
+
|
|
61
|
+
Option 3: Use the dtolnay/rust-toolchain action before any Rust operations to
|
|
62
|
+
install or update rustup via the upstream installer, bypassing the Homebrew version.
|
|
63
|
+
fix_code:
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: 'Replace Homebrew rustup with upstream installer before Rust build steps'
|
|
66
|
+
code: |
|
|
67
|
+
jobs:
|
|
68
|
+
build:
|
|
69
|
+
runs-on: macos-latest # or macos-15-arm64
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v6
|
|
72
|
+
|
|
73
|
+
- name: Replace Homebrew rustup with upstream installer
|
|
74
|
+
run: |
|
|
75
|
+
# Remove Homebrew rustup to avoid the broken rustup-init binary
|
|
76
|
+
brew uninstall rustup-init rustup 2>/dev/null || true
|
|
77
|
+
# Install from upstream — stable, no Homebrew dependency
|
|
78
|
+
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path
|
|
79
|
+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
|
80
|
+
|
|
81
|
+
- name: Install Rust target
|
|
82
|
+
run: rustup target add aarch64-apple-darwin
|
|
83
|
+
|
|
84
|
+
- uses: PyO3/maturin-action@v1
|
|
85
|
+
with:
|
|
86
|
+
target: aarch64-apple-darwin
|
|
87
|
+
- language: yaml
|
|
88
|
+
label: 'Use dtolnay/rust-toolchain to install a clean rustup before builds'
|
|
89
|
+
code: |
|
|
90
|
+
jobs:
|
|
91
|
+
build:
|
|
92
|
+
runs-on: macos-latest
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v6
|
|
95
|
+
|
|
96
|
+
- name: Install Rust toolchain (upstream, not Homebrew)
|
|
97
|
+
uses: dtolnay/rust-toolchain@stable
|
|
98
|
+
with:
|
|
99
|
+
components: llvm-tools-preview
|
|
100
|
+
|
|
101
|
+
- name: Build
|
|
102
|
+
run: cargo build --release
|
|
103
|
+
prevention:
|
|
104
|
+
- 'Prefer dtolnay/rust-toolchain or rustup-toolchain-install-master for cross-platform Rust toolchain management rather than relying on the pre-installed Homebrew rustup'
|
|
105
|
+
- 'Test macOS ARM64 jobs on both arm64 and x86-via-Rosetta matrix entries to detect image-lottery regressions early'
|
|
106
|
+
- 'Watch actions/runner-images#14097 for the fix and the image version that resolves the broken rustup-init'
|
|
107
|
+
- 'Avoid using the Homebrew rustup binary directly; upstream rustup at ~/.cargo/bin/rustup is more stable across image updates'
|
|
108
|
+
docs:
|
|
109
|
+
- url: 'https://github.com/actions/runner-images/issues/14097'
|
|
110
|
+
label: 'actions/runner-images#14097: macos-15-arm64/20260511.0048 ships broken Homebrew rustup-init (May 2026)'
|
|
111
|
+
- url: 'https://rustup.rs/'
|
|
112
|
+
label: 'rustup.rs: Official upstream rustup installer'
|
|
113
|
+
- url: 'https://github.com/PyO3/maturin-action'
|
|
114
|
+
label: 'PyO3/maturin-action: Affected action that calls rustup component add'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
id: 'runner-environment-087'
|
|
2
|
+
title: '`ip -d link show` segfaults (exit 139 / SIGSEGV) on VXLAN interfaces on ubuntu-24.04 after kernel 6.14 upgrade'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu-24-04
|
|
7
|
+
- networking
|
|
8
|
+
- vxlan
|
|
9
|
+
- iproute2
|
|
10
|
+
- kernel
|
|
11
|
+
- segfault
|
|
12
|
+
- regression
|
|
13
|
+
- container-networking
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'Segmentation fault.*ip.*link'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'ip -d link show.*exit.*139'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'ip\s+-d\s+link\s+show'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
error_messages:
|
|
22
|
+
- 'Segmentation fault (core dumped)'
|
|
23
|
+
- 'Process completed with exit code 139.'
|
|
24
|
+
- 'ip: Segmentation fault'
|
|
25
|
+
root_cause: |
|
|
26
|
+
On ubuntu-24.04 runner image 20260209.23 and later, the kernel was upgraded to
|
|
27
|
+
6.14.0-1017-azure while the iproute2 package remained at version 6.1.0-1ubuntu6.2.
|
|
28
|
+
|
|
29
|
+
Linux kernel 6.14 introduced a new VXLAN netlink attribute (IFLA_VXLAN_FAN_MAP) in
|
|
30
|
+
the rtnetlink interface. When iproute2 6.1 receives a netlink response containing this
|
|
31
|
+
unrecognized attribute and parses it with the -d (details) flag, it dereferences an
|
|
32
|
+
unexpected attribute structure, triggering a SIGSEGV.
|
|
33
|
+
|
|
34
|
+
The crash occurs specifically when:
|
|
35
|
+
1. A VXLAN network interface exists on the runner (e.g., created with `ip link add ... type vxlan`)
|
|
36
|
+
2. `ip -d link show dev <vxlan-interface>` is executed to inspect its details
|
|
37
|
+
|
|
38
|
+
Without the -d flag, `ip link show` succeeds. The regression was confirmed by comparing:
|
|
39
|
+
- Working image: 20260201.15.1 (kernel 6.11.0-1018-azure, iproute2 6.1.0-1ubuntu6.2)
|
|
40
|
+
- Broken image: 20260209.23.1 (kernel 6.14.0-1017-azure, iproute2 6.1.0-1ubuntu6.2)
|
|
41
|
+
|
|
42
|
+
Affected workflows: any CI that creates VXLAN interfaces for network simulation, overlay
|
|
43
|
+
networking tests, or Kubernetes-in-CI scenarios (e.g., containerlab, kind with custom
|
|
44
|
+
network topologies, network protocol testing).
|
|
45
|
+
|
|
46
|
+
Tracked in actions/runner-images#13669 (February 2026, open).
|
|
47
|
+
fix: |
|
|
48
|
+
Option 1: Upgrade iproute2 to a version compatible with kernel 6.14 at the start of
|
|
49
|
+
the job. Install a newer iproute2 from a backport PPA or compile from source.
|
|
50
|
+
|
|
51
|
+
Option 2: Avoid the -d flag when only basic VXLAN interface information is needed.
|
|
52
|
+
`ip link show dev <vxlan-interface>` (without -d) does not trigger the crash.
|
|
53
|
+
|
|
54
|
+
Option 3: Pin to ubuntu-22.04, which uses an older kernel/iproute2 combination that
|
|
55
|
+
does not exhibit this issue.
|
|
56
|
+
|
|
57
|
+
Option 4: Parse VXLAN interface details via /sys/class/net/ sysfs entries instead of
|
|
58
|
+
relying on `ip -d link show`.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: 'Upgrade iproute2 before creating VXLAN interfaces to avoid segfault'
|
|
62
|
+
code: |
|
|
63
|
+
jobs:
|
|
64
|
+
network-test:
|
|
65
|
+
runs-on: ubuntu-24.04
|
|
66
|
+
steps:
|
|
67
|
+
- name: Upgrade iproute2 to kernel-6.14-compatible version
|
|
68
|
+
run: |
|
|
69
|
+
sudo apt-get update -qq
|
|
70
|
+
sudo apt-get install -y --only-upgrade iproute2
|
|
71
|
+
ip -V # Verify new version
|
|
72
|
+
|
|
73
|
+
- name: Create and inspect VXLAN interface
|
|
74
|
+
run: |
|
|
75
|
+
sudo ip link add vxlan-test type vxlan id 100 \
|
|
76
|
+
local 10.0.0.1 remote 10.0.0.2 dev eth0 dstport 4789
|
|
77
|
+
ip -d link show dev vxlan-test # Safe with upgraded iproute2
|
|
78
|
+
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: 'Avoid -d flag as workaround on ubuntu-24.04 without iproute2 upgrade'
|
|
81
|
+
code: |
|
|
82
|
+
jobs:
|
|
83
|
+
network-test:
|
|
84
|
+
runs-on: ubuntu-24.04
|
|
85
|
+
steps:
|
|
86
|
+
- name: Create VXLAN interface
|
|
87
|
+
run: |
|
|
88
|
+
sudo ip link add vxlan-test type vxlan id 100 dstport 4789
|
|
89
|
+
# Use ip link show WITHOUT -d flag to avoid SIGSEGV on kernel 6.14
|
|
90
|
+
ip link show dev vxlan-test
|
|
91
|
+
# VXLAN-specific attributes via sysfs instead of iproute2 -d output:
|
|
92
|
+
cat /sys/class/net/vxlan-test/carrier || true
|
|
93
|
+
prevention:
|
|
94
|
+
- 'Add an iproute2 upgrade step when workflows inspect VXLAN or other advanced interface types with `ip -d link show`'
|
|
95
|
+
- 'Watch actions/runner-images#13669 for a fix that aligns iproute2 with the kernel 6.14 netlink attributes'
|
|
96
|
+
- 'Test network-intensive CI on both ubuntu-22.04 and ubuntu-24.04 to detect kernel/iproute2 mismatch regressions'
|
|
97
|
+
- 'Consider ubuntu-22.04 for VXLAN-dependent workflows until the iproute2 package is updated on ubuntu-24.04 images'
|
|
98
|
+
docs:
|
|
99
|
+
- url: 'https://github.com/actions/runner-images/issues/13669'
|
|
100
|
+
label: 'actions/runner-images#13669: ip -d link show segfaults on vxlan interfaces — iproute2/kernel 6.14 mismatch'
|
|
101
|
+
- url: 'https://manpages.ubuntu.com/manpages/noble/man8/ip-link.8.html'
|
|
102
|
+
label: 'Ubuntu ip-link manpage: ip link show options'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
id: 'runner-environment-088'
|
|
2
|
+
title: '`ubuntu-slim` runner has empty Node.js toolcache — `setup-node` with semver range downloads latest patch instead of using cached version'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu-slim
|
|
7
|
+
- setup-node
|
|
8
|
+
- toolcache
|
|
9
|
+
- semver
|
|
10
|
+
- caching
|
|
11
|
+
- node
|
|
12
|
+
- version-mismatch
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'evaluating 0 versions'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'Attempting to download.*\.\.\.'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'ubuntu-slim.*setup-node'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
error_messages:
|
|
21
|
+
- '##[debug]evaluating 0 versions'
|
|
22
|
+
- '##[debug]match not found'
|
|
23
|
+
- 'Attempting to download 24.x...'
|
|
24
|
+
- 'Acquiring 24.13.1 - x64 from https://github.com/actions/node-versions/releases/download/'
|
|
25
|
+
root_cause: |
|
|
26
|
+
The ubuntu-slim runner image ships with an empty or absent Node.js toolcache manifest.
|
|
27
|
+
When actions/setup-node evaluates a semver range (e.g., 24, 24.x, 22.x) it first
|
|
28
|
+
inspects the local toolcache versions manifest. On ubuntu-slim, the manifest contains
|
|
29
|
+
zero cached versions (log line: "evaluating 0 versions"), so setup-node falls through
|
|
30
|
+
to download the latest matching version from GitHub releases at runtime.
|
|
31
|
+
|
|
32
|
+
On ubuntu-24.04 and ubuntu-latest, setup-node finds the cached version (e.g., 24.13.0)
|
|
33
|
+
and logs "Found tool in cache". The same workflow on ubuntu-slim downloads a newer
|
|
34
|
+
patch release (e.g., 24.13.1) because the remote versions API returns the freshest
|
|
35
|
+
available release.
|
|
36
|
+
|
|
37
|
+
This causes three distinct problems:
|
|
38
|
+
1. Slower CI: every job downloads Node.js from the network instead of using the cache
|
|
39
|
+
2. Version drift: ubuntu-slim jobs may use a different Node.js version than ubuntu-24.04
|
|
40
|
+
jobs in the same matrix, silently breaking cross-platform consistency checks
|
|
41
|
+
3. Test failures: libraries that cache built binaries keyed on Node.js version will
|
|
42
|
+
produce cache misses across ubuntu-slim and ubuntu-latest jobs in the same run
|
|
43
|
+
|
|
44
|
+
Reproduced on ubuntu-slim image version 20260120.46.1.
|
|
45
|
+
Tracked in actions/runner-images#13671 and actions/setup-node#1492 (February 2026).
|
|
46
|
+
fix: |
|
|
47
|
+
Option 1: Pin an explicit Node.js version (e.g., 24.13.0) instead of a semver range.
|
|
48
|
+
setup-node downloads this version on ubuntu-slim, but the result is deterministic and
|
|
49
|
+
matches any version you also pin on ubuntu-latest or ubuntu-24.04.
|
|
50
|
+
|
|
51
|
+
Option 2: Use a .node-version or .nvmrc file with an exact version. setup-node reads
|
|
52
|
+
the file and pins to that version on all runner images.
|
|
53
|
+
|
|
54
|
+
Option 3: Use actions/cache to manually cache the Node.js installation directory on
|
|
55
|
+
ubuntu-slim jobs.
|
|
56
|
+
|
|
57
|
+
Option 4: Avoid ubuntu-slim for workflows that require a specific Node.js toolcache
|
|
58
|
+
version. Use ubuntu-24.04 or ubuntu-latest which ship pre-cached Node.js versions.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: 'Pin exact Node.js version to avoid toolcache miss on ubuntu-slim'
|
|
62
|
+
code: |
|
|
63
|
+
jobs:
|
|
64
|
+
build:
|
|
65
|
+
runs-on: ubuntu-slim # empty toolcache
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v6
|
|
68
|
+
|
|
69
|
+
# WRONG: semver range downloads latest patch on ubuntu-slim
|
|
70
|
+
# - uses: actions/setup-node@v6
|
|
71
|
+
# with:
|
|
72
|
+
# node-version: '24' # evaluating 0 versions → downloads 24.13.1
|
|
73
|
+
|
|
74
|
+
# CORRECT: explicit version is deterministic on all images
|
|
75
|
+
- uses: actions/setup-node@v6
|
|
76
|
+
with:
|
|
77
|
+
node-version: '24.13.0' # exact — no toolcache lookup needed
|
|
78
|
+
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: 'Use .node-version file for consistent version across all runner images'
|
|
81
|
+
code: |
|
|
82
|
+
# .node-version (at repo root):
|
|
83
|
+
# 24.13.0
|
|
84
|
+
|
|
85
|
+
jobs:
|
|
86
|
+
build:
|
|
87
|
+
runs-on: ${{ matrix.os }}
|
|
88
|
+
strategy:
|
|
89
|
+
matrix:
|
|
90
|
+
os: [ubuntu-slim, ubuntu-24.04, macos-latest]
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v6
|
|
93
|
+
|
|
94
|
+
- uses: actions/setup-node@v6
|
|
95
|
+
with:
|
|
96
|
+
node-version-file: '.node-version' # same version on all images
|
|
97
|
+
prevention:
|
|
98
|
+
- 'Use exact Node.js versions (not semver ranges like 24.x) when running on ubuntu-slim to ensure deterministic toolcache behavior'
|
|
99
|
+
- 'Add a .node-version or .nvmrc file at the repo root and reference it with node-version-file: for consistent versions across all runner images'
|
|
100
|
+
- 'In matrix builds mixing ubuntu-slim with ubuntu-24.04, verify that node --version outputs match to detect silent version drift'
|
|
101
|
+
- 'Watch actions/runner-images#13671 for a fix that pre-populates the ubuntu-slim toolcache with a Node.js manifest'
|
|
102
|
+
docs:
|
|
103
|
+
- url: 'https://github.com/actions/runner-images/issues/13671'
|
|
104
|
+
label: 'actions/runner-images#13671: setup-node incorrectly downloads latest on ubuntu-slim'
|
|
105
|
+
- url: 'https://github.com/actions/setup-node/issues/1492'
|
|
106
|
+
label: 'actions/setup-node#1492: ubuntu-slim toolcache manifest missing'
|
|
107
|
+
- url: 'https://github.com/actions/setup-node#usage'
|
|
108
|
+
label: 'actions/setup-node documentation: node-version-file option'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
id: 'silent-failures-038'
|
|
2
|
+
title: '`download-artifact` `merge-multiple: true` silently corrupts files when multiple artifacts contain identical filenames'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- download-artifact
|
|
7
|
+
- merge-multiple
|
|
8
|
+
- file-corruption
|
|
9
|
+
- parallel-download
|
|
10
|
+
- race-condition
|
|
11
|
+
- artifacts
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'merge-multiple:\s*true'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Downloading artifact:.*\n.*Downloading artifact:'
|
|
16
|
+
flags: 'im'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'Downloading artifact: build-linux to /home/runner/work/repo/repo/dist'
|
|
19
|
+
- 'Downloading artifact: build-macos to /home/runner/work/repo/repo/dist'
|
|
20
|
+
root_cause: |
|
|
21
|
+
When actions/download-artifact (v4+) is used with merge-multiple: true and two or more
|
|
22
|
+
artifacts contain files with identical relative paths (e.g., multiple matrix builds each
|
|
23
|
+
upload a file named checksums.txt or report.json), the action downloads all artifacts
|
|
24
|
+
in parallel using a PARALLEL_DOWNLOADS worker pool.
|
|
25
|
+
|
|
26
|
+
All parallel workers write to the same destination directory concurrently. When two
|
|
27
|
+
workers attempt to write to the same filename simultaneously, the result is undefined:
|
|
28
|
+
- The output file may contain interleaved bytes from both sources
|
|
29
|
+
- The file may be truncated to the size of the smaller upload
|
|
30
|
+
- One artifact's version may completely overwrite the other (last writer wins)
|
|
31
|
+
|
|
32
|
+
The action exits with code 0 and emits no error or warning. The log shows both artifacts
|
|
33
|
+
downloading normally. The resulting corrupt file is indistinguishable from a valid one
|
|
34
|
+
unless explicitly checksummed.
|
|
35
|
+
|
|
36
|
+
Bug confirmed in actions/download-artifact#395 (March 2025, still open as of Jan 2026).
|
|
37
|
+
Affects: v4.x, v5.x, v6.x, v7.x
|
|
38
|
+
|
|
39
|
+
Common patterns that trigger this bug:
|
|
40
|
+
- Matrix builds across OS/arch each producing a same-named binary or manifest
|
|
41
|
+
- Release workflows where each artifact contains a same-named README.md or LICENSE
|
|
42
|
+
- Merge-and-sign workflows where each per-platform artifact has a checksums.txt
|
|
43
|
+
fix: |
|
|
44
|
+
Option 1 — Rename artifact files to include OS/arch before uploading so no two
|
|
45
|
+
artifacts ever contain a file at the same relative path.
|
|
46
|
+
|
|
47
|
+
Option 2 — Download each artifact by name into a separate directory rather than using
|
|
48
|
+
merge-multiple: true.
|
|
49
|
+
|
|
50
|
+
Option 3 — Validate checksums of all critical files after downloading and before
|
|
51
|
+
using them, so corruption is detected rather than silently propagated.
|
|
52
|
+
|
|
53
|
+
Option 4 — Use pattern: to download specific artifact subsets one group at a time,
|
|
54
|
+
ensuring no overlapping filenames within each group.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'Include OS/arch in output filenames to eliminate same-path collisions'
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
build:
|
|
61
|
+
strategy:
|
|
62
|
+
matrix:
|
|
63
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
64
|
+
runs-on: ${{ matrix.os }}
|
|
65
|
+
steps:
|
|
66
|
+
- name: Build and rename output to include OS identifier
|
|
67
|
+
run: |
|
|
68
|
+
cp build/output dist/output-${{ runner.os }}-${{ runner.arch }}
|
|
69
|
+
|
|
70
|
+
- uses: actions/upload-artifact@v4
|
|
71
|
+
with:
|
|
72
|
+
name: build-${{ runner.os }}-${{ runner.arch }}
|
|
73
|
+
path: dist/output-${{ runner.os }}-${{ runner.arch }}
|
|
74
|
+
|
|
75
|
+
release:
|
|
76
|
+
needs: build
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- name: Download all artifacts (safe — all filenames are unique)
|
|
80
|
+
uses: actions/download-artifact@v4
|
|
81
|
+
with:
|
|
82
|
+
pattern: build-*
|
|
83
|
+
path: dist/
|
|
84
|
+
merge-multiple: true
|
|
85
|
+
- language: yaml
|
|
86
|
+
label: 'Download each artifact by name into a separate directory (avoids merge-multiple entirely)'
|
|
87
|
+
code: |
|
|
88
|
+
jobs:
|
|
89
|
+
release:
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: build-Linux
|
|
95
|
+
path: dist/linux/
|
|
96
|
+
|
|
97
|
+
- uses: actions/download-artifact@v4
|
|
98
|
+
with:
|
|
99
|
+
name: build-macOS
|
|
100
|
+
path: dist/macos/
|
|
101
|
+
|
|
102
|
+
- uses: actions/download-artifact@v4
|
|
103
|
+
with:
|
|
104
|
+
name: build-Windows
|
|
105
|
+
path: dist/windows/
|
|
106
|
+
|
|
107
|
+
# Files are in separate directories — no parallel write collision possible
|
|
108
|
+
prevention:
|
|
109
|
+
- 'Always include a unique dimension (OS, arch, matrix value) in filenames when uploading from matrix jobs'
|
|
110
|
+
- 'Audit all artifacts before using merge-multiple: true to confirm no two artifacts share a file path'
|
|
111
|
+
- 'Verify checksums of all merged files before using them in signing, publishing, or deployment steps'
|
|
112
|
+
- 'Prefer downloading by name into separate directories over merge-multiple when file collisions are possible'
|
|
113
|
+
docs:
|
|
114
|
+
- url: 'https://github.com/actions/download-artifact/issues/395'
|
|
115
|
+
label: 'actions/download-artifact#395: merge-multiple silently corrupts files when same-named files exist (open, March 2025)'
|
|
116
|
+
- url: 'https://github.com/actions/download-artifact/blob/main/docs/MIGRATION.md#merging-multiple-artifacts'
|
|
117
|
+
label: 'download-artifact migration guide: Merging multiple artifacts'
|
|
118
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflows-do/storing-workflow-data-as-artifacts'
|
|
119
|
+
label: 'GitHub Docs: Storing workflow data as artifacts'
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: 'silent-failures-039'
|
|
2
|
+
title: '`GITHUB_STEP_SUMMARY` content silently discarded when write size approaches the 1 MiB per-step limit'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- step-summary
|
|
7
|
+
- github-step-summary
|
|
8
|
+
- silent-discard
|
|
9
|
+
- 1mib-limit
|
|
10
|
+
- job-summary
|
|
11
|
+
- reporting
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'GITHUB_STEP_SUMMARY'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: '\$env:GITHUB_STEP_SUMMARY'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'Summary content written to /home/runner/work/_temp/_github_workflow/step_summary_*.md'
|
|
19
|
+
- 'Summary content written to C:\Users\runneradmin\AppData\Local\Temp\_github_workflow\step_summary_*.md'
|
|
20
|
+
root_cause: |
|
|
21
|
+
The GitHub Actions runner enforces a 1 MiB (1,048,576 bytes) per-step size limit on
|
|
22
|
+
GITHUB_STEP_SUMMARY content. When a step writes content that is within approximately
|
|
23
|
+
2 bytes of this limit, the runner silently discards the entire summary with no error
|
|
24
|
+
message, no warning, and no non-zero exit code.
|
|
25
|
+
|
|
26
|
+
Two distinct bugs are present (both observed on runner 2.333.1+):
|
|
27
|
+
|
|
28
|
+
Bug 1 — Off-by-one at the limit boundary: Content that is exactly (1 MiB - 1) bytes
|
|
29
|
+
is rejected as "too long" even though it is one byte below the documented limit.
|
|
30
|
+
The effective hard limit is (1 MiB - 2) bytes, not 1 MiB.
|
|
31
|
+
|
|
32
|
+
Bug 2 — Silent discard near the boundary: Content written when the running total is
|
|
33
|
+
within ~2 bytes of the effective limit is silently dropped. The write succeeds (no
|
|
34
|
+
error), the step exits 0, but the summary does not appear in the GitHub Actions UI.
|
|
35
|
+
There is no log line, annotation, or job-level error indicating the drop occurred.
|
|
36
|
+
|
|
37
|
+
This is particularly dangerous for CI workflows that rely on step summary for:
|
|
38
|
+
- Test result tables (many failed tests → large HTML tables)
|
|
39
|
+
- Build logs written to summary for easy access
|
|
40
|
+
- Deployment manifests or diff outputs
|
|
41
|
+
- Security scan results
|
|
42
|
+
|
|
43
|
+
The content is lost at job execution time; there is no way to recover it after the fact.
|
|
44
|
+
The only indication is that the summary section in the GitHub UI appears blank or shows
|
|
45
|
+
less content than expected.
|
|
46
|
+
|
|
47
|
+
Tracked in actions/runner#4337 (April 2026, open).
|
|
48
|
+
fix: |
|
|
49
|
+
Truncate summary content to a safe size before writing to GITHUB_STEP_SUMMARY.
|
|
50
|
+
Leave a margin of ~5 KB below the 1 MiB limit to account for multi-step accumulation
|
|
51
|
+
and the off-by-one bug.
|
|
52
|
+
|
|
53
|
+
For multi-step workflows, track cumulative size: each step's contribution adds to the
|
|
54
|
+
total step summary for the job. The 1 MiB limit applies per step, not per job.
|
|
55
|
+
|
|
56
|
+
Always validate that critical summary content was written by checking the file size
|
|
57
|
+
after the write, not by relying on exit code alone.
|
|
58
|
+
fix_code:
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: 'Truncate step summary to safe size before writing (bash)'
|
|
61
|
+
code: |
|
|
62
|
+
jobs:
|
|
63
|
+
test:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Run tests and write summary
|
|
67
|
+
run: |
|
|
68
|
+
# Generate potentially large summary content
|
|
69
|
+
content=$(cat test-results.md 2>/dev/null || echo "No results")
|
|
70
|
+
|
|
71
|
+
# Enforce safe limit: 1 MiB minus 10 KB buffer
|
|
72
|
+
max_bytes=$(( 1048576 - 10240 ))
|
|
73
|
+
content_bytes=${#content}
|
|
74
|
+
|
|
75
|
+
if [ "$content_bytes" -gt "$max_bytes" ]; then
|
|
76
|
+
content="${content:0:$max_bytes}"
|
|
77
|
+
content="${content}"$'\n\n> **Note:** Summary truncated — exceeded safe 1 MiB limit.'
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
printf '%s' "$content" >> "$GITHUB_STEP_SUMMARY"
|
|
81
|
+
- language: yaml
|
|
82
|
+
label: 'Truncate step summary to safe size before writing (PowerShell)'
|
|
83
|
+
code: |
|
|
84
|
+
jobs:
|
|
85
|
+
test:
|
|
86
|
+
runs-on: windows-latest
|
|
87
|
+
steps:
|
|
88
|
+
- name: Write test summary with size guard
|
|
89
|
+
shell: pwsh
|
|
90
|
+
run: |
|
|
91
|
+
$content = Get-Content test-results.md -Raw -ErrorAction SilentlyContinue
|
|
92
|
+
if (-not $content) { $content = "No test results found." }
|
|
93
|
+
|
|
94
|
+
# Safe limit: 1 MiB minus 10 KB buffer (accounts for off-by-one bug)
|
|
95
|
+
$maxBytes = 1048576 - 10240
|
|
96
|
+
$bytes = [System.Text.Encoding]::UTF8.GetByteCount($content)
|
|
97
|
+
|
|
98
|
+
if ($bytes -gt $maxBytes) {
|
|
99
|
+
# Truncate and add warning marker
|
|
100
|
+
$truncated = $content.Substring(0, [Math]::Min($content.Length, $maxBytes / 2))
|
|
101
|
+
$content = $truncated + "`n`n> **Note:** Summary truncated — exceeded safe 1 MiB limit."
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
$content | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8
|
|
105
|
+
prevention:
|
|
106
|
+
- 'Always apply a size guard before writing to GITHUB_STEP_SUMMARY — use (1 MiB - 10 KB) as a safe upper bound'
|
|
107
|
+
- 'For test result summaries, limit to the N most-recent or most-critical failures rather than dumping all output'
|
|
108
|
+
- 'Verify critical summary writes by checking file size after the write step, not by relying on exit code 0'
|
|
109
|
+
- 'Track actions/runner#4337 for a runner fix that either raises the limit or surfaces an error when content is discarded'
|
|
110
|
+
- 'Split large summaries across multiple steps — but note each step has its own 1 MiB budget, not a shared pool'
|
|
111
|
+
docs:
|
|
112
|
+
- url: 'https://github.com/actions/runner/issues/4337'
|
|
113
|
+
label: 'actions/runner#4337: GITHUB_STEP_SUMMARY content silently dropped when size approaches 1 MiB limit (April 2026)'
|
|
114
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#adding-a-job-summary'
|
|
115
|
+
label: 'GitHub Docs: Adding a job summary — GITHUB_STEP_SUMMARY documentation and limits'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
id: 'triggers-027'
|
|
2
|
+
title: 'Copilot coding agent PR workflows stuck in `action_required` — require human approval before running'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- copilot
|
|
7
|
+
- coding-agent
|
|
8
|
+
- approval-required
|
|
9
|
+
- action-required
|
|
10
|
+
- pull-request
|
|
11
|
+
- workflow-approval
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'action_required'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'waiting for approval.*(?:outside contributor|fork|bot)|Approve and run'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'This workflow is waiting for approval from a maintainer. Learn more about approving workflows from public forks.'
|
|
19
|
+
- 'Workflow run is in action_required state. Approval is required before jobs can start.'
|
|
20
|
+
root_cause: |
|
|
21
|
+
Starting April 2025, GitHub treats the Copilot coding agent as an "outside contributor"
|
|
22
|
+
for GitHub Actions workflow approval purposes — the same policy applied to first-time
|
|
23
|
+
contributors from public forks.
|
|
24
|
+
|
|
25
|
+
When Copilot coding agent opens a pull request or pushes commits to a branch:
|
|
26
|
+
- Associated CI/CD workflows do NOT run automatically
|
|
27
|
+
- The check suite is created with conclusion: action_required
|
|
28
|
+
- No jobs start until a human with repository write access (or actions:write) manually
|
|
29
|
+
clicks "Approve and run workflows" in the PR UI, or approves via the REST API
|
|
30
|
+
- Unapproved runs are automatically deleted after 30 days with no notification
|
|
31
|
+
|
|
32
|
+
This is by design: Copilot-triggered workflows may have access to GITHUB_TOKEN,
|
|
33
|
+
repository secrets, and deployment environments. The approval gate prevents a
|
|
34
|
+
compromised or misbehaving AI agent from triggering arbitrary CI/CD actions.
|
|
35
|
+
|
|
36
|
+
However, this significantly slows AI-assisted development loops where developers rely
|
|
37
|
+
on CI feedback to validate Copilot's work. Teams discover this when:
|
|
38
|
+
- All Copilot-opened PRs show "Waiting for approval" with no CI results
|
|
39
|
+
- Required status checks never populate, blocking PR merges
|
|
40
|
+
- Automated pipelines that expected CI to run on all PRs silently stall
|
|
41
|
+
|
|
42
|
+
Affects: all GitHub Actions workflows triggered by pull_request, push, or
|
|
43
|
+
pull_request_target events on branches where Copilot coding agent is the commit author.
|
|
44
|
+
fix: |
|
|
45
|
+
Option 1 (Recommended for trusted private repos): Enable automatic workflow runs for
|
|
46
|
+
Copilot coding agent in repository settings:
|
|
47
|
+
Settings → Actions → General → "Approval for running workflows triggered by Copilot
|
|
48
|
+
coding agent" → "Allow Copilot coding agent to trigger workflows without approval"
|
|
49
|
+
(Available since March 2026 changelog)
|
|
50
|
+
|
|
51
|
+
Option 2: Manually approve each run after Copilot opens a PR.
|
|
52
|
+
In the PR, click "Approve and run workflows" in the Checks section.
|
|
53
|
+
|
|
54
|
+
Option 3: Build a lightweight auto-approve workflow triggered by check_suite events
|
|
55
|
+
with action: requested, scoped to Copilot actor identity.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: 'Approve a pending workflow run via GitHub CLI'
|
|
59
|
+
code: |
|
|
60
|
+
# List runs awaiting approval on a Copilot PR branch:
|
|
61
|
+
# gh run list --repo owner/repo --branch copilot/issue-123
|
|
62
|
+
|
|
63
|
+
# Approve a specific run (requires write access):
|
|
64
|
+
# gh run review <run-id> --approve --repo owner/repo
|
|
65
|
+
|
|
66
|
+
# Or approve via REST API:
|
|
67
|
+
# POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: 'Auto-approve workflow: trigger CI immediately on Copilot PRs (use with caution)'
|
|
70
|
+
code: |
|
|
71
|
+
name: Auto-approve Copilot workflows
|
|
72
|
+
on:
|
|
73
|
+
workflow_run:
|
|
74
|
+
workflows: ['CI']
|
|
75
|
+
types: [requested]
|
|
76
|
+
|
|
77
|
+
jobs:
|
|
78
|
+
approve:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
# Only auto-approve if triggered by Copilot coding agent
|
|
81
|
+
if: github.event.workflow_run.actor.login == 'copilot-swe-agent[bot]'
|
|
82
|
+
permissions:
|
|
83
|
+
actions: write
|
|
84
|
+
steps:
|
|
85
|
+
- name: Approve the workflow run
|
|
86
|
+
env:
|
|
87
|
+
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
88
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
89
|
+
run: |
|
|
90
|
+
gh run review $RUN_ID --approve --repo ${{ github.repository }}
|
|
91
|
+
prevention:
|
|
92
|
+
- 'Configure the "Allow Copilot coding agent to trigger workflows without approval" setting if your team uses Copilot heavily and the repo is trusted'
|
|
93
|
+
- 'Monitor for action_required check suite conclusions so stalled Copilot PRs do not block merges silently'
|
|
94
|
+
- 'Scope GITHUB_TOKEN permissions to the minimum required before enabling auto-approval, to reduce the blast radius if Copilot misbehaves'
|
|
95
|
+
- 'Set up required status checks carefully — if CI never runs, required checks will block PR merges indefinitely'
|
|
96
|
+
docs:
|
|
97
|
+
- url: 'https://github.blog/changelog/2025-04-15-upcoming-breaking-changes-and-releases-for-github-actions/'
|
|
98
|
+
label: 'GitHub Changelog: Copilot events not automatically triggering GitHub Actions workflows (April 2025)'
|
|
99
|
+
- url: 'https://github.blog/changelog/2026-03-13-optionally-skip-approval-for-copilot-coding-agent-actions-workflows/'
|
|
100
|
+
label: 'GitHub Changelog: Optionally skip approval for Copilot coding agent Actions workflows (March 2026)'
|
|
101
|
+
- url: 'https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/approving-workflow-runs-from-public-forks'
|
|
102
|
+
label: 'GitHub Docs: Approving workflow runs from public forks'
|
|
103
|
+
- url: 'https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/configuring-settings-for-github-copilot-coding-agent'
|
|
104
|
+
label: 'GitHub Docs: Configuring settings for GitHub Copilot coding agent'
|
package/package.json
CHANGED