@htekdev/actions-debugger 1.0.93 → 1.0.95
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-052.yml +107 -0
- package/errors/known-unsolved/known-unsolved-053.yml +114 -0
- package/errors/permissions-auth/permissions-auth-053.yml +103 -0
- package/errors/runner-environment/macos-xcrun-simctl-no-developer-tools.yml +84 -0
- package/errors/runner-environment/node-22-openssl-legacy-provider-removed.yml +96 -0
- package/errors/runner-environment/setup-java-temurin-java8-arm64-not-available.yml +93 -0
- package/errors/runner-environment/ubuntu-tzdata-apt-hang-noninteractive-required.yml +84 -0
- package/errors/silent-failures/silent-failures-087.yml +120 -0
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
id: caching-artifacts-052
|
|
2
|
+
title: '`download-artifact@v4` `name:` Does Not Support Glob Patterns — Use `pattern:` Instead'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- download-artifact
|
|
7
|
+
- v4-breaking-change
|
|
8
|
+
- glob
|
|
9
|
+
- pattern
|
|
10
|
+
- migration
|
|
11
|
+
- artifact-download
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'download-artifact@v4|download-artifact@v3.*name.*\*'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'name\s*:\s*[''"][^''"]*\*[^''"]*[''"]'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "No artifacts found with the provided name"
|
|
19
|
+
- "Error: Unable to find any artifacts for the associated workflow"
|
|
20
|
+
- "Unable to find artifact 'my-*-artifact'"
|
|
21
|
+
root_cause: |
|
|
22
|
+
In `actions/download-artifact@v3`, the `name:` input accepted glob patterns
|
|
23
|
+
(e.g., `name: 'build-*'`) and would download all matching artifacts.
|
|
24
|
+
In `actions/download-artifact@v4` this behavior changed: `name:` now requires
|
|
25
|
+
an EXACT artifact name match. Glob characters in `name:` are treated as
|
|
26
|
+
literal characters, so `name: 'build-*'` looks for an artifact literally
|
|
27
|
+
named `build-*`, finds none, and either errors or silently downloads nothing
|
|
28
|
+
depending on the `error-no-files-found` setting.
|
|
29
|
+
|
|
30
|
+
The `pattern:` input was introduced in v4 as the replacement for glob-based
|
|
31
|
+
artifact selection. Migrating from v3 to v4 requires moving glob expressions
|
|
32
|
+
from `name:` to `pattern:`.
|
|
33
|
+
|
|
34
|
+
This silently impacts workflows that:
|
|
35
|
+
- Download multiple build artifacts from matrix jobs using a shared prefix
|
|
36
|
+
- Use wildcards to grab all artifacts from a set of parallel jobs
|
|
37
|
+
- Were migrated to v4 without reading the full migration guide
|
|
38
|
+
fix: |
|
|
39
|
+
Replace glob patterns in `name:` with the `pattern:` input in
|
|
40
|
+
`actions/download-artifact@v4`. The `name:` input should only be used
|
|
41
|
+
when downloading a single artifact by its exact name.
|
|
42
|
+
|
|
43
|
+
When using `pattern:`, the action also has a `merge-multiple` option
|
|
44
|
+
that controls whether matched artifacts are merged into one directory
|
|
45
|
+
or placed in separate subdirectories.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "WRONG — glob in name: silently matches nothing in v4"
|
|
49
|
+
code: |
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/download-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: 'build-*' # ❌ globs not supported in name: for v4
|
|
54
|
+
path: ./artifacts
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: "RIGHT — use pattern: for glob matching in v4"
|
|
57
|
+
code: |
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/download-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
pattern: 'build-*' # ✅ use pattern: for glob matching
|
|
62
|
+
path: ./artifacts
|
|
63
|
+
merge-multiple: true # flatten into single directory
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: "RIGHT — download exact artifact by name in v4"
|
|
66
|
+
code: |
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/download-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: build-linux-amd64 # ✅ exact name, no glob needed
|
|
71
|
+
path: ./dist/linux
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "Matrix upload + glob download pattern"
|
|
74
|
+
code: |
|
|
75
|
+
jobs:
|
|
76
|
+
build:
|
|
77
|
+
strategy:
|
|
78
|
+
matrix:
|
|
79
|
+
os: [linux, windows, macos]
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/upload-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
name: build-${{ matrix.os }} # distinct name per job
|
|
85
|
+
path: ./dist/
|
|
86
|
+
|
|
87
|
+
package:
|
|
88
|
+
needs: build
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/download-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
pattern: 'build-*' # ✅ downloads all build-* artifacts
|
|
94
|
+
path: ./all-builds
|
|
95
|
+
merge-multiple: false # keep per-artifact subdirectories
|
|
96
|
+
prevention:
|
|
97
|
+
- "When migrating from download-artifact@v3 to @v4, audit all `name:` inputs for glob characters and move them to `pattern:`."
|
|
98
|
+
- "Use `name:` only for exact single-artifact downloads; use `pattern:` for any glob or multi-artifact download."
|
|
99
|
+
- "Set `error-no-files-found: error` to fail fast when no artifacts match, exposing glob issues early."
|
|
100
|
+
- "Review the download-artifact v4 migration guide before updating the action version."
|
|
101
|
+
docs:
|
|
102
|
+
- url: "https://github.com/actions/download-artifact/blob/main/docs/MIGRATION.md"
|
|
103
|
+
label: "download-artifact v4 migration guide"
|
|
104
|
+
- url: "https://github.com/actions/download-artifact#inputs"
|
|
105
|
+
label: "download-artifact — inputs reference (pattern vs name)"
|
|
106
|
+
- url: "https://github.com/actions/toolkit/releases/tag/%40actions%2Fartifact%402.0.0"
|
|
107
|
+
label: "actions/toolkit v2 — artifact v2 API underlying v4 action"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
id: known-unsolved-053
|
|
2
|
+
title: '`workflow_dispatch` Has No Native Array or List Input Type — Must Serialize as JSON String'
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_dispatch
|
|
7
|
+
- inputs
|
|
8
|
+
- array
|
|
9
|
+
- list
|
|
10
|
+
- json
|
|
11
|
+
- limitation
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'workflow_dispatch.*inputs|inputs.*type\s*:\s*string.*array'
|
|
14
|
+
flags: 'im'
|
|
15
|
+
- regex: "fromJSON\\(.*inputs\\."
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Input type 'array' is not supported for workflow_dispatch"
|
|
19
|
+
- "Unexpected value 'array'"
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions `workflow_dispatch` supports only five input types:
|
|
22
|
+
`string`, `boolean`, `choice`, `number`, and `environment`. There is no
|
|
23
|
+
native `array` or `list` type. Users who need to pass multiple values
|
|
24
|
+
(e.g., a list of services to deploy, a set of environments to target)
|
|
25
|
+
must serialize the array as a JSON string and parse it in the workflow.
|
|
26
|
+
|
|
27
|
+
This limitation affects:
|
|
28
|
+
- Manual dispatch with dynamic target lists
|
|
29
|
+
- CI/CD automation via the REST API (`POST /repos/.../actions/workflows/.../dispatches`)
|
|
30
|
+
- Reuse of workflow_dispatch workflows that naturally accept variable-length input
|
|
31
|
+
- Validation — no schema can prevent malformed JSON from being passed
|
|
32
|
+
|
|
33
|
+
A `type: array` schema was proposed in actions/runner#1844 (800+ reactions)
|
|
34
|
+
and remains one of the highest-voted open feature requests for GitHub Actions.
|
|
35
|
+
fix: |
|
|
36
|
+
Serialize the array as a JSON string in the dispatch call and use
|
|
37
|
+
`fromJSON()` in the workflow to parse it into a matrix or loop variable.
|
|
38
|
+
|
|
39
|
+
Workarounds by use case:
|
|
40
|
+
1. **API dispatch**: Serialize to `'["a","b","c"]'` and use `fromJSON(inputs.targets)`
|
|
41
|
+
2. **UI dispatch**: Document the expected JSON format in the input description
|
|
42
|
+
3. **Choice type**: If the list is finite and known ahead of time, use
|
|
43
|
+
`type: choice` with predefined options instead
|
|
44
|
+
4. **Multiple boolean inputs**: For small fixed sets, use separate boolean
|
|
45
|
+
inputs per option (verbose but typed)
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Workaround — serialize array as JSON string, parse with fromJSON"
|
|
49
|
+
code: |
|
|
50
|
+
on:
|
|
51
|
+
workflow_dispatch:
|
|
52
|
+
inputs:
|
|
53
|
+
services:
|
|
54
|
+
description: 'JSON array of services to deploy e.g. ["api","worker","ui"]'
|
|
55
|
+
required: true
|
|
56
|
+
type: string
|
|
57
|
+
default: '["api","worker"]'
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
deploy:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
strategy:
|
|
63
|
+
matrix:
|
|
64
|
+
service: ${{ fromJSON(inputs.services) }}
|
|
65
|
+
steps:
|
|
66
|
+
- run: echo "Deploying ${{ matrix.service }}"
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "Workaround — comma-separated string split in shell"
|
|
69
|
+
code: |
|
|
70
|
+
on:
|
|
71
|
+
workflow_dispatch:
|
|
72
|
+
inputs:
|
|
73
|
+
environments:
|
|
74
|
+
description: 'Comma-separated environments e.g. staging,production'
|
|
75
|
+
required: true
|
|
76
|
+
type: string
|
|
77
|
+
default: 'staging'
|
|
78
|
+
|
|
79
|
+
jobs:
|
|
80
|
+
deploy:
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
steps:
|
|
83
|
+
- name: Deploy to each environment
|
|
84
|
+
run: |
|
|
85
|
+
IFS=',' read -ra ENVS <<< "${{ inputs.environments }}"
|
|
86
|
+
for env in "${ENVS[@]}"; do
|
|
87
|
+
echo "Deploying to: $env"
|
|
88
|
+
done
|
|
89
|
+
- language: yaml
|
|
90
|
+
label: "Alternative — use type: choice for known finite sets"
|
|
91
|
+
code: |
|
|
92
|
+
on:
|
|
93
|
+
workflow_dispatch:
|
|
94
|
+
inputs:
|
|
95
|
+
target_env:
|
|
96
|
+
description: 'Target environment'
|
|
97
|
+
required: true
|
|
98
|
+
type: choice
|
|
99
|
+
options:
|
|
100
|
+
- staging
|
|
101
|
+
- production
|
|
102
|
+
- both
|
|
103
|
+
prevention:
|
|
104
|
+
- "Document the expected JSON format in the input `description` field so UI dispatchers know the required syntax."
|
|
105
|
+
- "Add a validation step that runs `echo '${{ inputs.targets }}' | jq .` to fail fast on malformed JSON before processing."
|
|
106
|
+
- "Consider using `type: choice` with predefined options if the list is small and known at workflow-definition time."
|
|
107
|
+
- "For API-driven workflows, generate the JSON array programmatically and pass it as a string in the dispatch payload."
|
|
108
|
+
docs:
|
|
109
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_dispatchinputsinput_idtype"
|
|
110
|
+
label: "workflow_dispatch input types — supported values"
|
|
111
|
+
- url: "https://github.com/actions/runner/issues/1844"
|
|
112
|
+
label: "actions/runner #1844 — Support array inputs for workflow_dispatch (800+ reactions)"
|
|
113
|
+
- url: "https://docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event"
|
|
114
|
+
label: "REST API — Create a workflow dispatch event"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
id: permissions-auth-053
|
|
2
|
+
title: 'GITHUB_TOKEN `packages: write` Cannot Delete Package Versions — Requires PAT with `delete:packages`'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- packages
|
|
7
|
+
- github-token
|
|
8
|
+
- delete-packages
|
|
9
|
+
- container-registry
|
|
10
|
+
- package-cleanup
|
|
11
|
+
- permissions
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Resource not accessible by integration'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'DELETE.*packages.*versions|packages.*DELETE.*versions'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: '403.*package|package.*403'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Resource not accessible by integration"
|
|
21
|
+
- "403 Forbidden — DELETE https://api.github.com/user/packages/{type}/{name}/versions/{id}"
|
|
22
|
+
- "Error: Resource not accessible by integration (HTTP 403)"
|
|
23
|
+
root_cause: |
|
|
24
|
+
The `packages: write` permission in `GITHUB_TOKEN` grants the ability to
|
|
25
|
+
publish and overwrite package versions but does NOT include the ability to
|
|
26
|
+
delete them. Package version deletion requires account-level permission
|
|
27
|
+
(`delete:packages` OAuth scope) that cannot be granted to an installation
|
|
28
|
+
token. The GitHub Packages REST API delete endpoint
|
|
29
|
+
(`DELETE /user/packages/{type}/{name}/versions/{id}` or the org equivalent)
|
|
30
|
+
validates the caller's account-level scope, which GITHUB_TOKEN—being a
|
|
31
|
+
repository-scoped installation token—can never satisfy regardless of the
|
|
32
|
+
`permissions: packages: write` declaration in the workflow.
|
|
33
|
+
|
|
34
|
+
This commonly surfaces in:
|
|
35
|
+
- Container image cleanup jobs that try to prune old tags/digests
|
|
36
|
+
- npm, Maven, or RubyGems package version retention workflows
|
|
37
|
+
- Automated housekeeping that uses `ghcr.io` image management actions
|
|
38
|
+
fix: |
|
|
39
|
+
Store a fine-grained or classic PAT with `delete:packages` scope as a
|
|
40
|
+
repository secret (e.g., `PACKAGES_DELETE_TOKEN`) and use that token when
|
|
41
|
+
calling the Packages delete API or actions that delete package versions.
|
|
42
|
+
|
|
43
|
+
For GitHub Container Registry (ghcr.io) image pruning, use a PAT or a
|
|
44
|
+
GitHub App installation token with Packages write + admin scope. Pass it
|
|
45
|
+
as the `token:` input to the relevant cleanup action.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "WRONG — GITHUB_TOKEN cannot delete package versions (403)"
|
|
49
|
+
code: |
|
|
50
|
+
jobs:
|
|
51
|
+
cleanup:
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
permissions:
|
|
54
|
+
packages: write # ❌ write permission does not grant delete
|
|
55
|
+
steps:
|
|
56
|
+
- name: Delete old package version
|
|
57
|
+
run: |
|
|
58
|
+
# This call will return 403 "Resource not accessible by integration"
|
|
59
|
+
VERSION_ID=12345
|
|
60
|
+
curl -X DELETE \
|
|
61
|
+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
|
|
62
|
+
https://api.github.com/user/packages/container/my-image/versions/$VERSION_ID
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "RIGHT — use a PAT with delete:packages scope"
|
|
65
|
+
code: |
|
|
66
|
+
jobs:
|
|
67
|
+
cleanup:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
steps:
|
|
70
|
+
- name: Delete old package version
|
|
71
|
+
env:
|
|
72
|
+
GH_TOKEN: ${{ secrets.PACKAGES_DELETE_TOKEN }} # PAT with delete:packages
|
|
73
|
+
run: |
|
|
74
|
+
VERSION_ID=12345
|
|
75
|
+
curl -X DELETE \
|
|
76
|
+
-H "Authorization: Bearer $GH_TOKEN" \
|
|
77
|
+
https://api.github.com/user/packages/container/my-image/versions/$VERSION_ID
|
|
78
|
+
- language: yaml
|
|
79
|
+
label: "RIGHT — ghcr.io cleanup action with PAT"
|
|
80
|
+
code: |
|
|
81
|
+
jobs:
|
|
82
|
+
cleanup-images:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
steps:
|
|
85
|
+
- uses: snok/container-retention-policy@v3
|
|
86
|
+
with:
|
|
87
|
+
account: user
|
|
88
|
+
token: ${{ secrets.PACKAGES_DELETE_TOKEN }} # PAT with delete:packages
|
|
89
|
+
image-names: my-image
|
|
90
|
+
cut-off: 7 days ago UTC
|
|
91
|
+
keep-n-most-recent: 5
|
|
92
|
+
prevention:
|
|
93
|
+
- "Never assume `packages: write` is sufficient for full package lifecycle management — deletion is always PAT-only."
|
|
94
|
+
- "Create a dedicated PAT or GitHub App with `delete:packages` scope and store it as a secret for cleanup workflows."
|
|
95
|
+
- "Scope cleanup jobs separately from build jobs so the PAT is only used where deletion is needed."
|
|
96
|
+
- "Document in your workflow that PACKAGES_DELETE_TOKEN requires `delete:packages` scope so it is renewed with the right scopes."
|
|
97
|
+
docs:
|
|
98
|
+
- url: "https://docs.github.com/en/rest/packages/packages#delete-a-package-version-for-a-user"
|
|
99
|
+
label: "REST API — Delete a package version for a user"
|
|
100
|
+
- url: "https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages#about-scopes-and-permissions-for-package-registries"
|
|
101
|
+
label: "About permissions for GitHub Packages"
|
|
102
|
+
- url: "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token"
|
|
103
|
+
label: "Creating a fine-grained PAT with delete:packages"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
id: runner-environment-161
|
|
2
|
+
title: 'macOS ARM64 runners: xcrun simctl fails — Xcode developer tools not selected'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- macos
|
|
7
|
+
- xcrun
|
|
8
|
+
- simctl
|
|
9
|
+
- xcode
|
|
10
|
+
- ios
|
|
11
|
+
- arm64
|
|
12
|
+
- xcode-select
|
|
13
|
+
- macos-14
|
|
14
|
+
- macos-15
|
|
15
|
+
patterns:
|
|
16
|
+
- regex: 'xcrun: error: unable to find utility "simctl"'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'xcode-select.*tool.*requires Xcode'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'active developer directory.*command line tools instance'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
- regex: 'xcode-select: note: No developer tools were found'
|
|
23
|
+
flags: 'i'
|
|
24
|
+
error_messages:
|
|
25
|
+
- 'xcrun: error: unable to find utility "simctl", not a developer tool or in PATH'
|
|
26
|
+
- 'xcode-select: error: tool ''xcodebuild'' requires Xcode, but active developer directory ''/Library/Developer/CommandLineTools'' is a command line tools instance'
|
|
27
|
+
- 'xcode-select: note: No developer tools were found, requesting install'
|
|
28
|
+
root_cause: |
|
|
29
|
+
macOS 14 (macos-14) and macOS 15 (macos-15) GitHub-hosted runners include
|
|
30
|
+
multiple Xcode versions and also have Xcode Command Line Tools installed. When
|
|
31
|
+
the active developer directory is set to the Command Line Tools path
|
|
32
|
+
(/Library/Developer/CommandLineTools) rather than a full Xcode.app installation,
|
|
33
|
+
tools that require full Xcode — xcrun simctl, xcodebuild, instruments, codesign,
|
|
34
|
+
and others — fail with "unable to find utility" or "requires Xcode" errors.
|
|
35
|
+
|
|
36
|
+
The runners have a default Xcode selected at image creation time, but this can
|
|
37
|
+
be changed or reset by steps earlier in the workflow (e.g., a Homebrew install
|
|
38
|
+
that runs xcode-select internally). iOS simulator builds on the ARM64 macos-14
|
|
39
|
+
and macos-15 runners are the most common trigger because simctl is exclusively
|
|
40
|
+
a full Xcode tool with no Command Line Tools counterpart.
|
|
41
|
+
fix: |
|
|
42
|
+
Explicitly select a full Xcode version using xcode-select or DEVELOPER_DIR at
|
|
43
|
+
the start of the workflow. List available Xcode versions with
|
|
44
|
+
ls /Applications/Xcode*.app to find what is pre-installed on the runner.
|
|
45
|
+
fix_code:
|
|
46
|
+
- language: yaml
|
|
47
|
+
label: 'Explicitly select Xcode at workflow start'
|
|
48
|
+
code: |
|
|
49
|
+
- name: Select Xcode version
|
|
50
|
+
run: |
|
|
51
|
+
sudo xcode-select -s /Applications/Xcode_16.2.app
|
|
52
|
+
xcode-select --print-path
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: 'Use DEVELOPER_DIR env var for all Xcode steps'
|
|
55
|
+
code: |
|
|
56
|
+
jobs:
|
|
57
|
+
ios-build:
|
|
58
|
+
runs-on: macos-15
|
|
59
|
+
env:
|
|
60
|
+
DEVELOPER_DIR: /Applications/Xcode_16.2.app/Contents/Developer
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- name: Build and test
|
|
64
|
+
run: |
|
|
65
|
+
xcrun simctl list devicetypes | head -5
|
|
66
|
+
xcodebuild -scheme MyApp -sdk iphonesimulator build
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'List available Xcode versions on the runner'
|
|
69
|
+
code: |
|
|
70
|
+
- name: List Xcode versions
|
|
71
|
+
run: ls /Applications/Xcode*.app
|
|
72
|
+
# Shows all pre-installed Xcode versions to pick the correct path
|
|
73
|
+
prevention:
|
|
74
|
+
- 'Always run xcode-select -p at the start of iOS/macOS workflows to verify the active developer directory'
|
|
75
|
+
- 'Pin the Xcode version explicitly using xcode-select or DEVELOPER_DIR instead of relying on runner defaults'
|
|
76
|
+
- 'Check the runner image README at github.com/actions/runner-images for pre-installed Xcode versions per image'
|
|
77
|
+
- 'Use the setup-xcode community action for simplified Xcode version selection'
|
|
78
|
+
docs:
|
|
79
|
+
- url: 'https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md'
|
|
80
|
+
label: 'macOS 15 runner image README — pre-installed Xcode versions'
|
|
81
|
+
- url: 'https://developer.apple.com/documentation/xcode/selecting-the-xcode-that-tools-will-use'
|
|
82
|
+
label: 'Apple Developer: Selecting the Xcode that tools will use'
|
|
83
|
+
- url: 'https://github.com/actions/runner-images/issues'
|
|
84
|
+
label: 'actions/runner-images GitHub Issues — macOS Xcode selection reports'
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: runner-environment-162
|
|
2
|
+
title: 'Node.js 22 removed --openssl-legacy-provider — webpack 4 and Create React App builds fail'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- nodejs
|
|
7
|
+
- node-22
|
|
8
|
+
- openssl
|
|
9
|
+
- webpack
|
|
10
|
+
- create-react-app
|
|
11
|
+
- lts
|
|
12
|
+
- setup-node
|
|
13
|
+
- openssl-legacy-provider
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'ERR_OSSL_UNSUPPORTED'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: '--openssl-legacy-provider is not allowed in NODE_OPTIONS'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'error:0308010C:digital envelope routines::unsupported'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
- regex: 'opensslErrorStack.*digital envelope routines.*initialization error'
|
|
22
|
+
flags: 'i'
|
|
23
|
+
error_messages:
|
|
24
|
+
- 'Error: error:0308010C:digital envelope routines::unsupported'
|
|
25
|
+
- 'node: --openssl-legacy-provider is not allowed in NODE_OPTIONS'
|
|
26
|
+
- 'opensslErrorStack: [ ''error:03000086:digital envelope routines::initialization error'' ]'
|
|
27
|
+
root_cause: |
|
|
28
|
+
Node.js 22 (LTS since October 2024) completed the removal of the
|
|
29
|
+
--openssl-legacy-provider CLI flag that was first deprecated in Node.js 18.
|
|
30
|
+
When node-version: 'lts/*' or node-version: '22' in setup-node resolves to
|
|
31
|
+
Node.js 22, workflows that build webpack 4 projects, older Create React App
|
|
32
|
+
setups, or any tool using MD4/RC4/legacy OpenSSL algorithms fail with
|
|
33
|
+
ERR_OSSL_UNSUPPORTED.
|
|
34
|
+
|
|
35
|
+
The common Node.js 17/18 workaround of setting
|
|
36
|
+
NODE_OPTIONS=--openssl-legacy-provider now causes a secondary error:
|
|
37
|
+
"--openssl-legacy-provider is not allowed in NODE_OPTIONS" on Node.js 22.
|
|
38
|
+
This double-failure — the original OpenSSL error AND the workaround error —
|
|
39
|
+
makes the root cause difficult to diagnose.
|
|
40
|
+
|
|
41
|
+
This is distinct from runner-environment-111 (setup-node major version upgrade
|
|
42
|
+
breaking engines field). That entry covers package.json engines compatibility;
|
|
43
|
+
this entry covers a specific CLI flag removal at the Node.js runtime level.
|
|
44
|
+
fix: |
|
|
45
|
+
The correct fix is to upgrade webpack to version 5 or migrate the project away
|
|
46
|
+
from Create React App. As a temporary workaround, pin node-version to '20'
|
|
47
|
+
while the webpack upgrade is in progress. Do not use --openssl-legacy-provider
|
|
48
|
+
as it is removed in Node.js 22 and will also error on Node.js 22.
|
|
49
|
+
fix_code:
|
|
50
|
+
- language: yaml
|
|
51
|
+
label: 'Pin to Node.js 20 LTS as temporary workaround'
|
|
52
|
+
code: |
|
|
53
|
+
- name: Setup Node.js
|
|
54
|
+
uses: actions/setup-node@v4
|
|
55
|
+
with:
|
|
56
|
+
node-version: '20' # Pin to Node 20 while upgrading webpack to v5
|
|
57
|
+
cache: 'npm'
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: 'Use .nvmrc to enforce Node version across all environments'
|
|
60
|
+
code: |
|
|
61
|
+
# .nvmrc contains: 20
|
|
62
|
+
- name: Setup Node.js
|
|
63
|
+
uses: actions/setup-node@v4
|
|
64
|
+
with:
|
|
65
|
+
node-version-file: '.nvmrc'
|
|
66
|
+
cache: 'npm'
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Test builds against Node.js 22 in a separate matrix job'
|
|
69
|
+
code: |
|
|
70
|
+
jobs:
|
|
71
|
+
test:
|
|
72
|
+
strategy:
|
|
73
|
+
matrix:
|
|
74
|
+
node: ['20', '22']
|
|
75
|
+
fail-fast: false
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- uses: actions/setup-node@v4
|
|
80
|
+
with:
|
|
81
|
+
node-version: ${{ matrix.node }}
|
|
82
|
+
cache: 'npm'
|
|
83
|
+
- run: npm ci
|
|
84
|
+
- run: npm run build
|
|
85
|
+
prevention:
|
|
86
|
+
- 'Upgrade to webpack 5 — webpack 4 uses MD4 hashes that depend on legacy OpenSSL algorithms'
|
|
87
|
+
- 'Do not set NODE_OPTIONS=--openssl-legacy-provider in workflow env — it was a temporary workaround removed in Node 22'
|
|
88
|
+
- 'Use node-version-file (.nvmrc or .node-version) to prevent automatic major version upgrades when lts/* resolves to a new major'
|
|
89
|
+
- 'Run a separate CI matrix job targeting Node.js 22 to detect future incompatibilities before they block your default branch'
|
|
90
|
+
docs:
|
|
91
|
+
- url: 'https://nodejs.org/en/blog/release/v22.0.0'
|
|
92
|
+
label: 'Node.js 22 release notes — OpenSSL legacy provider removal'
|
|
93
|
+
- url: 'https://github.com/webpack/webpack/issues/14532'
|
|
94
|
+
label: 'webpack GitHub issue: ERR_OSSL_UNSUPPORTED on Node.js 17+'
|
|
95
|
+
- url: 'https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs#specifying-the-nodejs-version'
|
|
96
|
+
label: 'GitHub Docs: Specifying the Node.js version in setup-node'
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: runner-environment-163
|
|
2
|
+
title: 'actions/setup-java Temurin distribution has no Java 8 builds for macOS ARM64 runners'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-java
|
|
7
|
+
- temurin
|
|
8
|
+
- java-8
|
|
9
|
+
- arm64
|
|
10
|
+
- macos-14
|
|
11
|
+
- macos-15
|
|
12
|
+
- apple-silicon
|
|
13
|
+
- adoptium
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: 'No matching distribution found for Java version 8.*Temurin'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'Downloading Java 8.*Temurin.*aarch64.*Error'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'Error: No matching distribution found for'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
- regex: 'Could not find satisfied version for SemVer.*8.*temurin'
|
|
22
|
+
flags: 'i'
|
|
23
|
+
error_messages:
|
|
24
|
+
- 'Error: No matching distribution found for Java version 8 and distribution Temurin'
|
|
25
|
+
- 'Downloading Java 8 (Temurin) aarch64... Not Found'
|
|
26
|
+
- 'Could not find satisfied version for SemVer 8 using temurin on darwin aarch64'
|
|
27
|
+
root_cause: |
|
|
28
|
+
Eclipse Temurin (the continuation of AdoptOpenJDK) does not publish Java 8
|
|
29
|
+
(JDK 1.8) builds for macOS ARM64 (Apple Silicon / aarch64). Java 8 reached
|
|
30
|
+
end-of-life for Temurin production support, and no ARM64 macOS binaries were
|
|
31
|
+
ever released for that version.
|
|
32
|
+
|
|
33
|
+
The macos-14 and macos-15 GitHub-hosted runners use ARM64 hardware (M-series
|
|
34
|
+
chips). When a workflow specifies distribution: temurin with java-version: '8',
|
|
35
|
+
actions/setup-java cannot find a compatible download and fails. The error is
|
|
36
|
+
surprising because the identical workflow succeeds on macos-13 (Intel x86_64)
|
|
37
|
+
where Temurin Java 8 builds do exist.
|
|
38
|
+
|
|
39
|
+
This manifests when teams migrate from macos-13 to macos-14/15 for performance
|
|
40
|
+
or cost reasons without first auditing whether their Java version is supported
|
|
41
|
+
on ARM64.
|
|
42
|
+
fix: |
|
|
43
|
+
Preferred: Upgrade to Java 11, 17, or 21 LTS — all of which have Temurin
|
|
44
|
+
ARM64 macOS builds. If Java 8 is strictly required for the build, use the
|
|
45
|
+
Zulu distribution (Azul), which publishes Java 8 builds for macOS ARM64.
|
|
46
|
+
As a last resort, use macos-13 (Intel) for legacy Java 8 jobs.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: 'Upgrade to Java 17 LTS with Temurin (recommended)'
|
|
50
|
+
code: |
|
|
51
|
+
- name: Setup Java 17
|
|
52
|
+
uses: actions/setup-java@v4
|
|
53
|
+
with:
|
|
54
|
+
distribution: temurin
|
|
55
|
+
java-version: '17' # Temurin supports ARM64 for Java 11, 17, 21
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'Use Zulu distribution for Java 8 on ARM64 (workaround)'
|
|
58
|
+
code: |
|
|
59
|
+
- name: Setup Java 8 via Zulu (ARM64-compatible)
|
|
60
|
+
uses: actions/setup-java@v4
|
|
61
|
+
with:
|
|
62
|
+
distribution: zulu # Azul Zulu provides Java 8 for macOS ARM64
|
|
63
|
+
java-version: '8'
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: 'Matrix build — Java 8 on Intel, Java 17 on ARM64'
|
|
66
|
+
code: |
|
|
67
|
+
jobs:
|
|
68
|
+
test:
|
|
69
|
+
strategy:
|
|
70
|
+
matrix:
|
|
71
|
+
include:
|
|
72
|
+
- runner: macos-13 # Intel — Temurin Java 8 available
|
|
73
|
+
java: '8'
|
|
74
|
+
- runner: macos-15 # ARM64 — Temurin Java 17 only
|
|
75
|
+
java: '17'
|
|
76
|
+
runs-on: ${{ matrix.runner }}
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/setup-java@v4
|
|
79
|
+
with:
|
|
80
|
+
distribution: temurin
|
|
81
|
+
java-version: ${{ matrix.java }}
|
|
82
|
+
prevention:
|
|
83
|
+
- 'Upgrade to Java 11, 17, or 21 LTS — Java 8 is past EOL for Temurin and has no ARM64 macOS builds'
|
|
84
|
+
- 'When migrating from macos-13 (Intel) to macos-14/15 (ARM64), audit all Java version requirements first'
|
|
85
|
+
- 'Use the Zulu distribution as a drop-in replacement for Java 8 when ARM64 support is required'
|
|
86
|
+
- 'Check Adoptium release availability at adoptium.net/temurin/releases before assuming a version exists on your target platform'
|
|
87
|
+
docs:
|
|
88
|
+
- url: 'https://adoptium.net/temurin/releases/?version=8&os=mac&arch=aarch64'
|
|
89
|
+
label: 'Eclipse Temurin releases — Java 8 macOS AArch64 availability'
|
|
90
|
+
- url: 'https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#supported-distributions'
|
|
91
|
+
label: 'actions/setup-java: Supported distributions and platforms'
|
|
92
|
+
- url: 'https://endoflife.date/eclipse-temurin'
|
|
93
|
+
label: 'Eclipse Temurin end-of-life dates'
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
id: runner-environment-160
|
|
2
|
+
title: 'ubuntu-24.04 apt-get install hangs on tzdata interactive timezone prompt'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- ubuntu-24.04
|
|
7
|
+
- apt-get
|
|
8
|
+
- tzdata
|
|
9
|
+
- noninteractive
|
|
10
|
+
- hang
|
|
11
|
+
- debian-frontend
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Please select the geographic area in which you live'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Configuring tzdata'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'debconf: unable to initialize frontend: Dialog'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- 'Please select the geographic area in which you live.'
|
|
21
|
+
- 'Configuring tzdata'
|
|
22
|
+
- 'debconf: unable to initialize frontend: Dialog'
|
|
23
|
+
- 'debconf: falling back to frontend: Readline'
|
|
24
|
+
root_cause: |
|
|
25
|
+
When installing packages that depend on tzdata (tzdata itself, libssl-dev, php,
|
|
26
|
+
libpq-dev, and many others) without setting DEBIAN_FRONTEND=noninteractive,
|
|
27
|
+
apt-get sends interactive prompts for timezone configuration to the TTY. GitHub
|
|
28
|
+
Actions runner steps have no interactive TTY to respond to debconf prompts, so
|
|
29
|
+
the step hangs indefinitely until the 6-hour job timeout is reached.
|
|
30
|
+
|
|
31
|
+
Ubuntu 24.04 runners are particularly susceptible because the debconf default
|
|
32
|
+
frontend is "dialog", which requires a terminal. The hang appears to come from
|
|
33
|
+
a frozen step with no output — making it look like a performance issue rather
|
|
34
|
+
than a prompt waiting for input.
|
|
35
|
+
|
|
36
|
+
The symptom is often indirect: a workflow step that installs a package with
|
|
37
|
+
tzdata as a transitive dependency (not an explicitly installed package) silently
|
|
38
|
+
hangs. Developers are surprised because the same apt-get install command works
|
|
39
|
+
fine in an interactive shell.
|
|
40
|
+
fix: |
|
|
41
|
+
Set DEBIAN_FRONTEND=noninteractive in the step or job env block before running
|
|
42
|
+
apt-get install. For tzdata specifically, pre-configure the timezone using
|
|
43
|
+
environment variables or a symlink before installation.
|
|
44
|
+
fix_code:
|
|
45
|
+
- language: yaml
|
|
46
|
+
label: 'Set DEBIAN_FRONTEND at step level'
|
|
47
|
+
code: |
|
|
48
|
+
- name: Install dependencies
|
|
49
|
+
env:
|
|
50
|
+
DEBIAN_FRONTEND: noninteractive
|
|
51
|
+
run: |
|
|
52
|
+
sudo apt-get update
|
|
53
|
+
sudo apt-get install -y tzdata libssl-dev
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: 'Set DEBIAN_FRONTEND at job level (applies to all steps)'
|
|
56
|
+
code: |
|
|
57
|
+
jobs:
|
|
58
|
+
build:
|
|
59
|
+
runs-on: ubuntu-24.04
|
|
60
|
+
env:
|
|
61
|
+
DEBIAN_FRONTEND: noninteractive
|
|
62
|
+
steps:
|
|
63
|
+
- name: Install dependencies
|
|
64
|
+
run: |
|
|
65
|
+
sudo apt-get update
|
|
66
|
+
sudo apt-get install -y tzdata
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Pre-configure timezone to avoid the prompt entirely'
|
|
69
|
+
code: |
|
|
70
|
+
- name: Install tzdata non-interactively
|
|
71
|
+
run: |
|
|
72
|
+
sudo ln -snf /usr/share/zoneinfo/UTC /etc/localtime
|
|
73
|
+
echo UTC | sudo tee /etc/timezone
|
|
74
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
|
|
75
|
+
prevention:
|
|
76
|
+
- 'Always set DEBIAN_FRONTEND=noninteractive when running apt-get in CI environments'
|
|
77
|
+
- 'Set DEBIAN_FRONTEND at the job level env block to protect all steps automatically'
|
|
78
|
+
- 'Check transitive dependencies with apt-cache show <package> to detect tzdata deps early'
|
|
79
|
+
- 'Add a timeout-minutes to critical steps so tzdata hangs fail fast instead of blocking for 6 hours'
|
|
80
|
+
docs:
|
|
81
|
+
- url: 'https://manpages.ubuntu.com/manpages/noble/man7/debconf.7.html'
|
|
82
|
+
label: 'debconf manual page — debconf frontends and noninteractive mode'
|
|
83
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables'
|
|
84
|
+
label: 'GitHub Actions: Store information in variables'
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
id: silent-failures-087
|
|
2
|
+
title: '`fail-fast: false` Does Not Prevent Downstream `needs:` Jobs From Being Skipped When Matrix Jobs Fail'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- fail-fast
|
|
8
|
+
- needs
|
|
9
|
+
- downstream-job
|
|
10
|
+
- skipped
|
|
11
|
+
- strategy
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'fail-fast\s*:\s*false'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'needs\s*:.*matrix|matrix.*needs\s*:'
|
|
16
|
+
flags: 'im'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "This job was skipped"
|
|
19
|
+
- "Job 'X' is skipped because all dependencies failed or were skipped"
|
|
20
|
+
root_cause: |
|
|
21
|
+
`strategy.fail-fast: false` only prevents other MATRIX jobs from being
|
|
22
|
+
cancelled when one matrix job fails. It does NOT affect how downstream
|
|
23
|
+
jobs that `needs:` the matrix job handle the overall result.
|
|
24
|
+
|
|
25
|
+
When at least one matrix job fails (even with `fail-fast: false` allowing
|
|
26
|
+
all others to complete), the `needs.<matrix-job>.result` for the downstream
|
|
27
|
+
job evaluates to `'failure'`. A downstream job with a standard `needs:`
|
|
28
|
+
dependency is then SKIPPED — not because of `fail-fast`, but because its
|
|
29
|
+
dependency is in a failed state.
|
|
30
|
+
|
|
31
|
+
This surprises developers who add `fail-fast: false` expecting the entire
|
|
32
|
+
pipeline to continue running end-to-end. The final summary/report job
|
|
33
|
+
that needs the matrix job is silently skipped, producing no output.
|
|
34
|
+
fix: |
|
|
35
|
+
Add `if: always()` to downstream jobs that should run regardless of matrix
|
|
36
|
+
outcome. Then explicitly check `needs.<job>.result` values to determine
|
|
37
|
+
pass/fail:
|
|
38
|
+
|
|
39
|
+
- `if: always()` ensures the job is never skipped due to upstream failure
|
|
40
|
+
- Check `contains(needs.*.result, 'failure')` to detect any matrix failure
|
|
41
|
+
- Use `needs.<job>.result == 'success'` for strict pass gates
|
|
42
|
+
fix_code:
|
|
43
|
+
- language: yaml
|
|
44
|
+
label: "WRONG — summary job silently skipped when matrix job fails"
|
|
45
|
+
code: |
|
|
46
|
+
jobs:
|
|
47
|
+
test:
|
|
48
|
+
strategy:
|
|
49
|
+
fail-fast: false # ❌ allows all matrix jobs to run, but...
|
|
50
|
+
matrix:
|
|
51
|
+
node: [18, 20, 22]
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
steps:
|
|
54
|
+
- run: npm test
|
|
55
|
+
|
|
56
|
+
summary:
|
|
57
|
+
needs: test # ❌ still skipped if any matrix job failed
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- run: echo "All tests done"
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: "RIGHT — use if: always() and check result explicitly"
|
|
63
|
+
code: |
|
|
64
|
+
jobs:
|
|
65
|
+
test:
|
|
66
|
+
strategy:
|
|
67
|
+
fail-fast: false
|
|
68
|
+
matrix:
|
|
69
|
+
node: [18, 20, 22]
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
- run: npm test
|
|
73
|
+
|
|
74
|
+
summary:
|
|
75
|
+
needs: test
|
|
76
|
+
if: always() # ✅ runs even when test jobs failed
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- name: Check matrix results
|
|
80
|
+
if: contains(needs.test.result, 'failure')
|
|
81
|
+
run: |
|
|
82
|
+
echo "One or more matrix jobs failed"
|
|
83
|
+
exit 1
|
|
84
|
+
- name: Success
|
|
85
|
+
run: echo "All matrix jobs passed"
|
|
86
|
+
- language: yaml
|
|
87
|
+
label: "RIGHT — gate final deploy only when all matrix jobs pass"
|
|
88
|
+
code: |
|
|
89
|
+
jobs:
|
|
90
|
+
test:
|
|
91
|
+
strategy:
|
|
92
|
+
fail-fast: false
|
|
93
|
+
matrix:
|
|
94
|
+
node: [18, 20, 22]
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
outputs:
|
|
97
|
+
result: ${{ job.status }}
|
|
98
|
+
steps:
|
|
99
|
+
- run: npm test
|
|
100
|
+
|
|
101
|
+
deploy:
|
|
102
|
+
needs: test
|
|
103
|
+
if: always() && needs.test.result == 'success' # ✅ explicit success check
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
steps:
|
|
106
|
+
- run: echo "Deploying"
|
|
107
|
+
prevention:
|
|
108
|
+
- "Always add `if: always()` to aggregation/summary/deploy jobs that need to run after a matrix job."
|
|
109
|
+
- "Explicitly check `needs.<job>.result` or `contains(needs.*.result, 'failure')` rather than relying on implicit pass-through."
|
|
110
|
+
- "Understand that `fail-fast: false` is matrix-scoped — it does not change how the `needs:` graph handles failures."
|
|
111
|
+
- "Use job outputs combined with `if: always()` to build robust reporting jobs that capture all matrix outcomes."
|
|
112
|
+
docs:
|
|
113
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow#defining-prerequisite-jobs"
|
|
114
|
+
label: "Defining prerequisite jobs — needs context and result values"
|
|
115
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast"
|
|
116
|
+
label: "workflow syntax — jobs.<job_id>.strategy.fail-fast"
|
|
117
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#status-check-functions"
|
|
118
|
+
label: "Status check functions — always(), success(), failure()"
|
|
119
|
+
- url: "https://github.com/orgs/community/discussions/26822"
|
|
120
|
+
label: "GitHub Community — fail-fast: false but summary job still skipped"
|
package/package.json
CHANGED