@htekdev/actions-debugger 1.0.23 → 1.0.24
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/artifact-minimum-retention-one-day.yml +153 -0
- package/errors/caching-artifacts/cache-api-propagation-delay-post-save.yml +128 -0
- package/errors/caching-artifacts/cache-backend-internal-error-skipped.yml +75 -0
- package/errors/caching-artifacts/cache-hit-step-id-case-sensitive-mismatch.yml +95 -0
- package/errors/caching-artifacts/cache-save-post-step-skipped-on-failure.yml +114 -0
- package/errors/concurrency-timing/deploy-pages-in-progress-deployment-wedged.yml +70 -0
- package/errors/concurrency-timing/deployment-review-timeout-expired.yml +88 -0
- package/errors/concurrency-timing/job-concurrency-scope-per-run-not-global.yml +81 -0
- package/errors/concurrency-timing/merge-queue-concurrency-cancel-blocks-all.yml +86 -0
- package/errors/concurrency-timing/reusable-workflow-github-workflow-context-cancel.yml +124 -0
- package/errors/concurrency-timing/runner-scale-set-jobs-never-start.yml +123 -0
- package/errors/concurrency-timing/runner-temp-dir-race-concurrent-workers.yml +90 -0
- package/errors/known-unsolved/artifact-download-url-unauthenticated-404.yml +98 -0
- package/errors/known-unsolved/checkout-v6-credentials-docker-run-manual.yml +105 -0
- package/errors/known-unsolved/concurrency-groups-repo-scoped-only.yml +138 -0
- package/errors/known-unsolved/matrix-256-job-limit.yml +142 -0
- package/errors/known-unsolved/merge-group-paths-filter-not-supported.yml +137 -0
- package/errors/known-unsolved/no-job-allow-failure.yml +73 -0
- package/errors/known-unsolved/schedule-cron-hours-long-queue-drift.yml +101 -0
- package/errors/permissions-auth/checkout-persist-credentials-token-write.yml +90 -0
- package/errors/permissions-auth/create-github-app-token-cross-job-token-revoked.yml +95 -0
- package/errors/permissions-auth/github-token-contents-write-missing-git-push.yml +117 -0
- package/errors/permissions-auth/org-actions-policy-blocks-unapproved-action.yml +106 -0
- package/errors/runner-environment/codeql-action-v2-deprecated.yml +110 -0
- package/errors/runner-environment/macos-26-openssl-3-system-library-breaking.yml +114 -0
- package/errors/runner-environment/macos-26-ruby-34-default-upgrade.yml +114 -0
- package/errors/runner-environment/macos-26-xcode-default-265-pin-required.yml +99 -0
- package/errors/runner-environment/macos-latest-label-switches-to-macos26.yml +127 -0
- package/errors/runner-environment/node20-removed-toolcache-default-node22.yml +104 -0
- package/errors/runner-environment/powershell-74-76-threadjob-module-rename.yml +124 -0
- package/errors/runner-environment/self-hosted-runner-not-found.yml +134 -0
- package/errors/runner-environment/self-hosted-runner-selinux-service-exec-failure.yml +116 -0
- package/errors/runner-environment/service-container-no-healthcheck.yml +158 -0
- package/errors/runner-environment/setup-node-v5-corepack-pnpm-not-found.yml +101 -0
- package/errors/runner-environment/setup-node-yarn-not-installed-self-hosted.yml +76 -0
- package/errors/runner-environment/setup-python-externally-managed-env-error.yml +95 -0
- package/errors/runner-environment/windows-2019-runner-retired-june2025.yml +118 -0
- package/errors/runner-environment/windows-2022-docker-daemon-not-started.yml +108 -0
- package/errors/silent-failures/cache-hit-output-string-not-boolean.yml +96 -0
- package/errors/silent-failures/checkout-lfs-pointer-not-content.yml +105 -0
- package/errors/silent-failures/reusable-workflow-output-skipped-contains-secret.yml +115 -0
- package/errors/silent-failures/setup-node-silent-download-exit-zero.yml +105 -0
- package/errors/silent-failures/setup-python-truncated-manifest-silent-exit.yml +111 -0
- package/errors/silent-failures/undefined-env-expression-empty-string-silent.yml +115 -0
- package/errors/silent-failures/windows-powershell-github-output-bash-syntax.yml +118 -0
- package/errors/triggers/fork-pr-first-time-contributor-approval-required.yml +142 -0
- package/errors/triggers/on-push-branches-glob-star-no-slash-match.yml +78 -0
- package/errors/triggers/pull-request-target-env-protection-default-branch-eval.yml +117 -0
- package/errors/triggers/required-status-check-renamed-never-passes.yml +87 -0
- package/errors/triggers/schedule-cron-self-hosted-runner-not-triggered.yml +107 -0
- package/errors/yaml-syntax/composite-action-run-shell-missing.yml +90 -0
- package/errors/yaml-syntax/composite-action-secrets-context-unavailable.yml +99 -0
- package/errors/yaml-syntax/github-script-octokit-renamed-to-github.yml +130 -0
- package/errors/yaml-syntax/labeler-v5-config-format-breaking.yml +67 -0
- package/errors/yaml-syntax/runs-on-expression-array-syntax-error.yml +121 -0
- package/errors/yaml-syntax/setup-go-matrix-version-float-coercion.yml +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: silent-failures-030
|
|
2
|
+
title: "Reusable Workflow Output Silently Dropped When Value Contains a Secret Substring"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- reusable-workflows
|
|
7
|
+
- secrets-masking
|
|
8
|
+
- workflow-outputs
|
|
9
|
+
- workflow-call
|
|
10
|
+
- secret-substring
|
|
11
|
+
- output-propagation
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "Skip output '\\w+' since it may contain secret"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "Skip output.*may contain secret"
|
|
16
|
+
flags: "i"
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Skip output 'file-url' since it may contain secret."
|
|
19
|
+
- "Skip output 'artifact-path' since it may contain secret."
|
|
20
|
+
- "Skip output 'deploy-url' since it may contain secret."
|
|
21
|
+
root_cause: |
|
|
22
|
+
When a reusable workflow (called via `workflow_call`) propagates a workflow-level `output`
|
|
23
|
+
whose value contains a substring matching any registered secret, the runner **silently drops
|
|
24
|
+
the entire output** and logs "Skip output 'X' since it may contain secret." in the callee
|
|
25
|
+
workflow's log. The calling workflow's `needs.<job>.outputs.X` resolves to an empty string
|
|
26
|
+
with no error or warning in the caller's logs.
|
|
27
|
+
|
|
28
|
+
The runner performs substring matching — if ANY registered secret appears anywhere within the
|
|
29
|
+
output string (even as a short substring), the runner refuses to propagate the output. Common
|
|
30
|
+
triggers:
|
|
31
|
+
- S3 URLs where part of the bucket path matches an AWS access key fragment
|
|
32
|
+
- File paths with directory components that happen to match a short registered secret
|
|
33
|
+
- Outputs containing account IDs, port numbers, or other short secrets that appear in
|
|
34
|
+
longer strings by coincidence
|
|
35
|
+
- Base64-encoded outputs whose encoding coincidentally contains a secret substring
|
|
36
|
+
|
|
37
|
+
This is distinct from `job-output-masked-as-secret-empty` (regular job step outputs masked
|
|
38
|
+
by actions/runner#1498). The "Skip output" message only appears in reusable workflow output
|
|
39
|
+
propagation and is ONLY visible in the callee's log — the caller shows no warning, making
|
|
40
|
+
diagnosis extremely difficult.
|
|
41
|
+
fix: |
|
|
42
|
+
Avoid including values that contain (or match substrings of) registered secrets in reusable
|
|
43
|
+
workflow outputs. Structural approaches:
|
|
44
|
+
|
|
45
|
+
1. **Return only the non-secret portion** of a path or URL, and reconstruct the full value
|
|
46
|
+
in the calling workflow using `vars` context or a known prefix.
|
|
47
|
+
2. **Use artifacts** (`actions/upload-artifact` / `actions/download-artifact`) to pass files
|
|
48
|
+
or large data payloads between the callee and caller instead of workflow outputs.
|
|
49
|
+
3. **Check callee logs** for "Skip output" messages — they are invisible from the caller side.
|
|
50
|
+
4. **Review short secrets**: secrets shorter than ~8 characters risk false-positive substring
|
|
51
|
+
matches. Consider rotating short secrets to longer values.
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: "Return non-secret path portion; reconstruct in caller"
|
|
55
|
+
code: |
|
|
56
|
+
# ❌ BROKEN: output contains secret substring (S3 bucket name matches a secret)
|
|
57
|
+
on:
|
|
58
|
+
workflow_call:
|
|
59
|
+
outputs:
|
|
60
|
+
artifact-url:
|
|
61
|
+
value: ${{ jobs.build.outputs.url }} # silently skipped if URL contains secret
|
|
62
|
+
jobs:
|
|
63
|
+
build:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
outputs:
|
|
66
|
+
url: ${{ steps.upload.outputs.file-url }}
|
|
67
|
+
steps:
|
|
68
|
+
- id: upload
|
|
69
|
+
run: |
|
|
70
|
+
echo "file-url=https://s3.amazonaws.com/${{ secrets.BUCKET_NAME }}/build-${{ github.run_id }}.zip" \
|
|
71
|
+
>> "$GITHUB_OUTPUT"
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
# ✅ WORKAROUND: return only the non-secret key; caller prepends known S3 prefix
|
|
76
|
+
on:
|
|
77
|
+
workflow_call:
|
|
78
|
+
outputs:
|
|
79
|
+
artifact-key:
|
|
80
|
+
value: ${{ jobs.build.outputs.key }} # just "build-12345678.zip" — no secret
|
|
81
|
+
jobs:
|
|
82
|
+
build:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
outputs:
|
|
85
|
+
key: ${{ steps.upload.outputs.key }}
|
|
86
|
+
steps:
|
|
87
|
+
- id: upload
|
|
88
|
+
run: echo "key=build-${{ github.run_id }}.zip" >> "$GITHUB_OUTPUT"
|
|
89
|
+
- language: yaml
|
|
90
|
+
label: "Use artifacts to pass files between reusable workflow and caller"
|
|
91
|
+
code: |
|
|
92
|
+
# In the reusable workflow job:
|
|
93
|
+
- uses: actions/upload-artifact@v4
|
|
94
|
+
with:
|
|
95
|
+
name: build-output-${{ github.run_id }}
|
|
96
|
+
path: dist/
|
|
97
|
+
|
|
98
|
+
# In the calling workflow job (after the reusable workflow completes):
|
|
99
|
+
- uses: actions/download-artifact@v4
|
|
100
|
+
with:
|
|
101
|
+
name: build-output-${{ github.run_id }}
|
|
102
|
+
path: dist/
|
|
103
|
+
prevention:
|
|
104
|
+
- "Never include secret-containing values (URLs, paths, credentials) as reusable workflow outputs."
|
|
105
|
+
- "Reserve workflow outputs for short, non-sensitive metadata: version strings, boolean flags, run IDs."
|
|
106
|
+
- "Use artifacts for any data payload that might contain a value related to a registered secret."
|
|
107
|
+
- "Always check callee workflow logs — not just caller logs — for 'Skip output' messages."
|
|
108
|
+
- "Register secrets that are long and unique enough not to appear as substrings in build output values."
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://stackoverflow.com/questions/72536256/output-in-reusable-workflow-is-incorrectly-recognized-as-secret-github-actions"
|
|
111
|
+
label: "SO#72536256 — Output incorrectly recognized as secret in reusable workflow (5,709 views)"
|
|
112
|
+
- url: "https://stackoverflow.com/questions/75061897/github-actions-incorrectly-thinks-variable-is-a-secret-and-so-does-not-set-outpu"
|
|
113
|
+
label: "SO#75061897 — Actions incorrectly thinks variable is a secret (2,780 views)"
|
|
114
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#using-outputs-from-a-reusable-workflow"
|
|
115
|
+
label: "GitHub Docs: Using outputs from a reusable workflow"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
id: silent-failures-028
|
|
2
|
+
title: "setup-node Silently Exits 0 Without Installing Node.js on Self-Hosted Runners"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- setup-node
|
|
7
|
+
- self-hosted
|
|
8
|
+
- download-failure
|
|
9
|
+
- silent-exit
|
|
10
|
+
- node-not-found
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Attempting to download [0-9]+\.[0-9]+\.[0-9]+\.\.\.'
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "exec: node: not found"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "node: not found"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Attempting to download 24.15.0..."
|
|
20
|
+
- "exec: node: not found"
|
|
21
|
+
- "node: not found"
|
|
22
|
+
- "/usr/bin/bash: node: not found"
|
|
23
|
+
root_cause: |
|
|
24
|
+
On self-hosted runners (particularly those using `gha-runner-scale-set` or ephemeral
|
|
25
|
+
ARC runners), `actions/setup-node` can sporadically print
|
|
26
|
+
"Attempting to download <version>..." and then silently exit 0 without completing
|
|
27
|
+
the download or install.
|
|
28
|
+
|
|
29
|
+
The step outcome is `success` and the runner logs show no error — the action
|
|
30
|
+
simply returns without installing Node.js. Subsequent steps that invoke `node`,
|
|
31
|
+
`npm`, `pnpm`, or other Node-dependent tools then fail with "exec: node: not found"
|
|
32
|
+
or equivalent errors, which are far from the actual root cause.
|
|
33
|
+
|
|
34
|
+
This is a transient network or HTTP client failure in the `@actions/tool-cache`
|
|
35
|
+
download path. When the initial download attempt encounters a non-fatal HTTP error
|
|
36
|
+
(connection reset, early EOF, or interrupted read), the action sometimes swallows
|
|
37
|
+
the error and exits cleanly rather than retrying or calling `core.setFailed()`.
|
|
38
|
+
The issue is more common on self-hosted runners where network conditions are more
|
|
39
|
+
variable than on GitHub-hosted runners.
|
|
40
|
+
|
|
41
|
+
The action version affected is v6.x on self-hosted runners with `gha-runner-scale-set`
|
|
42
|
+
0.13.x and runner 2.334.x. Retrying the job almost always succeeds.
|
|
43
|
+
fix: |
|
|
44
|
+
Add an explicit Node version verification step after setup-node to catch silent
|
|
45
|
+
failures before they cause confusing downstream errors:
|
|
46
|
+
|
|
47
|
+
- run: node --version
|
|
48
|
+
|
|
49
|
+
If this step fails, the issue is with setup-node — not the downstream tool.
|
|
50
|
+
|
|
51
|
+
For persistent failures, add a retry wrapper around setup-node, or use the
|
|
52
|
+
runner's pre-installed Node.js version as a fallback by pinning node-version
|
|
53
|
+
to the runner's system Node.
|
|
54
|
+
|
|
55
|
+
Long-term: upgrade to the latest setup-node patch release — several network
|
|
56
|
+
resilience improvements have been made to the download path. File an issue at
|
|
57
|
+
actions/setup-node with runner version, scale-set version, and the relevant
|
|
58
|
+
log section.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: "Add immediate verification to catch silent download failure"
|
|
62
|
+
code: |
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- uses: actions/setup-node@v6
|
|
66
|
+
with:
|
|
67
|
+
node-version-file: .nvmrc
|
|
68
|
+
cache: pnpm
|
|
69
|
+
|
|
70
|
+
# Catch silent exit-0 failures immediately
|
|
71
|
+
- name: Verify Node.js installed
|
|
72
|
+
run: |
|
|
73
|
+
node --version || (echo "::error::setup-node silently failed — Node.js not installed" && exit 1)
|
|
74
|
+
npm --version
|
|
75
|
+
|
|
76
|
+
- run: pnpm install --frozen-lockfile
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Retry wrapper using nick-invision/retry"
|
|
79
|
+
code: |
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
- name: Setup Node.js with retry
|
|
83
|
+
uses: nick-invision/retry@v3
|
|
84
|
+
with:
|
|
85
|
+
timeout_minutes: 5
|
|
86
|
+
max_attempts: 3
|
|
87
|
+
command: |
|
|
88
|
+
# Re-run setup-node inline
|
|
89
|
+
echo "Attempt ${{ github.run_attempt }}"
|
|
90
|
+
- uses: actions/setup-node@v6
|
|
91
|
+
with:
|
|
92
|
+
node-version: 24
|
|
93
|
+
prevention:
|
|
94
|
+
- "Always add a node --version step after setup-node on self-hosted runners to catch silent failures."
|
|
95
|
+
- "Use check-latest: true to ensure the action validates the installed version before proceeding."
|
|
96
|
+
- "Report sporadic failures with full runner version, scale-set version, and log output to actions/setup-node."
|
|
97
|
+
- "Consider using the runner's pre-cached Node.js version (omit node-version) to avoid downloads on self-hosted runners."
|
|
98
|
+
- "Monitor for setup-node step outcome === 'success' followed by downstream node-not-found — this pattern indicates silent download failure."
|
|
99
|
+
docs:
|
|
100
|
+
- url: "https://github.com/actions/setup-node/issues/1555"
|
|
101
|
+
label: "actions/setup-node#1555: Sporadic silent failure when downloading Node.js (12 reactions)"
|
|
102
|
+
- url: "https://github.com/actions/download-artifact/issues/454"
|
|
103
|
+
label: "actions/download-artifact#454: Related silent download failure pattern"
|
|
104
|
+
- url: "https://github.com/actions/toolkit/tree/main/packages/tool-cache"
|
|
105
|
+
label: "actions/toolkit: tool-cache package (download internals)"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
id: silent-failures-029
|
|
2
|
+
title: "setup-python Silently Exits 0 Without Installing Python When versions-manifest.json Is Truncated"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- setup-python
|
|
7
|
+
- versions-manifest
|
|
8
|
+
- silent-exit
|
|
9
|
+
- truncated-response
|
|
10
|
+
- python-not-installed
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "evaluating 0 versions"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "Version .+ was not found in the local cache"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "Node Action run completed with exit code 0"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "evaluating 0 versions"
|
|
20
|
+
- "Version 3.11 - 3.13 was not found in the local cache"
|
|
21
|
+
- "Getting manifest from actions/python-versions@main"
|
|
22
|
+
- "##[debug]Node Action run completed with exit code 0"
|
|
23
|
+
- "##[debug]Finishing: Setup python"
|
|
24
|
+
root_cause: |
|
|
25
|
+
`actions/setup-python` fetches `versions-manifest.json` from the
|
|
26
|
+
`actions/python-versions` repository via two GitHub API calls:
|
|
27
|
+
|
|
28
|
+
1. A git tree request to locate the blob URL.
|
|
29
|
+
2. A raw blob fetch with `Accept: application/vnd.github.VERSION.raw`.
|
|
30
|
+
|
|
31
|
+
Sporadically, the second call returns HTTP 200 with a **truncated body** — the
|
|
32
|
+
JSON is cut off mid-object and unparseable. The action catches the JSON parse
|
|
33
|
+
error silently, treats the result as an empty release list, logs
|
|
34
|
+
"evaluating 0 versions", and exits with code 0 **without installing Python**.
|
|
35
|
+
|
|
36
|
+
The truncated response has two consistent symptoms:
|
|
37
|
+
- A ~30-second server-side delay before any bytes arrive.
|
|
38
|
+
- Only affects authenticated requests (5,000 req/hour tier); anonymous requests
|
|
39
|
+
return the full manifest, but the 60 req/hour limit is too low for real CI.
|
|
40
|
+
|
|
41
|
+
After the step exits 0, subsequent steps that call `python`, `pip`, or `python3`
|
|
42
|
+
either use the system Python (which may differ in version) or fail with
|
|
43
|
+
"python: not found" — neither of which points back to the manifest fetch failure.
|
|
44
|
+
fix: |
|
|
45
|
+
Add an explicit Python version check immediately after setup-python to catch the
|
|
46
|
+
silent exit before it causes confusing downstream errors:
|
|
47
|
+
|
|
48
|
+
- run: python --version
|
|
49
|
+
|
|
50
|
+
For a more robust workaround, set `python-version` to an exact version string
|
|
51
|
+
(e.g., "3.11.9") rather than a range — exact versions can fall back to the
|
|
52
|
+
runner's pre-cached toolcache without requiring a manifest fetch.
|
|
53
|
+
|
|
54
|
+
If you must use a version range, add a shell-level retry loop around the Python
|
|
55
|
+
invocation, or use the `setup-python` `check-latest: false` option with a pinned
|
|
56
|
+
version to reduce manifest fetches.
|
|
57
|
+
|
|
58
|
+
File the issue with captured truncated manifest bodies at actions/setup-python
|
|
59
|
+
to help maintainers add retry-with-backoff logic to the manifest fetch path.
|
|
60
|
+
fix_code:
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: "Catch silent exit immediately after setup-python"
|
|
63
|
+
code: |
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/setup-python@v5
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.11 - 3.13"
|
|
68
|
+
cache: pip
|
|
69
|
+
|
|
70
|
+
# Catch silent manifest-fetch failure before downstream confusion
|
|
71
|
+
- name: Verify Python installed
|
|
72
|
+
run: |
|
|
73
|
+
python --version || (echo "::error::setup-python silently failed — Python not installed" && exit 1)
|
|
74
|
+
pip --version
|
|
75
|
+
|
|
76
|
+
- run: pip install -r requirements.txt
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Use exact version to avoid manifest fetch entirely (uses toolcache)"
|
|
79
|
+
code: |
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/setup-python@v5
|
|
82
|
+
with:
|
|
83
|
+
python-version: "3.11.9" # exact version — uses pre-cached toolcache
|
|
84
|
+
cache: pip # no manifest fetch needed for exact match
|
|
85
|
+
- run: python --version
|
|
86
|
+
- run: pip install -r requirements.txt
|
|
87
|
+
- language: yaml
|
|
88
|
+
label: "Use .python-version file for explicit pinning"
|
|
89
|
+
code: |
|
|
90
|
+
# .python-version file: 3.12.7
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/setup-python@v5
|
|
93
|
+
with:
|
|
94
|
+
python-version-file: .python-version # exact pin, no manifest range fetch
|
|
95
|
+
cache: pip
|
|
96
|
+
- run: python --version
|
|
97
|
+
prevention:
|
|
98
|
+
- "Always add a python --version verification step after setup-python to surface silent failures immediately."
|
|
99
|
+
- "Pin to an exact Python version (e.g., '3.11.9') rather than a range to use the pre-cached toolcache and avoid manifest fetches."
|
|
100
|
+
- "Use a .python-version file for reproducible, exact pinning that also works with pyenv locally."
|
|
101
|
+
- "Monitor for the symptom pattern: setup-python exits 0 + 'evaluating 0 versions' + downstream python-not-found."
|
|
102
|
+
- "If using version ranges, run setup-python with cache: false first in a debug context to isolate manifest vs. cache failures."
|
|
103
|
+
docs:
|
|
104
|
+
- url: "https://github.com/actions/setup-python/issues/1318"
|
|
105
|
+
label: "actions/setup-python#1318: setup-python silently exits 0 on truncated versions-manifest.json"
|
|
106
|
+
- url: "https://github.com/actions/python-versions"
|
|
107
|
+
label: "actions/python-versions: Source of versions-manifest.json"
|
|
108
|
+
- url: "https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#specifying-a-python-version"
|
|
109
|
+
label: "GitHub Docs: Specifying a Python version"
|
|
110
|
+
- url: "https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md"
|
|
111
|
+
label: "setup-python: Advanced Usage"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: silent-failures-032
|
|
2
|
+
title: "Undefined env/context Variable Silently Evaluates to Empty String"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- env-context
|
|
7
|
+
- expression
|
|
8
|
+
- undefined-variable
|
|
9
|
+
- empty-string
|
|
10
|
+
- silent-failure
|
|
11
|
+
- jenkins-migration
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: '\$\{\{\s*env\.[A-Za-z_][A-Za-z0-9_]*\s*\}\}'
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "expression.*evaluates.*empty.*string|undefined.*variable.*no.*error"
|
|
16
|
+
flags: "i"
|
|
17
|
+
error_messages:
|
|
18
|
+
- "(no error shown -- undefined env/context variables produce empty strings silently)"
|
|
19
|
+
- "(step proceeds with empty value, downstream steps receive blank input)"
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions expression evaluation does not throw errors when referencing
|
|
22
|
+
variables that do not exist. An expression like ${{ env.SOME_VAR }} where
|
|
23
|
+
SOME_VAR is not defined in any env: block evaluates silently to an empty string.
|
|
24
|
+
|
|
25
|
+
This behavior affects all expression contexts:
|
|
26
|
+
- ${{ env.VAR_NAME }} -- undefined env variable evaluates to empty string
|
|
27
|
+
- ${{ inputs.param }} -- undefined workflow input evaluates to empty string
|
|
28
|
+
- ${{ vars.CONFIG }} -- undefined repository variable evaluates to empty string
|
|
29
|
+
- ${{ needs.job.outputs.value }} -- missing job output evaluates to empty string
|
|
30
|
+
|
|
31
|
+
Common causes:
|
|
32
|
+
1. Jenkins migration: GitHub Actions Importer generates env references for all
|
|
33
|
+
Jenkins variables, but not all are defined in the converted workflow env block.
|
|
34
|
+
2. Variable renaming: renamed in one place but old reference still used elsewhere.
|
|
35
|
+
3. Conditional env blocks: variable only set for certain trigger events.
|
|
36
|
+
4. Copy-paste across repos where the env block differs.
|
|
37
|
+
|
|
38
|
+
Silent empty values cause cascading failures:
|
|
39
|
+
- Docker image tags resolve to empty, pushing to wrong registry path silently
|
|
40
|
+
- Build version strings empty, artifacts built without version identifier
|
|
41
|
+
- Path variables empty, steps act on current directory unexpectedly
|
|
42
|
+
- Conditional checks always false because gating variable is always empty
|
|
43
|
+
- Upload artifact path empty, upload succeeds with zero files
|
|
44
|
+
fix: |
|
|
45
|
+
Add explicit variable validation using shell parameter expansion to fail fast
|
|
46
|
+
with a clear error message when required variables are empty or unset.
|
|
47
|
+
|
|
48
|
+
The POSIX ${VAR:?message} syntax causes the step to exit non-zero with a clear
|
|
49
|
+
error immediately when VAR is empty or unset.
|
|
50
|
+
|
|
51
|
+
For non-shell contexts, use the GitHub Actions || operator to provide fallback
|
|
52
|
+
values or error markers.
|
|
53
|
+
fix_code:
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: "Shell validation with POSIX parameter expansion -- fail fast on empty"
|
|
56
|
+
code: |
|
|
57
|
+
jobs:
|
|
58
|
+
deploy:
|
|
59
|
+
env:
|
|
60
|
+
DEPLOY_TARGET: ${{ env.DEPLOY_TARGET }}
|
|
61
|
+
IMAGE_TAG: ${{ env.IMAGE_TAG }}
|
|
62
|
+
steps:
|
|
63
|
+
- name: Validate required environment variables
|
|
64
|
+
shell: bash
|
|
65
|
+
run: |
|
|
66
|
+
: "${DEPLOY_TARGET:?DEPLOY_TARGET is required but not set}"
|
|
67
|
+
: "${IMAGE_TAG:?IMAGE_TAG is required -- check the env: block}"
|
|
68
|
+
echo "All required variables validated"
|
|
69
|
+
- name: Deploy
|
|
70
|
+
run: ./deploy.sh "$DEPLOY_TARGET" "$IMAGE_TAG"
|
|
71
|
+
- language: yaml
|
|
72
|
+
label: "Expression fallback to make undefined variables visible in logs"
|
|
73
|
+
code: |
|
|
74
|
+
steps:
|
|
75
|
+
- name: Resolve configuration
|
|
76
|
+
env:
|
|
77
|
+
DEPLOY_TARGET: ${{ env.DEPLOY_TARGET || 'MISSING_DEPLOY_TARGET' }}
|
|
78
|
+
run: |
|
|
79
|
+
if [[ "$DEPLOY_TARGET" == "MISSING_DEPLOY_TARGET" ]]; then
|
|
80
|
+
echo "::error::DEPLOY_TARGET is not set -- add it to the env: block"
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
echo "Deploying to: $DEPLOY_TARGET"
|
|
84
|
+
- language: yaml
|
|
85
|
+
label: "Bulk audit for Jenkins-migrated workflows"
|
|
86
|
+
code: |
|
|
87
|
+
steps:
|
|
88
|
+
- name: Audit required environment variables
|
|
89
|
+
shell: bash
|
|
90
|
+
run: |
|
|
91
|
+
REQUIRED=(APP_VERSION DEPLOY_ENV BUILD_TARGET REGISTRY_URL)
|
|
92
|
+
MISSING=()
|
|
93
|
+
for VAR in "${REQUIRED[@]}"; do
|
|
94
|
+
[[ -z "${!VAR}" ]] && MISSING+=("$VAR")
|
|
95
|
+
done
|
|
96
|
+
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
|
97
|
+
echo "::error::Missing env vars: ${MISSING[*]} -- add them to env: block"
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
echo "All required variables are set"
|
|
101
|
+
prevention:
|
|
102
|
+
- "After Jenkins migration, audit all env expression references against the workflow env: block."
|
|
103
|
+
- "Add a validation step at job start using shell parameter expansion for all required variables."
|
|
104
|
+
- "Use repository variables (vars.X) instead of env for shared config -- gaps are visible in UI."
|
|
105
|
+
- "Use the || operator to make undefined variables visible: ${{ env.X || 'MISSING' }}."
|
|
106
|
+
- "Set required: true on workflow_dispatch inputs to prevent the undefined-input variant."
|
|
107
|
+
docs:
|
|
108
|
+
- url: "https://stackoverflow.com/questions/76611427/how-to-verify-if-the-github-syntax-variables-actually-exist-in-github-actions-wo"
|
|
109
|
+
label: "Stack Overflow: Verify GitHub syntax variables exist in workflow YAML"
|
|
110
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions"
|
|
111
|
+
label: "GitHub Docs: Evaluate expressions -- operators including || for defaults"
|
|
112
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables"
|
|
113
|
+
label: "GitHub Docs: Store information in variables -- env, vars, inputs contexts"
|
|
114
|
+
- url: "https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_06_02"
|
|
115
|
+
label: "POSIX Shell: Parameter expansion with :? for unset/empty detection"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
id: silent-failures-034
|
|
2
|
+
title: "Windows PowerShell GITHUB_OUTPUT and GITHUB_ENV Require $env: Prefix, Not Bash-Style $VARIABLE Syntax"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- windows
|
|
7
|
+
- powershell
|
|
8
|
+
- GITHUB_OUTPUT
|
|
9
|
+
- GITHUB_ENV
|
|
10
|
+
- environment-variables
|
|
11
|
+
- shell-syntax
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "echo\\s+[\"']?[A-Z_]+=.*>>\\s+\\$GITHUB_OUTPUT"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "echo\\s+[\"']?[A-Z_]+=.*>>\\s+\\$GITHUB_ENV"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "shell:\\s*(pwsh|powershell)"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "echo \"KEY=value\" >> $GITHUB_OUTPUT"
|
|
21
|
+
- "echo \"KEY=value\" >> $GITHUB_ENV"
|
|
22
|
+
- "Val1: "
|
|
23
|
+
- "Val2: "
|
|
24
|
+
root_cause: |
|
|
25
|
+
When a workflow step uses shell: pwsh (PowerShell Core, the default on Windows
|
|
26
|
+
runners) or shell: powershell (legacy Windows PowerShell), the bash-style
|
|
27
|
+
variable syntax $GITHUB_OUTPUT and $GITHUB_ENV does NOT work.
|
|
28
|
+
|
|
29
|
+
In PowerShell, $GITHUB_OUTPUT is treated as a PowerShell variable (undefined
|
|
30
|
+
and therefore null). The >> append operator writes nothing to a null path.
|
|
31
|
+
The step exits 0 (success) with no error or warning. All downstream jobs
|
|
32
|
+
that reference step outputs or environment variables receive empty strings.
|
|
33
|
+
|
|
34
|
+
The correct PowerShell syntax uses $env:GITHUB_OUTPUT to access the OS
|
|
35
|
+
environment variable, not $GITHUB_OUTPUT (a PS variable).
|
|
36
|
+
|
|
37
|
+
Shell-specific syntax comparison:
|
|
38
|
+
- bash / sh: echo "KEY=val" >> $GITHUB_OUTPUT
|
|
39
|
+
- pwsh (PS 7+): "KEY=val" >> $env:GITHUB_OUTPUT
|
|
40
|
+
- powershell: "KEY=val" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
|
41
|
+
- cmd: echo KEY=val >> %GITHUB_OUTPUT%
|
|
42
|
+
|
|
43
|
+
This is a common mistake when:
|
|
44
|
+
- Migrating workflows from Linux to Windows runners
|
|
45
|
+
- Copying bash examples from documentation into PowerShell steps
|
|
46
|
+
- Writing cross-platform workflows without specifying shell: per step
|
|
47
|
+
fix: |
|
|
48
|
+
Replace $GITHUB_OUTPUT with $env:GITHUB_OUTPUT and $GITHUB_ENV with
|
|
49
|
+
$env:GITHUB_ENV in all PowerShell steps.
|
|
50
|
+
|
|
51
|
+
Alternatively, add shell: bash to every run: step that uses bash-style
|
|
52
|
+
syntax — Git Bash is available on all Windows GitHub-hosted runners and
|
|
53
|
+
supports the standard $GITHUB_OUTPUT syntax.
|
|
54
|
+
|
|
55
|
+
For old Windows PowerShell (shell: powershell), use Out-File with
|
|
56
|
+
explicit UTF-8 encoding to avoid BOM issues.
|
|
57
|
+
fix_code:
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: "Broken — bash syntax in PowerShell step silently produces no output"
|
|
60
|
+
code: |
|
|
61
|
+
- name: Set output (broken on PowerShell)
|
|
62
|
+
shell: pwsh
|
|
63
|
+
run: |
|
|
64
|
+
# WRONG: $GITHUB_OUTPUT is a null PS variable — no output is set
|
|
65
|
+
echo "BUILD_VERSION=1.2.3" >> $GITHUB_OUTPUT
|
|
66
|
+
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "Fixed — correct $env: prefix for PowerShell Core (pwsh)"
|
|
69
|
+
code: |
|
|
70
|
+
- name: Set output (PowerShell Core)
|
|
71
|
+
id: version
|
|
72
|
+
shell: pwsh
|
|
73
|
+
run: |
|
|
74
|
+
# CORRECT: $env:GITHUB_OUTPUT accesses the OS environment variable
|
|
75
|
+
"BUILD_VERSION=1.2.3" >> $env:GITHUB_OUTPUT
|
|
76
|
+
|
|
77
|
+
- name: Use output
|
|
78
|
+
run: echo "Version is ${{ steps.version.outputs.BUILD_VERSION }}"
|
|
79
|
+
shell: pwsh
|
|
80
|
+
|
|
81
|
+
- language: yaml
|
|
82
|
+
label: "Old Windows PowerShell (shell: powershell) — explicit UTF-8 required"
|
|
83
|
+
code: |
|
|
84
|
+
- name: Set output (legacy Windows PowerShell)
|
|
85
|
+
shell: powershell
|
|
86
|
+
run: |
|
|
87
|
+
"BUILD_VERSION=1.2.3" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
|
|
88
|
+
|
|
89
|
+
- language: yaml
|
|
90
|
+
label: "Cross-platform — use shell: bash to keep bash-style syntax on Windows"
|
|
91
|
+
code: |
|
|
92
|
+
- name: Set output (bash on Windows runner)
|
|
93
|
+
shell: bash
|
|
94
|
+
run: |
|
|
95
|
+
# shell: bash uses Git Bash on Windows — $GITHUB_OUTPUT works fine
|
|
96
|
+
echo "BUILD_VERSION=1.2.3" >> $GITHUB_OUTPUT
|
|
97
|
+
|
|
98
|
+
- language: yaml
|
|
99
|
+
label: "CMD shell syntax"
|
|
100
|
+
code: |
|
|
101
|
+
- name: Set output (CMD)
|
|
102
|
+
shell: cmd
|
|
103
|
+
run: echo BUILD_VERSION=1.2.3 >> %GITHUB_OUTPUT%
|
|
104
|
+
prevention:
|
|
105
|
+
- "Use shell: bash for all run: steps that use bash-style $GITHUB_OUTPUT or $GITHUB_ENV syntax, even on Windows runners."
|
|
106
|
+
- "In team workflow templates, document the shell-specific syntax for GITHUB_OUTPUT and GITHUB_ENV."
|
|
107
|
+
- "Add actionlint to CI — it can detect shell/syntax mismatches in workflow files."
|
|
108
|
+
- "Add an explicit post-step validation that reads back expected outputs to confirm they were written correctly."
|
|
109
|
+
- "Prefer shell: bash globally (via defaults: run: shell: bash) when your workflows target cross-platform and use bash syntax."
|
|
110
|
+
docs:
|
|
111
|
+
- url: "https://stackoverflow.com/questions/74443940/value-not-set-using-github-output"
|
|
112
|
+
label: "Stack Overflow: Value not set using $GITHUB_OUTPUT on Windows PowerShell (Score: 22, 17K views)"
|
|
113
|
+
- url: "https://stackoverflow.com/questions/66733076/github-actions-set-environment-variable-for-windows-build-with-powershell"
|
|
114
|
+
label: "Stack Overflow: Set environment variable for Windows build with PowerShell (Score: 16, 11K views)"
|
|
115
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter"
|
|
116
|
+
label: "GitHub Docs: Setting an output parameter (includes PowerShell examples)"
|
|
117
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#defaultsrun"
|
|
118
|
+
label: "GitHub Docs: defaults.run.shell — set default shell for all run steps"
|