@htekdev/actions-debugger 1.0.53 → 1.0.55
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-037.yml +95 -0
- package/errors/permissions-auth/permissions-auth-039.yml +108 -0
- package/errors/runner-environment/runner-environment-111.yml +93 -0
- package/errors/silent-failures/silent-failures-054.yml +100 -0
- package/errors/silent-failures/silent-failures-055.yml +99 -0
- package/errors/triggers/triggers-039.yml +115 -0
- package/errors/yaml-syntax/yaml-syntax-038.yml +91 -0
- package/errors/yaml-syntax/yaml-syntax-039.yml +115 -0
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
id: caching-artifacts-037
|
|
2
|
+
title: "upload-artifact if-no-files-found defaults to 'warn' — empty upload succeeds, download job fails"
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- upload-artifact
|
|
7
|
+
- if-no-files-found
|
|
8
|
+
- artifacts
|
|
9
|
+
- silent
|
|
10
|
+
- warn
|
|
11
|
+
- download
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'No files were found with the provided path'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'No artifact uploads were performed'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'was not found for the associated workflow run'
|
|
18
|
+
flags: i
|
|
19
|
+
error_messages:
|
|
20
|
+
- "No files were found with the provided path: ./dist. No artifacts will be uploaded."
|
|
21
|
+
- "Warning: No files were found with the provided path: build/"
|
|
22
|
+
- "No artifact uploads were performed."
|
|
23
|
+
- "Error: An artifact named build-output was not found for the associated workflow run."
|
|
24
|
+
root_cause: |
|
|
25
|
+
The if-no-files-found input of actions/upload-artifact defaults to warn, not error.
|
|
26
|
+
When the upload path: glob matches no files — because the build directory is missing,
|
|
27
|
+
a glob pattern is wrong, the working-directory setting differs from the upload path,
|
|
28
|
+
or a preceding build step failed silently — the upload step logs a warning message and
|
|
29
|
+
exits with code 0 (success).
|
|
30
|
+
|
|
31
|
+
The calling workflow sees a green upload step in the UI. The problem only surfaces in
|
|
32
|
+
the downstream job that calls actions/download-artifact, which fails with an error like
|
|
33
|
+
"An artifact named X was not found for the associated workflow run."
|
|
34
|
+
|
|
35
|
+
Developers spend time debugging the download step or the job that uses the artifact when
|
|
36
|
+
the real problem — the build not producing output — occurred earlier, often in a different
|
|
37
|
+
job. The default warn behavior exists for optional artifacts, but it is a frequent source
|
|
38
|
+
of confusion for required CI artifacts.
|
|
39
|
+
fix: |
|
|
40
|
+
Set if-no-files-found: error on every upload step where the artifact is required.
|
|
41
|
+
The upload step will immediately fail with a descriptive error message that names the
|
|
42
|
+
missing path, pointing directly to the correct job.
|
|
43
|
+
|
|
44
|
+
Reserve warn or ignore only for genuinely optional artifacts — for example, test
|
|
45
|
+
screenshots that only exist when tests fail, or coverage reports that may be skipped
|
|
46
|
+
in some build configurations.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: "Correct: fail immediately when required build output is missing"
|
|
50
|
+
code: |
|
|
51
|
+
- name: Upload build artifacts
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: build-output
|
|
55
|
+
path: ./dist/
|
|
56
|
+
if-no-files-found: error # Fail here — not in the downstream download job
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Optional artifact — keep warn or ignore"
|
|
59
|
+
code: |
|
|
60
|
+
- name: Upload test screenshots (optional — only exist on test failure)
|
|
61
|
+
if: failure()
|
|
62
|
+
uses: actions/upload-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: test-screenshots
|
|
65
|
+
path: ./test-results/screenshots/
|
|
66
|
+
if-no-files-found: ignore # OK — screenshots only exist when tests fail
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "Verify build output before uploading"
|
|
69
|
+
code: |
|
|
70
|
+
- name: Verify dist/ was built
|
|
71
|
+
run: |
|
|
72
|
+
if [ ! -d "./dist" ] || [ -z "$(ls -A ./dist)" ]; then
|
|
73
|
+
echo "ERROR: dist/ directory is empty or missing"
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
- name: Upload build artifacts
|
|
78
|
+
uses: actions/upload-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: build-output
|
|
81
|
+
path: ./dist/
|
|
82
|
+
if-no-files-found: error
|
|
83
|
+
prevention:
|
|
84
|
+
- "Default to if-no-files-found: error in all CI pipeline templates for required artifacts"
|
|
85
|
+
- "Note that upload path: is relative to GITHUB_WORKSPACE, not the step's working-directory setting"
|
|
86
|
+
- "Add an explicit build verification step before upload to fail fast with a clear message"
|
|
87
|
+
- "Audit existing workflows: any upload-artifact step without if-no-files-found: error is a silent failure risk"
|
|
88
|
+
- "When a build step uses continue-on-error: true, verify it did not silently skip output generation before uploading"
|
|
89
|
+
docs:
|
|
90
|
+
- url: "https://github.com/actions/upload-artifact#inputs"
|
|
91
|
+
label: "actions/upload-artifact: Input parameters reference"
|
|
92
|
+
- url: "https://github.com/actions/upload-artifact/blob/main/RELEASES.md"
|
|
93
|
+
label: "actions/upload-artifact: Release notes"
|
|
94
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow"
|
|
95
|
+
label: "GitHub Docs: Storing and sharing data from a workflow"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
id: permissions-auth-039
|
|
2
|
+
title: "setup-node registry-url creates .npmrc but NODE_AUTH_TOKEN not set → npm E401"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-node
|
|
7
|
+
- npm
|
|
8
|
+
- registry
|
|
9
|
+
- authentication
|
|
10
|
+
- publish
|
|
11
|
+
- node_auth_token
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'npm ERR! code E401'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'npm ERR! 401 Unauthorized'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'npm ERR! need auth'
|
|
18
|
+
flags: i
|
|
19
|
+
- regex: 'npm error code EBADAUTH'
|
|
20
|
+
flags: i
|
|
21
|
+
error_messages:
|
|
22
|
+
- "npm ERR! code E401"
|
|
23
|
+
- "npm ERR! 401 Unauthorized - PUT https://registry.npmjs.org/@scope/package-name"
|
|
24
|
+
- "npm ERR! need auth You need to authorize this machine using `npm adduser`"
|
|
25
|
+
- "npm error code EBADAUTH"
|
|
26
|
+
root_cause: |
|
|
27
|
+
When registry-url is set in actions/setup-node, the action generates an .npmrc file
|
|
28
|
+
containing a token placeholder line such as:
|
|
29
|
+
//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
|
|
30
|
+
|
|
31
|
+
The variable name NODE_AUTH_TOKEN is hardcoded in this template and must be supplied
|
|
32
|
+
as an environment variable at runtime on every step that communicates with the registry.
|
|
33
|
+
|
|
34
|
+
If the npm publish or npm install step does not declare
|
|
35
|
+
`NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` in its env: block, the placeholder expands
|
|
36
|
+
to an empty string. npm sends an unauthenticated request and receives HTTP 401
|
|
37
|
+
Unauthorized from the registry.
|
|
38
|
+
|
|
39
|
+
The setup-node step itself succeeds with no warnings — there is no validation that
|
|
40
|
+
NODE_AUTH_TOKEN will be set. The 401 only surfaces during the npm command, misleading
|
|
41
|
+
developers to investigate the token or registry configuration rather than the missing
|
|
42
|
+
env: declaration.
|
|
43
|
+
fix: |
|
|
44
|
+
Add NODE_AUTH_TOKEN to the env: block of every step that runs npm commands against the
|
|
45
|
+
authenticated registry. The variable name must be exactly NODE_AUTH_TOKEN — npm reads
|
|
46
|
+
it directly from the .npmrc template generated by setup-node.
|
|
47
|
+
|
|
48
|
+
For GitHub Packages (npm.pkg.github.com), use secrets.GITHUB_TOKEN.
|
|
49
|
+
For npmjs.com, use a dedicated automation token stored as a repository secret (e.g. NPM_TOKEN).
|
|
50
|
+
|
|
51
|
+
If all registry requests — including npm install of private scoped packages — need
|
|
52
|
+
authentication (not just publish), also set always-auth: true in the setup-node step.
|
|
53
|
+
fix_code:
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: "Correct: NODE_AUTH_TOKEN on the publish step"
|
|
56
|
+
code: |
|
|
57
|
+
- name: Set up Node.js
|
|
58
|
+
uses: actions/setup-node@v4
|
|
59
|
+
with:
|
|
60
|
+
node-version: '20'
|
|
61
|
+
registry-url: 'https://registry.npmjs.org'
|
|
62
|
+
|
|
63
|
+
- name: Publish to npm
|
|
64
|
+
run: npm publish
|
|
65
|
+
env:
|
|
66
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "GitHub Packages registry variant"
|
|
69
|
+
code: |
|
|
70
|
+
- name: Set up Node.js
|
|
71
|
+
uses: actions/setup-node@v4
|
|
72
|
+
with:
|
|
73
|
+
node-version: '20'
|
|
74
|
+
registry-url: 'https://npm.pkg.github.com'
|
|
75
|
+
scope: '@your-org'
|
|
76
|
+
|
|
77
|
+
- name: Publish to GitHub Packages
|
|
78
|
+
run: npm publish
|
|
79
|
+
env:
|
|
80
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
81
|
+
- language: yaml
|
|
82
|
+
label: "Private package install with always-auth"
|
|
83
|
+
code: |
|
|
84
|
+
- name: Set up Node.js
|
|
85
|
+
uses: actions/setup-node@v4
|
|
86
|
+
with:
|
|
87
|
+
node-version: '20'
|
|
88
|
+
registry-url: 'https://npm.pkg.github.com'
|
|
89
|
+
scope: '@your-org'
|
|
90
|
+
always-auth: true # Send auth on all requests, not just publish
|
|
91
|
+
|
|
92
|
+
- name: Install dependencies (includes private scoped packages)
|
|
93
|
+
run: npm ci
|
|
94
|
+
env:
|
|
95
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
96
|
+
prevention:
|
|
97
|
+
- "Every npm step that interacts with an authenticated registry must declare NODE_AUTH_TOKEN in its env: block"
|
|
98
|
+
- "Setting registry-url in setup-node does NOT automatically forward any secrets to npm — the env: mapping is always required"
|
|
99
|
+
- "Use always-auth: true in setup-node when npm install (not just publish) must authenticate, such as for private scoped packages"
|
|
100
|
+
- "Store your npm automation token as a repository secret: Settings → Secrets and variables → Actions → New repository secret"
|
|
101
|
+
- "For GitHub Packages, NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} is sufficient — no separate token needed"
|
|
102
|
+
docs:
|
|
103
|
+
- url: "https://github.com/actions/setup-node#publishing-to-npmjs-and-github-packages-registries"
|
|
104
|
+
label: "actions/setup-node: Publishing to registries"
|
|
105
|
+
- url: "https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry"
|
|
106
|
+
label: "GitHub Docs: Working with the npm registry"
|
|
107
|
+
- url: "https://docs.npmjs.com/using-private-packages-in-a-ci-cd-workflow"
|
|
108
|
+
label: "npm Docs: Using private packages in CI/CD"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: runner-environment-111
|
|
2
|
+
title: "setup-node node-version: 'latest' silently upgrades to new Node.js major, breaking engines field"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- setup-node
|
|
7
|
+
- node-version
|
|
8
|
+
- latest
|
|
9
|
+
- engines
|
|
10
|
+
- breaking-change
|
|
11
|
+
- major-version
|
|
12
|
+
- semver
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'The engine "node" is incompatible with this module'
|
|
15
|
+
flags: i
|
|
16
|
+
- regex: 'EBADENGINE.*Unsupported engine'
|
|
17
|
+
flags: i
|
|
18
|
+
- regex: 'npm warn EBADENGINE'
|
|
19
|
+
flags: i
|
|
20
|
+
- regex: 'engine.*node.*incompatible.*Expected version'
|
|
21
|
+
flags: i
|
|
22
|
+
error_messages:
|
|
23
|
+
- "error The engine \"node\" is incompatible with this module. Expected version \">=18 <21\". Got \"23.x.x\""
|
|
24
|
+
- "npm warn EBADENGINE Unsupported engine { required: { node: '>=16 <20' }, current: { node: 'v22.0.0' } }"
|
|
25
|
+
- "npm error code EBADENGINE"
|
|
26
|
+
- "error This package requires Node.js >= 18.0.0 and <= 22.x"
|
|
27
|
+
root_cause: |
|
|
28
|
+
When node-version: 'latest' is used in actions/setup-node, the action resolves the
|
|
29
|
+
alias against the official Node.js release schedule manifest on every workflow run.
|
|
30
|
+
When Node.js releases a new major version (e.g. v23.0.0, v24.0.0), the next run silently
|
|
31
|
+
downloads and activates the new major without any warning in the setup-node step output.
|
|
32
|
+
|
|
33
|
+
Packages that declare an engines constraint in their package.json (e.g.
|
|
34
|
+
engines: { node: ">=18 <22" }) then fail during npm install or yarn install with
|
|
35
|
+
EBADENGINE because the newly installed version exceeds the accepted range.
|
|
36
|
+
|
|
37
|
+
The setup-node step itself succeeds and logs the installed version; the failure only
|
|
38
|
+
appears downstream when the package manager processes the engines field. Because the
|
|
39
|
+
workflow ran successfully for months before the Node.js major release, developers
|
|
40
|
+
are not expecting a version bump and may incorrectly blame a dependency change.
|
|
41
|
+
|
|
42
|
+
Native addons and frameworks that have not yet published compatibility updates for the
|
|
43
|
+
new major can also fail during postinstall or build steps.
|
|
44
|
+
fix: |
|
|
45
|
+
Pin to a specific LTS major version string (e.g. '20', '22') rather than 'latest'.
|
|
46
|
+
LTS majors receive security patches but do not automatically jump to a new major.
|
|
47
|
+
|
|
48
|
+
Alternatively, use the node-version-file input to read the version from a .nvmrc or
|
|
49
|
+
.node-version file committed to the repository. This keeps the Node.js version
|
|
50
|
+
consistent between local development and CI and makes version upgrades explicit
|
|
51
|
+
(a PR changes the version file).
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: "Pin to a specific LTS major (recommended)"
|
|
55
|
+
code: |
|
|
56
|
+
- name: Set up Node.js
|
|
57
|
+
uses: actions/setup-node@v4
|
|
58
|
+
with:
|
|
59
|
+
node-version: '22' # Pinned LTS major — will not jump to Node 24 automatically
|
|
60
|
+
cache: 'npm'
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: "Use .nvmrc for repo-defined version shared with local dev"
|
|
63
|
+
code: |
|
|
64
|
+
# .nvmrc (committed to repository root)
|
|
65
|
+
# 22.x
|
|
66
|
+
|
|
67
|
+
- name: Set up Node.js
|
|
68
|
+
uses: actions/setup-node@v4
|
|
69
|
+
with:
|
|
70
|
+
node-version-file: '.nvmrc' # Same version in local dev and CI
|
|
71
|
+
cache: 'npm'
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "Pin with explicit patch version for maximum reproducibility"
|
|
74
|
+
code: |
|
|
75
|
+
- name: Set up Node.js
|
|
76
|
+
uses: actions/setup-node@v4
|
|
77
|
+
with:
|
|
78
|
+
node-version: '22.13.1' # Exact patch — fully reproducible but requires manual updates
|
|
79
|
+
cache: 'npm'
|
|
80
|
+
prevention:
|
|
81
|
+
- "Never use node-version: 'latest' in production or long-lived CI workflows — pin to an LTS major"
|
|
82
|
+
- "Prefer '18', '20', or '22' (active LTS) — these receive security patches without surprise major bumps"
|
|
83
|
+
- "Use node-version-file pointing to .nvmrc to keep local development and CI on the same version"
|
|
84
|
+
- "Use Renovate or Dependabot to automate controlled Node.js upgrades with a PR and changelog review"
|
|
85
|
+
- "Test new Node.js majors in a dedicated branch before adopting them in main CI"
|
|
86
|
+
- "Declare an engines field in package.json to make your Node.js version requirement explicit and testable"
|
|
87
|
+
docs:
|
|
88
|
+
- url: "https://github.com/actions/setup-node#supported-version-syntax"
|
|
89
|
+
label: "actions/setup-node: Supported version syntax"
|
|
90
|
+
- url: "https://nodejs.org/en/about/previous-releases"
|
|
91
|
+
label: "Node.js: Release schedule and LTS versions"
|
|
92
|
+
- url: "https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines"
|
|
93
|
+
label: "npm: package.json engines field documentation"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
id: silent-failures-054
|
|
2
|
+
title: "Windows CRLF line endings in committed scripts cause bad interpreter error on Linux runners"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- crlf
|
|
8
|
+
- line-endings
|
|
9
|
+
- windows
|
|
10
|
+
- bash
|
|
11
|
+
- gitattributes
|
|
12
|
+
- bad-interpreter
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'bad interpreter.*No such file or directory'
|
|
15
|
+
flags: i
|
|
16
|
+
- regex: '\^M: command not found'
|
|
17
|
+
flags: ''
|
|
18
|
+
- regex: '/bin/bash\^M'
|
|
19
|
+
flags: ''
|
|
20
|
+
- regex: '\r: command not found'
|
|
21
|
+
flags: ''
|
|
22
|
+
error_messages:
|
|
23
|
+
- "/bin/bash^M: bad interpreter: No such file or directory"
|
|
24
|
+
- "^M: command not found"
|
|
25
|
+
- ": /bin/sh^M: bad interpreter: No such file or directory"
|
|
26
|
+
- "syntax error: unexpected end of file"
|
|
27
|
+
root_cause: |
|
|
28
|
+
Shell scripts, Python files, and other text files committed from Windows workstations
|
|
29
|
+
where core.autocrlf is false (or not configured) retain Windows CRLF (\r\n) line endings.
|
|
30
|
+
actions/checkout preserves committed bytes exactly — it does not normalize line endings.
|
|
31
|
+
|
|
32
|
+
On a Linux runner, the kernel reads the shebang line #!/bin/bash\r as the interpreter
|
|
33
|
+
path /bin/bash^M (with a literal carriage return appended). No file with that name exists,
|
|
34
|
+
so the kernel returns "bad interpreter: No such file or directory." The error message
|
|
35
|
+
looks like a missing binary or path problem, masking the true cause: CRLF line endings.
|
|
36
|
+
|
|
37
|
+
This is a classic silent failure because:
|
|
38
|
+
- The developer's Windows machine runs the script correctly
|
|
39
|
+
- The checkout step succeeds with no warnings about line endings
|
|
40
|
+
- The failure only manifests when the script is executed on a Linux runner
|
|
41
|
+
- Most text editors hide the ^M characters, so the file looks normal in review
|
|
42
|
+
fix: |
|
|
43
|
+
Add a .gitattributes file to the repository root specifying LF normalization for text
|
|
44
|
+
files. This instructs Git to store files with LF endings in the repository regardless
|
|
45
|
+
of the committer's OS or local git configuration.
|
|
46
|
+
|
|
47
|
+
After adding .gitattributes, re-normalize all tracked files: stage all files with the
|
|
48
|
+
renormalize flag (e.g. `add --renormalize .` via the CLI), then commit the result.
|
|
49
|
+
Without this step, already-committed CRLF files remain unchanged.
|
|
50
|
+
|
|
51
|
+
Also add a CI detection step to catch any future regressions before they reach main.
|
|
52
|
+
fix_code:
|
|
53
|
+
- language: yaml
|
|
54
|
+
label: ".gitattributes — enforce LF for scripts and text files"
|
|
55
|
+
code: |
|
|
56
|
+
# .gitattributes (add to repository root)
|
|
57
|
+
|
|
58
|
+
# Normalize all text files to LF in the repository
|
|
59
|
+
* text=auto eol=lf
|
|
60
|
+
|
|
61
|
+
# Explicitly enforce LF for scripts and config files
|
|
62
|
+
*.sh text eol=lf
|
|
63
|
+
*.bash text eol=lf
|
|
64
|
+
*.py text eol=lf
|
|
65
|
+
*.yml text eol=lf
|
|
66
|
+
*.yaml text eol=lf
|
|
67
|
+
*.json text eol=lf
|
|
68
|
+
|
|
69
|
+
# Keep CRLF for Windows-specific files
|
|
70
|
+
*.bat text eol=crlf
|
|
71
|
+
*.cmd text eol=crlf
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "CI step to detect CRLF in shell and Python scripts"
|
|
74
|
+
code: |
|
|
75
|
+
- name: Check for CRLF line endings in scripts
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- name: Detect CRLF
|
|
80
|
+
run: |
|
|
81
|
+
found=$(find . -name '*.sh' -o -name '*.py' -o -name '*.yml' | \
|
|
82
|
+
xargs file 2>/dev/null | grep CRLF || true)
|
|
83
|
+
if [ -n "$found" ]; then
|
|
84
|
+
echo "ERROR: CRLF line endings detected in the following files:"
|
|
85
|
+
echo "$found"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
prevention:
|
|
89
|
+
- "Add .gitattributes with `* text=auto eol=lf` to every repository that may be edited on Windows"
|
|
90
|
+
- "Re-normalize existing files after adding .gitattributes: use the CLI `add --renormalize .` flag, then commit"
|
|
91
|
+
- "Configure the autocrlf setting on Windows development machines: set core.autocrlf=input so CRLF is converted to LF on commit"
|
|
92
|
+
- "Configure your editor (VS Code, Notepad++, JetBrains) to use LF line endings for shell and YAML files by default"
|
|
93
|
+
- "Add a CRLF detection step in CI to fail PRs that introduce Windows line endings into script files"
|
|
94
|
+
docs:
|
|
95
|
+
- url: "https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings"
|
|
96
|
+
label: "GitHub Docs: Configuring Git to handle line endings"
|
|
97
|
+
- url: "https://git-scm.com/docs/gitattributes"
|
|
98
|
+
label: "Git: gitattributes documentation"
|
|
99
|
+
- url: "https://github.com/actions/checkout/issues/135"
|
|
100
|
+
label: "actions/checkout Issue #135: Line ending normalization"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
id: silent-failures-055
|
|
2
|
+
title: "'github.event_name' is 'workflow_call' inside reusable workflows — not the caller's event"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- reusable-workflow
|
|
7
|
+
- workflow_call
|
|
8
|
+
- event_name
|
|
9
|
+
- github-context
|
|
10
|
+
- conditional
|
|
11
|
+
- silent
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'github\.event_name\s*==\s*[''"]push[''"]'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'github\.event_name\s*==\s*[''"]pull_request[''"]'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'github\.event_name\s*==\s*[''"]workflow_dispatch[''"]'
|
|
18
|
+
flags: i
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Evaluating: github.event_name == 'push' => false"
|
|
21
|
+
- "Step skipped: if condition 'github.event_name == \"push\"' was false"
|
|
22
|
+
root_cause: |
|
|
23
|
+
When a workflow is called as a reusable workflow via on: workflow_call, GitHub Actions
|
|
24
|
+
sets github.event_name to "workflow_call" inside that workflow — NOT to the triggering
|
|
25
|
+
event from the caller workflow.
|
|
26
|
+
|
|
27
|
+
A common mistake is to write conditional logic in a reusable workflow that checks
|
|
28
|
+
github.event_name expecting the caller's event (push, pull_request, workflow_dispatch,
|
|
29
|
+
etc.). These conditions always evaluate to false when the workflow is invoked via
|
|
30
|
+
workflow_call, silently skipping the affected steps or entire jobs.
|
|
31
|
+
|
|
32
|
+
The behaviour is especially confusing because developers often test the reusable workflow
|
|
33
|
+
in isolation via workflow_dispatch — in which case github.event_name IS correct. The
|
|
34
|
+
silent skip only appears when the workflow is called from another workflow, and the skipped
|
|
35
|
+
step or job disappears from the run summary with no error message.
|
|
36
|
+
fix: |
|
|
37
|
+
Pass the caller's event name as an explicit input to the reusable workflow. The caller
|
|
38
|
+
reads github.event_name from its own context (where it is correct) and passes it as a
|
|
39
|
+
string input. Inside the reusable workflow, check inputs.caller_event (or whatever name
|
|
40
|
+
you choose) instead of github.event_name.
|
|
41
|
+
|
|
42
|
+
Alternatively, restructure so that event-specific logic stays in the caller workflow and
|
|
43
|
+
the reusable workflow only receives final parameters — not the raw event name.
|
|
44
|
+
fix_code:
|
|
45
|
+
- language: yaml
|
|
46
|
+
label: "Problem: event_name check always false in reusable workflow"
|
|
47
|
+
code: |
|
|
48
|
+
# reusable.yml — WRONG: event_name is always "workflow_call" here
|
|
49
|
+
on:
|
|
50
|
+
workflow_call:
|
|
51
|
+
|
|
52
|
+
jobs:
|
|
53
|
+
build:
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- name: Deploy only on push
|
|
57
|
+
if: github.event_name == 'push' # Always false — event_name is "workflow_call"
|
|
58
|
+
run: echo "Deploying..."
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: "Fix: accept caller_event as an explicit input"
|
|
61
|
+
code: |
|
|
62
|
+
# reusable.yml — CORRECT: accept the caller's event as an input
|
|
63
|
+
on:
|
|
64
|
+
workflow_call:
|
|
65
|
+
inputs:
|
|
66
|
+
caller_event:
|
|
67
|
+
type: string
|
|
68
|
+
required: true
|
|
69
|
+
|
|
70
|
+
jobs:
|
|
71
|
+
build:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
steps:
|
|
74
|
+
- name: Deploy only on push
|
|
75
|
+
if: inputs.caller_event == 'push' # Correct — uses the passed-in value
|
|
76
|
+
run: echo "Deploying..."
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Caller workflow: pass github.event_name as input"
|
|
79
|
+
code: |
|
|
80
|
+
# caller.yml — pass the caller's event name to the reusable workflow
|
|
81
|
+
on: [push, pull_request]
|
|
82
|
+
|
|
83
|
+
jobs:
|
|
84
|
+
call-reusable:
|
|
85
|
+
uses: ./.github/workflows/reusable.yml
|
|
86
|
+
with:
|
|
87
|
+
caller_event: ${{ github.event_name }} # "push" or "pull_request"
|
|
88
|
+
prevention:
|
|
89
|
+
- "Never use 'github.event_name' in a reusable workflow to detect the caller's event — it will always be 'workflow_call'"
|
|
90
|
+
- "Pass event-specific data as explicit inputs; document expected values in the input description field"
|
|
91
|
+
- "When testing a reusable workflow via workflow_dispatch, simulate the production caller_event value via inputs to catch conditional errors early"
|
|
92
|
+
- "The same rule applies to github.event.* properties — many will be empty or refer to the workflow_call event, not the caller's event"
|
|
93
|
+
docs:
|
|
94
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows"
|
|
95
|
+
label: "GitHub Docs: Reusing workflows"
|
|
96
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context"
|
|
97
|
+
label: "GitHub Docs: github context reference"
|
|
98
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#limitations"
|
|
99
|
+
label: "GitHub Docs: Reusable workflow limitations"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: triggers-039
|
|
2
|
+
title: "'github.sha' in pull_request_target points to base branch HEAD, not the PR's head commit"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- pull_request_target
|
|
7
|
+
- github-sha
|
|
8
|
+
- base-branch
|
|
9
|
+
- pr-head
|
|
10
|
+
- checkout
|
|
11
|
+
- security
|
|
12
|
+
- silent
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'pull_request_target'
|
|
15
|
+
flags: i
|
|
16
|
+
- regex: 'github\.event\.pull_request\.head\.sha'
|
|
17
|
+
flags: i
|
|
18
|
+
- regex: 'github\.sha.*pull_request_target'
|
|
19
|
+
flags: i
|
|
20
|
+
error_messages:
|
|
21
|
+
- "Tests passed but PR code was never executed (checked out base branch at github.sha)"
|
|
22
|
+
root_cause: |
|
|
23
|
+
The pull_request_target event fires in the context of the BASE branch of a pull request,
|
|
24
|
+
not the PR's head branch. Consequently, github.sha inside a pull_request_target workflow
|
|
25
|
+
is the HEAD commit SHA of the BASE branch — typically main or master — not the PR
|
|
26
|
+
contributor's changes.
|
|
27
|
+
|
|
28
|
+
This design is intentional: pull_request_target runs with repository secrets and write
|
|
29
|
+
permissions available, so GitHub restricts it to trusted base-branch code to prevent
|
|
30
|
+
malicious PR code from exfiltrating secrets. However, it causes a silent logic error when
|
|
31
|
+
developers expect github.sha (or the default actions/checkout behaviour, which checks out
|
|
32
|
+
github.sha) to build and test the PR's changes.
|
|
33
|
+
|
|
34
|
+
The result is a CI run that appears to succeed — but it compiled and tested the base
|
|
35
|
+
branch code, not the pull request's changes. The PR author sees green CI even though
|
|
36
|
+
their changes were never executed.
|
|
37
|
+
fix: |
|
|
38
|
+
To work with PR head code in a pull_request_target workflow, explicitly specify the PR
|
|
39
|
+
head SHA using github.event.pull_request.head.sha in the checkout step's ref: input.
|
|
40
|
+
|
|
41
|
+
SECURITY WARNING: checking out untrusted PR code in a workflow that has access to secrets
|
|
42
|
+
creates a pwn-request vulnerability. Only check out PR head code in jobs with minimal
|
|
43
|
+
permissions and no secret exposure.
|
|
44
|
+
|
|
45
|
+
For most CI use cases — building, linting, testing — use the standard pull_request event
|
|
46
|
+
instead of pull_request_target. The pull_request event correctly checks out PR changes
|
|
47
|
+
but runs without repository secrets, which is the safe and intended design.
|
|
48
|
+
fix_code:
|
|
49
|
+
- language: yaml
|
|
50
|
+
label: "Problem: default checkout in pull_request_target checks out base branch"
|
|
51
|
+
code: |
|
|
52
|
+
on: pull_request_target
|
|
53
|
+
|
|
54
|
+
jobs:
|
|
55
|
+
test:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
# No ref specified — uses github.sha which is BASE branch HEAD, not PR code
|
|
60
|
+
- run: npm test # Tests base branch, not the PR changes
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: "Explicit PR head checkout (only when no secrets are exposed)"
|
|
63
|
+
code: |
|
|
64
|
+
on: pull_request_target
|
|
65
|
+
|
|
66
|
+
jobs:
|
|
67
|
+
label-and-check:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
permissions:
|
|
70
|
+
pull-requests: write # Minimal permissions — no secrets
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
with:
|
|
74
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
75
|
+
# Only safe because this job has no secret access
|
|
76
|
+
# Do NOT add secrets: or environment: to jobs that do this
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Recommended: use pull_request for testing, pull_request_target only for privileged operations"
|
|
79
|
+
code: |
|
|
80
|
+
# Standard PR testing — pull_request has no secrets but correctly checks out PR changes
|
|
81
|
+
on: pull_request
|
|
82
|
+
|
|
83
|
+
jobs:
|
|
84
|
+
test:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
# github.sha is the PR merge commit — correct for testing PR changes
|
|
89
|
+
- run: npm test
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
# Separate privileged workflow for labeling/commenting — uses pull_request_target
|
|
93
|
+
# This one works with base-branch context (github.sha = base HEAD) — which is correct
|
|
94
|
+
# for privileged operations that should only run trusted code
|
|
95
|
+
on: pull_request_target
|
|
96
|
+
|
|
97
|
+
jobs:
|
|
98
|
+
label:
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
permissions:
|
|
101
|
+
pull-requests: write
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/labeler@v5
|
|
104
|
+
prevention:
|
|
105
|
+
- "Default to 'pull_request' event for building and testing PR code — it correctly checks out PR changes and has no secrets"
|
|
106
|
+
- "Use 'pull_request_target' only for privileged operations that genuinely need secrets or write permissions (labeling, commenting, deployment approval)"
|
|
107
|
+
- "Never expose repository secrets in the same job that checks out untrusted PR head code via github.event.pull_request.head.sha"
|
|
108
|
+
- "Add a comment next to any pull_request_target checkout step documenting which ref is used and why it is safe"
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target"
|
|
111
|
+
label: "GitHub Docs: pull_request_target event"
|
|
112
|
+
- url: "https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/"
|
|
113
|
+
label: "GitHub Security Lab: Preventing pwn requests"
|
|
114
|
+
- url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions"
|
|
115
|
+
label: "GitHub Docs: Security hardening for GitHub Actions"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
id: yaml-syntax-038
|
|
2
|
+
title: "'::save-state::' and '::get-state::' workflow commands deprecated — upgrade to GITHUB_STATE"
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- save-state
|
|
7
|
+
- get-state
|
|
8
|
+
- deprecated
|
|
9
|
+
- workflow-commands
|
|
10
|
+
- environment-files
|
|
11
|
+
- GITHUB_STATE
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'The `save-state` command is deprecated'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'The `get-state` command is deprecated'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: '::save-state name='
|
|
18
|
+
flags: ''
|
|
19
|
+
- regex: '::get-state name='
|
|
20
|
+
flags: ''
|
|
21
|
+
error_messages:
|
|
22
|
+
- "Warning: The `save-state` command is deprecated and will be disabled soon. Please upgrade to using Environment Files."
|
|
23
|
+
- "Warning: The `get-state` command is deprecated and will be disabled soon. Please upgrade to using Environment Files."
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub Actions originally provided workflow commands ::save-state name=FOO::value and
|
|
26
|
+
::get-state name=FOO:: as a way to persist data between the main body of a step and its
|
|
27
|
+
pre: or post: hooks in JavaScript actions.
|
|
28
|
+
|
|
29
|
+
GitHub deprecated these commands when it introduced environment files (GITHUB_STATE,
|
|
30
|
+
GITHUB_ENV, GITHUB_OUTPUT) as a more robust alternative. Workflows and actions that use
|
|
31
|
+
the old echo "::save-state name=...::value" or echo "::get-state name=...:" syntax now
|
|
32
|
+
produce deprecation warnings in workflow logs.
|
|
33
|
+
|
|
34
|
+
When these commands are eventually disabled, steps using them will silently fail to persist
|
|
35
|
+
state — the post: hook will read an empty string from the state variable, causing subtle
|
|
36
|
+
logic errors. Actions published to the Marketplace are particularly affected because older
|
|
37
|
+
versions used these commands before the deprecation was announced.
|
|
38
|
+
fix: |
|
|
39
|
+
Replace the deprecated workflow commands with the GITHUB_STATE environment file approach:
|
|
40
|
+
- To write state: append "KEY=value" to $GITHUB_STATE (shell) or write to
|
|
41
|
+
process.env['GITHUB_STATE'] (Node.js)
|
|
42
|
+
- To read state in pre:/post: hooks: read the environment variable STATE_<KEY>
|
|
43
|
+
|
|
44
|
+
In shell-based steps the pattern is identical to GITHUB_ENV and GITHUB_OUTPUT.
|
|
45
|
+
In JavaScript actions, upgrade to @actions/core v1.10.0+ and use the saveState() and
|
|
46
|
+
getState() helpers, which internally write to GITHUB_STATE.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: "Deprecated: ::save-state:: and ::get-state:: commands (produces warnings)"
|
|
50
|
+
code: |
|
|
51
|
+
# DEPRECATED — produces warning in logs, will eventually break
|
|
52
|
+
- name: Save state (deprecated)
|
|
53
|
+
run: echo "::save-state name=SERVER_PID::12345"
|
|
54
|
+
|
|
55
|
+
- name: Read state in post step (deprecated)
|
|
56
|
+
run: echo "Saved PID was $(echo "::get-state name=SERVER_PID::")"
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Correct: GITHUB_STATE environment file"
|
|
59
|
+
code: |
|
|
60
|
+
# Write state to GITHUB_STATE file
|
|
61
|
+
- name: Save state
|
|
62
|
+
run: echo "SERVER_PID=12345" >> $GITHUB_STATE
|
|
63
|
+
|
|
64
|
+
# State is automatically available as STATE_<KEY> in later phases
|
|
65
|
+
- name: Read state (available in main, pre, and post phases)
|
|
66
|
+
run: echo "Server PID was $STATE_SERVER_PID"
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: "PowerShell equivalent"
|
|
69
|
+
code: |
|
|
70
|
+
- name: Save state (PowerShell)
|
|
71
|
+
shell: pwsh
|
|
72
|
+
run: |
|
|
73
|
+
"SERVER_PID=12345" | Out-File -FilePath $env:GITHUB_STATE -Append
|
|
74
|
+
|
|
75
|
+
- name: Read state (PowerShell)
|
|
76
|
+
shell: pwsh
|
|
77
|
+
run: |
|
|
78
|
+
Write-Host "Server PID was $env:STATE_SERVER_PID"
|
|
79
|
+
prevention:
|
|
80
|
+
- "Audit actions with pre: or post: hooks — these are the primary consumers of save-state/get-state"
|
|
81
|
+
- "Upgrade to @actions/core v1.10.0+ which uses GITHUB_STATE internally via saveState()/getState()"
|
|
82
|
+
- "Search existing workflows and composite actions for '::save-state' and '::get-state' patterns before they are disabled"
|
|
83
|
+
- "Pin to action versions that have migrated to GITHUB_STATE; check CHANGELOG for migration notes"
|
|
84
|
+
- "The same deprecation cycle affected ::set-output:: (now GITHUB_OUTPUT) and ::set-env:: (now GITHUB_ENV)"
|
|
85
|
+
docs:
|
|
86
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files"
|
|
87
|
+
label: "GitHub Docs: Workflow commands — environment files"
|
|
88
|
+
- url: "https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/"
|
|
89
|
+
label: "GitHub Changelog: Deprecating save-state and set-output commands (Oct 2022)"
|
|
90
|
+
- url: "https://github.com/actions/toolkit/releases/tag/%40actions%2Fcore%401.10.0"
|
|
91
|
+
label: "actions/toolkit: @actions/core v1.10.0 release notes"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
id: yaml-syntax-039
|
|
2
|
+
title: "'type: choice' not supported for workflow_call inputs — validation error when combining with workflow_dispatch"
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_call
|
|
7
|
+
- workflow_dispatch
|
|
8
|
+
- inputs
|
|
9
|
+
- type-choice
|
|
10
|
+
- validation
|
|
11
|
+
- combined-triggers
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Input type .choice. is not supported'
|
|
14
|
+
flags: i
|
|
15
|
+
- regex: 'Unexpected value .choice.'
|
|
16
|
+
flags: i
|
|
17
|
+
- regex: 'Invalid workflow file.*type.*choice'
|
|
18
|
+
flags: i
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Invalid workflow file: Input type 'choice' is not supported for workflow_call triggers. Allowed types: boolean, number, string"
|
|
21
|
+
- "Unexpected value 'choice', expected one of: boolean, number, string"
|
|
22
|
+
- "Invalid value for inputs.*.type: 'choice'"
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub Actions supports different input types depending on the trigger:
|
|
25
|
+
- workflow_dispatch supports: boolean, choice, environment, number, string
|
|
26
|
+
- workflow_call supports only: boolean, number, string
|
|
27
|
+
|
|
28
|
+
When developers combine both triggers in the same workflow file (a common pattern for
|
|
29
|
+
workflows that can be called by other workflows AND dispatched manually), they define a
|
|
30
|
+
shared inputs: block. If those shared inputs use type: choice — valid for workflow_dispatch
|
|
31
|
+
but not workflow_call — the workflow fails schema validation with a cryptic error.
|
|
32
|
+
|
|
33
|
+
The error message does not always clearly indicate which trigger is rejecting the type,
|
|
34
|
+
causing developers to search for choice support across GitHub Actions documentation rather
|
|
35
|
+
than recognising it as a trigger-specific limitation. The same restriction applies to
|
|
36
|
+
type: environment.
|
|
37
|
+
fix: |
|
|
38
|
+
For inputs shared between workflow_dispatch and workflow_call, use type: string and
|
|
39
|
+
document the allowed values in the description field. The GitHub UI renders a free-text
|
|
40
|
+
field for manual dispatch instead of a dropdown, but callers still pass controlled values.
|
|
41
|
+
|
|
42
|
+
For strict input validation, add a shell step at the start of the job that fails if the
|
|
43
|
+
input value is not in the allowed set.
|
|
44
|
+
|
|
45
|
+
If the dropdown UI is essential for manual dispatch, create a thin dispatcher workflow
|
|
46
|
+
(workflow_dispatch only with type: choice) that calls the main reusable workflow passing
|
|
47
|
+
the validated value as a string.
|
|
48
|
+
fix_code:
|
|
49
|
+
- language: yaml
|
|
50
|
+
label: "Problem: type: choice breaks workflow_call validation"
|
|
51
|
+
code: |
|
|
52
|
+
on:
|
|
53
|
+
workflow_dispatch:
|
|
54
|
+
inputs:
|
|
55
|
+
environment:
|
|
56
|
+
type: choice # Valid for workflow_dispatch only
|
|
57
|
+
options: [dev, staging, prod]
|
|
58
|
+
workflow_call:
|
|
59
|
+
inputs:
|
|
60
|
+
environment:
|
|
61
|
+
type: choice # INVALID for workflow_call — causes validation error
|
|
62
|
+
- language: yaml
|
|
63
|
+
label: "Fix: use type: string with documented allowed values"
|
|
64
|
+
code: |
|
|
65
|
+
on:
|
|
66
|
+
workflow_dispatch:
|
|
67
|
+
inputs:
|
|
68
|
+
environment:
|
|
69
|
+
type: string
|
|
70
|
+
description: "Target environment. Allowed values: dev, staging, prod"
|
|
71
|
+
default: dev
|
|
72
|
+
workflow_call:
|
|
73
|
+
inputs:
|
|
74
|
+
environment:
|
|
75
|
+
type: string
|
|
76
|
+
description: "Target environment. Allowed values: dev, staging, prod"
|
|
77
|
+
|
|
78
|
+
jobs:
|
|
79
|
+
deploy:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- name: Validate environment input
|
|
83
|
+
run: |
|
|
84
|
+
case "${{ inputs.environment }}" in
|
|
85
|
+
dev|staging|prod) echo "Environment: ${{ inputs.environment }}" ;;
|
|
86
|
+
*) echo "Invalid environment: ${{ inputs.environment }}"; exit 1 ;;
|
|
87
|
+
esac
|
|
88
|
+
- language: yaml
|
|
89
|
+
label: "Alternative: dispatcher wrapper preserves dropdown UI for manual runs"
|
|
90
|
+
code: |
|
|
91
|
+
# dispatcher.yml — manual dispatch wrapper with choice dropdown
|
|
92
|
+
on:
|
|
93
|
+
workflow_dispatch:
|
|
94
|
+
inputs:
|
|
95
|
+
environment:
|
|
96
|
+
type: choice
|
|
97
|
+
options: [dev, staging, prod]
|
|
98
|
+
|
|
99
|
+
jobs:
|
|
100
|
+
dispatch:
|
|
101
|
+
uses: ./.github/workflows/deploy.yml # calls the reusable workflow
|
|
102
|
+
with:
|
|
103
|
+
environment: ${{ inputs.environment }} # passes as string
|
|
104
|
+
prevention:
|
|
105
|
+
- "workflow_call inputs support only: boolean, number, string — not choice or environment"
|
|
106
|
+
- "Use type: string with a description listing allowed values when an input is shared across both triggers"
|
|
107
|
+
- "Add input validation as the first job step to fail fast when an unexpected value is passed via workflow_call"
|
|
108
|
+
- "Run actionlint locally before pushing — it catches unsupported input types at development time"
|
|
109
|
+
docs:
|
|
110
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_callinputs"
|
|
111
|
+
label: "GitHub Docs: on.workflow_call.inputs syntax"
|
|
112
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_dispatchinputs"
|
|
113
|
+
label: "GitHub Docs: on.workflow_dispatch.inputs syntax"
|
|
114
|
+
- url: "https://rhysd.github.io/actionlint/"
|
|
115
|
+
label: "actionlint: static checker for GitHub Actions workflow files"
|
package/package.json
CHANGED