@htekdev/actions-debugger 1.0.56 → 1.0.57
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-038.yml +95 -0
- package/errors/caching-artifacts/caching-artifacts-039.yml +110 -0
- package/errors/concurrency-timing/concurrency-timing-033.yml +104 -0
- package/errors/concurrency-timing/concurrency-timing-034.yml +123 -0
- package/errors/known-unsolved/known-unsolved-038.yml +124 -0
- package/errors/known-unsolved/known-unsolved-039.yml +102 -0
- package/errors/permissions-auth/permissions-auth-041.yml +110 -0
- package/errors/runner-environment/runner-environment-112.yml +98 -0
- package/errors/runner-environment/runner-environment-113.yml +118 -0
- package/errors/runner-environment/runner-environment-114.yml +130 -0
- package/errors/runner-environment/runner-environment-115.yml +120 -0
- package/errors/runner-environment/runner-environment-116.yml +106 -0
- package/errors/runner-environment/runner-environment-117.yml +109 -0
- package/errors/silent-failures/silent-failures-057.yml +120 -0
- package/errors/silent-failures/silent-failures-058.yml +126 -0
- package/errors/triggers/triggers-041.yml +105 -0
- package/errors/triggers/triggers-042.yml +110 -0
- package/errors/triggers/triggers-043.yml +125 -0
- package/errors/yaml-syntax/yaml-syntax-040.yml +135 -0
- package/errors/yaml-syntax/yaml-syntax-041.yml +147 -0
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
id: runner-environment-116
|
|
2
|
+
title: "actions/setup-python with cache: pip fails when no standard requirements file exists in the repository"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-python
|
|
7
|
+
- pip
|
|
8
|
+
- caching
|
|
9
|
+
- requirements
|
|
10
|
+
- dependency-file
|
|
11
|
+
- cache-dependency-path
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'No file found with the provided path.*requirements'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'No dependencies file path found for pip'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'Couldn''t find a dependency file for pip'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
- regex: 'Error: No file found with the provided path'
|
|
20
|
+
flags: 'i'
|
|
21
|
+
error_messages:
|
|
22
|
+
- "Error: No file found with the provided path: requirements.txt"
|
|
23
|
+
- "No dependencies file path found for pip"
|
|
24
|
+
- "Couldn't find a dependency file for pip"
|
|
25
|
+
- "Error: No file found with the provided path: **/requirements.txt"
|
|
26
|
+
root_cause: |
|
|
27
|
+
When `actions/setup-python` is configured with `cache: 'pip'`, it searches the
|
|
28
|
+
repository for a standard Python dependency file to use as the cache hash key.
|
|
29
|
+
The action looks for these files by default (in order):
|
|
30
|
+
|
|
31
|
+
- `requirements.txt`
|
|
32
|
+
- `requirements/*.txt`
|
|
33
|
+
- `Pipfile.lock`
|
|
34
|
+
- `poetry.lock`
|
|
35
|
+
- `pyproject.toml` (only if it contains a `[project]` or `[tool.poetry]` section)
|
|
36
|
+
- `setup.cfg` (only if it contains `[options]` with `install_requires`)
|
|
37
|
+
|
|
38
|
+
If none of these files are present, the action fails with an error during the
|
|
39
|
+
cache configuration phase. This commonly affects repositories that:
|
|
40
|
+
|
|
41
|
+
- Use a custom requirements filename (e.g., `dev-requirements.txt`, `requirements-dev.txt`)
|
|
42
|
+
- Store requirements in a non-standard path (e.g., `ci/requirements.txt`, `tests/requirements.txt`)
|
|
43
|
+
- Use only `setup.py` for dependency declaration (not recognized by default)
|
|
44
|
+
- Generate requirements dynamically at build time
|
|
45
|
+
- Are library repos with no explicit requirements file (dependencies in `pyproject.toml`
|
|
46
|
+
but without a recognized table)
|
|
47
|
+
|
|
48
|
+
The error appears either immediately at setup time or during the post-step cache save
|
|
49
|
+
phase, depending on the setup-python version.
|
|
50
|
+
fix: |
|
|
51
|
+
Provide the `cache-dependency-path` input to explicitly point to your dependency
|
|
52
|
+
file(s). This input accepts glob patterns and newline-separated paths.
|
|
53
|
+
|
|
54
|
+
If your repository has no dependency files at all (e.g., it generates them
|
|
55
|
+
dynamically), remove `cache: 'pip'` and implement pip caching manually using
|
|
56
|
+
`actions/cache@v4`, using a hash of whatever inputs determine your dependency set
|
|
57
|
+
(e.g., a Makefile, Dockerfile, or script).
|
|
58
|
+
fix_code:
|
|
59
|
+
- language: yaml
|
|
60
|
+
label: "Specify non-standard requirements file path"
|
|
61
|
+
code: |
|
|
62
|
+
- uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: '3.12'
|
|
65
|
+
cache: 'pip'
|
|
66
|
+
cache-dependency-path: ci/requirements.txt # Non-standard path
|
|
67
|
+
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Multiple dependency files with glob pattern"
|
|
70
|
+
code: |
|
|
71
|
+
- uses: actions/setup-python@v5
|
|
72
|
+
with:
|
|
73
|
+
python-version: '3.12'
|
|
74
|
+
cache: 'pip'
|
|
75
|
+
cache-dependency-path: |
|
|
76
|
+
requirements.txt
|
|
77
|
+
requirements-dev.txt
|
|
78
|
+
tests/requirements.txt
|
|
79
|
+
|
|
80
|
+
- language: yaml
|
|
81
|
+
label: "Manual pip caching when no dependency file exists"
|
|
82
|
+
code: |
|
|
83
|
+
- uses: actions/setup-python@v5
|
|
84
|
+
with:
|
|
85
|
+
python-version: '3.12'
|
|
86
|
+
# Omit cache: pip — handle caching manually
|
|
87
|
+
|
|
88
|
+
- name: Cache pip packages
|
|
89
|
+
uses: actions/cache@v4
|
|
90
|
+
with:
|
|
91
|
+
path: ~/.cache/pip
|
|
92
|
+
key: pip-${{ runner.os }}-${{ hashFiles('setup.py', 'Makefile') }}
|
|
93
|
+
restore-keys: |
|
|
94
|
+
pip-${{ runner.os }}-
|
|
95
|
+
prevention:
|
|
96
|
+
- "Always set cache-dependency-path when your requirements file is not named requirements.txt or in the root directory"
|
|
97
|
+
- "Standard filenames recognized automatically: requirements.txt, Pipfile.lock, poetry.lock, pyproject.toml (with [project] table), setup.cfg (with install_requires)"
|
|
98
|
+
- "If using poetry, set cache: 'poetry' instead of cache: 'pip' — it detects poetry.lock automatically"
|
|
99
|
+
- "Consider adding a requirements.txt generated from pyproject.toml or poetry.lock to your repo for compatibility with setup-python caching"
|
|
100
|
+
docs:
|
|
101
|
+
- url: "https://github.com/actions/setup-python#caching-packages-dependencies"
|
|
102
|
+
label: "actions/setup-python — Caching packages documentation"
|
|
103
|
+
- url: "https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages"
|
|
104
|
+
label: "actions/setup-python — Advanced caching usage"
|
|
105
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows"
|
|
106
|
+
label: "GitHub Docs — Caching dependencies to speed up workflows"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
id: runner-environment-117
|
|
2
|
+
title: "aws-actions/configure-aws-credentials@v4 requires explicit aws-region — silent failure after version bump from v1/v2"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- aws
|
|
7
|
+
- configure-aws-credentials
|
|
8
|
+
- aws-region
|
|
9
|
+
- oidc
|
|
10
|
+
- version-upgrade
|
|
11
|
+
- breaking-change
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'Must provide region information'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Input required and not supplied:\s*aws-region'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'Region is not set|No region provided|aws.region.*not.*set'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Must provide region information"
|
|
21
|
+
- "Input required and not supplied: aws-region"
|
|
22
|
+
- "Region is not set"
|
|
23
|
+
- "No region provided"
|
|
24
|
+
root_cause: |
|
|
25
|
+
aws-actions/configure-aws-credentials@v4 (and v2+) made aws-region a required
|
|
26
|
+
input for all authentication methods. In @v1, aws-region was optional and could
|
|
27
|
+
be derived from the AWS_DEFAULT_REGION environment variable already present on
|
|
28
|
+
the runner or set in a prior step.
|
|
29
|
+
|
|
30
|
+
When Dependabot, Renovate, or a manual version bump updates configure-aws-credentials
|
|
31
|
+
from @v1 to @v4, workflows that relied on region auto-detection or inherited
|
|
32
|
+
environment variables begin failing with "Must provide region information" or
|
|
33
|
+
"Input required and not supplied: aws-region".
|
|
34
|
+
|
|
35
|
+
Additional breaking changes between v1 and v4 that affect real workflows:
|
|
36
|
+
|
|
37
|
+
1. aws-region is now required (was optional in v1 when AWS_DEFAULT_REGION was set)
|
|
38
|
+
2. mask-aws-account-id is now always true and cannot be disabled — workflows
|
|
39
|
+
logging the account ID for debugging will see '***' in all log output
|
|
40
|
+
3. OIDC token audience: v4 defaults to 'sts.amazonaws.com'; older IAM OIDC
|
|
41
|
+
trust policies configured for a different audience must be updated
|
|
42
|
+
4. Node.js runtime: updated from Node 16 to Node 20, which may cause issues
|
|
43
|
+
on very old self-hosted runners (Node 20 requires glibc 2.17+)
|
|
44
|
+
5. role-session-name auto-generation format changed — if downstream IAM policies
|
|
45
|
+
or CloudTrail queries match on session name patterns, they may stop matching
|
|
46
|
+
fix: |
|
|
47
|
+
Add an explicit aws-region input to every configure-aws-credentials step.
|
|
48
|
+
|
|
49
|
+
For OIDC-based auth, also verify your IAM trust policy's Condition block is
|
|
50
|
+
compatible with the @v4 defaults (audience: sts.amazonaws.com, subject claim
|
|
51
|
+
format: repo:OWNER/REPO:ref:refs/heads/BRANCH).
|
|
52
|
+
|
|
53
|
+
Store the region in a repository or organization variable (vars.AWS_REGION)
|
|
54
|
+
to avoid hardcoding the same region string across multiple workflow files.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "configure-aws-credentials@v4 with required aws-region (OIDC)"
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
deploy:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
permissions:
|
|
63
|
+
id-token: write # Required for OIDC
|
|
64
|
+
contents: read
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
|
|
68
|
+
- name: Configure AWS credentials
|
|
69
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
70
|
+
with:
|
|
71
|
+
aws-region: us-east-1 # Required in v4 (was optional in v1)
|
|
72
|
+
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
|
|
73
|
+
role-session-name: GitHubActionsSession
|
|
74
|
+
|
|
75
|
+
- name: Deploy
|
|
76
|
+
run: aws s3 sync dist/ s3://${{ vars.S3_BUCKET }}/
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "configure-aws-credentials@v4 with static key auth"
|
|
79
|
+
code: |
|
|
80
|
+
jobs:
|
|
81
|
+
deploy:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/checkout@v4
|
|
85
|
+
|
|
86
|
+
- name: Configure AWS credentials (static keys)
|
|
87
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
88
|
+
with:
|
|
89
|
+
aws-region: ${{ vars.AWS_REGION }} # Use variable, not hardcoded
|
|
90
|
+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
91
|
+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
92
|
+
|
|
93
|
+
- name: Deploy
|
|
94
|
+
run: aws s3 sync dist/ s3://${{ vars.S3_BUCKET }}/
|
|
95
|
+
prevention:
|
|
96
|
+
- "Always specify aws-region explicitly in every configure-aws-credentials step — never rely on AWS_DEFAULT_REGION"
|
|
97
|
+
- "Store the AWS region in a repository variable (vars.AWS_REGION) to keep it consistent and easy to change"
|
|
98
|
+
- "When Dependabot bumps configure-aws-credentials to a new major version, review the release notes and test in a non-production environment first"
|
|
99
|
+
- "After bumping to v4, validate your IAM OIDC trust policy: the audience should be 'sts.amazonaws.com' and the subject claim format should match repo:OWNER/REPO:ref:refs/..."
|
|
100
|
+
- "Use actionlint locally to catch missing required inputs before committing workflow files"
|
|
101
|
+
docs:
|
|
102
|
+
- url: "https://github.com/aws-actions/configure-aws-credentials"
|
|
103
|
+
label: "aws-actions/configure-aws-credentials — README and migration guide"
|
|
104
|
+
- url: "https://github.com/aws-actions/configure-aws-credentials/releases"
|
|
105
|
+
label: "aws-actions/configure-aws-credentials — Release notes (v4 breaking changes)"
|
|
106
|
+
- url: "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html"
|
|
107
|
+
label: "AWS Docs — Creating OpenID Connect identity providers"
|
|
108
|
+
- url: "https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"
|
|
109
|
+
label: "GitHub Docs — Configuring OIDC in Amazon Web Services"
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
id: silent-failures-057
|
|
2
|
+
title: "needs.<job>.outputs empty when upstream job failed — if: always() downstream job silently receives empty strings"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- needs
|
|
7
|
+
- job-outputs
|
|
8
|
+
- always
|
|
9
|
+
- failure
|
|
10
|
+
- silent-failure
|
|
11
|
+
- job-dependencies
|
|
12
|
+
- error-handling
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'needs\.\w+\.outputs\.\w+'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'if.*always.*needs.*result.*failure'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages: []
|
|
19
|
+
root_cause: |
|
|
20
|
+
GitHub Actions job outputs are only populated when the upstream job completes successfully
|
|
21
|
+
(exit code 0). If an upstream job fails, its outputs are never set in the workflow context
|
|
22
|
+
— even if the job partially executed and wrote to $GITHUB_OUTPUT before the failure.
|
|
23
|
+
|
|
24
|
+
When a downstream job uses `if: always()` or `if: needs.upstream.result == 'failure'`
|
|
25
|
+
to run after a failed upstream job, all `needs.<upstream>.outputs.*` values resolve to
|
|
26
|
+
empty strings. No warning is emitted in the logs. The downstream job runs without any
|
|
27
|
+
indication that it received empty output values instead of the expected data.
|
|
28
|
+
|
|
29
|
+
This silently causes:
|
|
30
|
+
- Notification steps sending messages with blank context (empty SHA, branch, artifact path)
|
|
31
|
+
- Conditional steps skipping because a non-empty string check evaluates to false
|
|
32
|
+
- Upload or deploy steps using empty or incorrectly-derived paths
|
|
33
|
+
- Downstream matrix jobs running with no dimension values
|
|
34
|
+
|
|
35
|
+
The pattern is valid YAML — `needs.upstream.outputs.my_value` passes schema validation.
|
|
36
|
+
The failure only manifests at runtime after the upstream job has failed, and the symptoms
|
|
37
|
+
look unrelated to the empty output (e.g., "artifact not found" rather than "output was empty").
|
|
38
|
+
fix: |
|
|
39
|
+
Option 1 (recommended): Set outputs as early as possible in the upstream job, before any
|
|
40
|
+
step that might fail. Use a dedicated first step to write context values to $GITHUB_OUTPUT
|
|
41
|
+
before running builds or tests that could fail.
|
|
42
|
+
|
|
43
|
+
Option 2: Add `|| 'fallback'` expressions to all `needs.*.outputs.*` references in
|
|
44
|
+
if:always() downstream jobs to make empty values visible:
|
|
45
|
+
env:
|
|
46
|
+
ARTIFACT_PATH: ${{ needs.build.outputs.artifact_path || 'unknown' }}
|
|
47
|
+
|
|
48
|
+
Option 3: In notification/cleanup jobs, use `github.*` context values directly rather
|
|
49
|
+
than relying on upstream job outputs for data that is always available:
|
|
50
|
+
github.sha, github.ref_name, github.actor, github.run_id
|
|
51
|
+
|
|
52
|
+
Option 4: Use a `result` check before accessing outputs and provide explicit fallbacks
|
|
53
|
+
when result is not 'success':
|
|
54
|
+
${{ needs.build.result == 'success' && needs.build.outputs.artifact || 'build-failed' }}
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Set outputs in a first step before any step that might fail"
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
build:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
outputs:
|
|
63
|
+
artifact_path: ${{ steps.init.outputs.artifact_path }}
|
|
64
|
+
commit_sha: ${{ steps.init.outputs.commit_sha }}
|
|
65
|
+
steps:
|
|
66
|
+
# Set outputs FIRST before any step that might fail
|
|
67
|
+
- name: Initialize outputs
|
|
68
|
+
id: init
|
|
69
|
+
run: |
|
|
70
|
+
echo "artifact_path=dist/" >> $GITHUB_OUTPUT
|
|
71
|
+
echo "commit_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
72
|
+
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
|
|
75
|
+
- name: Build (may fail)
|
|
76
|
+
run: npm run build
|
|
77
|
+
|
|
78
|
+
notify:
|
|
79
|
+
needs: build
|
|
80
|
+
if: always()
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
steps:
|
|
83
|
+
- name: Send build notification
|
|
84
|
+
run: |
|
|
85
|
+
RESULT="${{ needs.build.result }}"
|
|
86
|
+
# Use || fallback since outputs are empty when build failed
|
|
87
|
+
ARTIFACT="${{ needs.build.outputs.artifact_path || 'N/A' }}"
|
|
88
|
+
SHA="${{ needs.build.outputs.commit_sha || github.sha }}"
|
|
89
|
+
echo "Build: $RESULT | Artifact: $ARTIFACT | SHA: $SHA"
|
|
90
|
+
- language: yaml
|
|
91
|
+
label: "Use github context directly in failure-handling jobs instead of upstream outputs"
|
|
92
|
+
code: |
|
|
93
|
+
jobs:
|
|
94
|
+
notify-on-failure:
|
|
95
|
+
needs: [build, test]
|
|
96
|
+
if: failure()
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
steps:
|
|
99
|
+
- name: Notify failure
|
|
100
|
+
run: |
|
|
101
|
+
# Use github context — always available regardless of upstream failure
|
|
102
|
+
echo "Repository: ${{ github.repository }}"
|
|
103
|
+
echo "Branch: ${{ github.ref_name }}"
|
|
104
|
+
echo "Commit: ${{ github.sha }}"
|
|
105
|
+
echo "Actor: ${{ github.actor }}"
|
|
106
|
+
echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
107
|
+
prevention:
|
|
108
|
+
- "Write all critical values to GITHUB_OUTPUT in the very first step of a job, before any step that might fail"
|
|
109
|
+
- "Add || 'fallback' to every needs.*.outputs.* expression in if:always() or if:failure() downstream jobs"
|
|
110
|
+
- "Use github.* context values for data always available (sha, ref_name, actor, run_id) rather than upstream outputs"
|
|
111
|
+
- "Test failure paths by adding a workflow_dispatch that intentionally fails the upstream job and verify downstream output"
|
|
112
|
+
docs:
|
|
113
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idoutputs"
|
|
114
|
+
label: "GitHub Docs — Job outputs"
|
|
115
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#needs-context"
|
|
116
|
+
label: "GitHub Docs — needs context"
|
|
117
|
+
- url: "https://github.com/orgs/community/discussions/18163"
|
|
118
|
+
label: "GitHub Community — needs outputs empty when job failed"
|
|
119
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idif"
|
|
120
|
+
label: "GitHub Docs — if conditions with always()"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
id: silent-failures-058
|
|
2
|
+
title: "workflow_dispatch type:choice inputs bypass validation via REST API — arbitrary strings accepted silently"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_dispatch
|
|
7
|
+
- inputs
|
|
8
|
+
- choice
|
|
9
|
+
- rest-api
|
|
10
|
+
- validation
|
|
11
|
+
- type-checking
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'type:\s*choice'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'workflow.*dispatch.*inputs.*choice'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "type: choice"
|
|
19
|
+
root_cause: |
|
|
20
|
+
The workflow_dispatch event supports an input type of 'choice' which presents
|
|
21
|
+
a dropdown menu in the GitHub UI, restricting the user to a predefined list of
|
|
22
|
+
valid values. However, this validation is enforced only in the GitHub web UI —
|
|
23
|
+
it is NOT enforced by the GitHub Actions API.
|
|
24
|
+
|
|
25
|
+
When a workflow is triggered via the REST API (POST /repos/{owner}/{repo}/actions/
|
|
26
|
+
workflows/{workflow_id}/dispatches), any string value can be supplied for a
|
|
27
|
+
choice-type input, including values not in the defined options list. The workflow
|
|
28
|
+
accepts and runs with the unsanitized value without error or warning.
|
|
29
|
+
|
|
30
|
+
This means:
|
|
31
|
+
- CI/CD scripts calling workflow_dispatch via REST API can accidentally pass
|
|
32
|
+
the wrong environment name (typos, case differences, wrong string)
|
|
33
|
+
- Automation systems that construct the dispatch payload dynamically may pass
|
|
34
|
+
a value that was valid at construction time but is no longer a valid choice
|
|
35
|
+
- Malicious actors with repository write access can trigger unexpected code paths
|
|
36
|
+
by dispatching with unexpected values
|
|
37
|
+
|
|
38
|
+
Steps using 'if: inputs.environment == "production"' may silently skip or
|
|
39
|
+
silently run depending on whether the API-supplied string matches exactly.
|
|
40
|
+
No error is raised — the workflow just behaves unexpectedly.
|
|
41
|
+
|
|
42
|
+
Example: inputs.environment options are ['dev', 'staging', 'production'] but
|
|
43
|
+
API call passes 'prod' — all condition checks against 'production' silently
|
|
44
|
+
fail, causing the deployment step to be skipped with no indication of why.
|
|
45
|
+
fix: |
|
|
46
|
+
Explicitly validate choice-type inputs at the start of the workflow using a
|
|
47
|
+
validation step that fails fast with a clear error message when an invalid
|
|
48
|
+
value is supplied.
|
|
49
|
+
|
|
50
|
+
For critical path decisions (environment selection, deployment targets), use
|
|
51
|
+
a validation step before any consequential operations:
|
|
52
|
+
|
|
53
|
+
- name: Validate environment input
|
|
54
|
+
run: |
|
|
55
|
+
case "${{ inputs.environment }}" in
|
|
56
|
+
dev|staging|production) ;;
|
|
57
|
+
*) echo "Invalid environment: ${{ inputs.environment }}" && exit 1 ;;
|
|
58
|
+
esac
|
|
59
|
+
|
|
60
|
+
This approach works regardless of how the workflow was triggered (UI or API)
|
|
61
|
+
and produces a clear failure message instead of a silent wrong-path execution.
|
|
62
|
+
fix_code:
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "Validate choice input before using it — fails fast with clear error"
|
|
65
|
+
code: |
|
|
66
|
+
on:
|
|
67
|
+
workflow_dispatch:
|
|
68
|
+
inputs:
|
|
69
|
+
environment:
|
|
70
|
+
description: 'Target environment'
|
|
71
|
+
type: choice
|
|
72
|
+
required: true
|
|
73
|
+
options:
|
|
74
|
+
- dev
|
|
75
|
+
- staging
|
|
76
|
+
- production
|
|
77
|
+
|
|
78
|
+
jobs:
|
|
79
|
+
deploy:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- name: Validate environment input
|
|
83
|
+
run: |
|
|
84
|
+
valid_envs="dev staging production"
|
|
85
|
+
if ! echo "$valid_envs" | grep -qw "${{ inputs.environment }}"; then
|
|
86
|
+
echo "ERROR: Invalid environment '${{ inputs.environment }}'"
|
|
87
|
+
echo "Valid options: $valid_envs"
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
- name: Deploy to ${{ inputs.environment }}
|
|
92
|
+
run: echo "Deploying to ${{ inputs.environment }}"
|
|
93
|
+
- language: yaml
|
|
94
|
+
label: "Use environment-scoped deployment for automatic protection"
|
|
95
|
+
code: |
|
|
96
|
+
on:
|
|
97
|
+
workflow_dispatch:
|
|
98
|
+
inputs:
|
|
99
|
+
environment:
|
|
100
|
+
description: 'Target environment'
|
|
101
|
+
type: choice
|
|
102
|
+
required: true
|
|
103
|
+
options:
|
|
104
|
+
- dev
|
|
105
|
+
- staging
|
|
106
|
+
- production
|
|
107
|
+
|
|
108
|
+
jobs:
|
|
109
|
+
deploy:
|
|
110
|
+
runs-on: ubuntu-latest
|
|
111
|
+
environment: ${{ inputs.environment }} # Invalid names cause job failure
|
|
112
|
+
steps:
|
|
113
|
+
- name: Deploy
|
|
114
|
+
run: echo "Deploying to ${{ inputs.environment }}"
|
|
115
|
+
prevention:
|
|
116
|
+
- "Never rely solely on type:choice validation — always add an explicit validation step for inputs that affect deployment targets or security-sensitive code paths"
|
|
117
|
+
- "Use environment: ${{ inputs.environment }} on jobs to leverage GitHub's environment protection rules as a secondary validation layer"
|
|
118
|
+
- "Document that API dispatch bypasses choice validation in your workflow's comments so future maintainers know validation is needed"
|
|
119
|
+
- "In automation scripts that call workflow_dispatch via API, validate the environment string against the allowed list before making the API call"
|
|
120
|
+
docs:
|
|
121
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch"
|
|
122
|
+
label: "GitHub Docs — workflow_dispatch inputs"
|
|
123
|
+
- url: "https://docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event"
|
|
124
|
+
label: "GitHub REST API — Create a workflow dispatch event"
|
|
125
|
+
- url: "https://stackoverflow.com/questions/69578241/github-actions-workflow-dispatch-with-choices"
|
|
126
|
+
label: "Stack Overflow — workflow_dispatch with choice inputs (highly voted)"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
id: triggers-041
|
|
2
|
+
title: "on.schedule workflow only runs from default branch — schedule changes in feature branches never fire"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- cron
|
|
8
|
+
- default-branch
|
|
9
|
+
- triggers
|
|
10
|
+
- known-limitation
|
|
11
|
+
- testing
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'schedule.*not.*trigger'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'cron.*not.*running.*branch'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'workflow.*not.*present.*default.*branch'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "This workflow has a workflow_dispatch event trigger, however a workflow_dispatch event workflow run cannot be created as this workflow is not present in the default branch."
|
|
21
|
+
root_cause: |
|
|
22
|
+
GitHub Actions `on.schedule` workflows only execute from the repository's default branch
|
|
23
|
+
(typically `main` or `master`). No matter what branch the workflow file is pushed to, the
|
|
24
|
+
schedule trigger always runs the version of the workflow file on the default branch.
|
|
25
|
+
|
|
26
|
+
This creates a silent testing trap: developers modify a cron-based workflow in a feature
|
|
27
|
+
branch, push it, and wait for the scheduled time. The schedule fires correctly — but runs
|
|
28
|
+
the OLD workflow definition from the default branch, not the feature branch changes. The
|
|
29
|
+
developer sees no behavior change and cannot validate their modifications until after merging.
|
|
30
|
+
|
|
31
|
+
There is no GitHub UI indication that a scheduled workflow is "pending for a branch". All
|
|
32
|
+
scheduled runs appear under the default branch in the Actions tab regardless of which branch
|
|
33
|
+
triggered or modified them.
|
|
34
|
+
|
|
35
|
+
This limitation applies exclusively to `on.schedule`. Other triggers (`on.push`,
|
|
36
|
+
`on.pull_request`, `on.workflow_dispatch`) work correctly from any branch that contains
|
|
37
|
+
the workflow file.
|
|
38
|
+
fix: |
|
|
39
|
+
There is no way to run `on.schedule` from a feature branch. Use these testing alternatives:
|
|
40
|
+
|
|
41
|
+
Option 1 (recommended): Add `on.workflow_dispatch` to the same workflow alongside
|
|
42
|
+
`on.schedule`. Use the GitHub UI or CLI to manually trigger runs from your feature branch
|
|
43
|
+
to validate changes before merging.
|
|
44
|
+
|
|
45
|
+
Option 2: Add `on.push` with a specific branch filter during development. Push triggers
|
|
46
|
+
fire from any branch and will use the feature branch's workflow definition.
|
|
47
|
+
|
|
48
|
+
Option 3: Use `act` (nektos/act) locally to simulate scheduled runs before pushing.
|
|
49
|
+
|
|
50
|
+
Option 4: Merge to a short-lived integration/staging branch that serves as a temporary
|
|
51
|
+
"default" for validation, then merge to main.
|
|
52
|
+
|
|
53
|
+
After validation, merge to the default branch — the schedule will automatically use the
|
|
54
|
+
updated workflow definition from that point forward.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Add workflow_dispatch alongside schedule to enable branch-based testing"
|
|
58
|
+
code: |
|
|
59
|
+
on:
|
|
60
|
+
# Production: runs on schedule from default branch only
|
|
61
|
+
schedule:
|
|
62
|
+
- cron: '0 6 * * 1' # Every Monday at 6 AM UTC
|
|
63
|
+
|
|
64
|
+
# Development: add this to test changes from any branch manually
|
|
65
|
+
workflow_dispatch:
|
|
66
|
+
|
|
67
|
+
jobs:
|
|
68
|
+
scheduled-job:
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- name: Run scheduled task
|
|
73
|
+
run: ./scripts/weekly-task.sh
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: "Add on.push temporarily during development to fire from feature branch"
|
|
76
|
+
code: |
|
|
77
|
+
on:
|
|
78
|
+
schedule:
|
|
79
|
+
- cron: '0 6 * * 1'
|
|
80
|
+
|
|
81
|
+
# TEMPORARY: Remove before merging — used to test schedule logic from branch
|
|
82
|
+
push:
|
|
83
|
+
branches: ['feature/update-schedule-workflow']
|
|
84
|
+
|
|
85
|
+
jobs:
|
|
86
|
+
scheduled-job:
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
steps:
|
|
89
|
+
- uses: actions/checkout@v4
|
|
90
|
+
- name: Run scheduled task
|
|
91
|
+
run: ./scripts/weekly-task.sh
|
|
92
|
+
prevention:
|
|
93
|
+
- "Always add workflow_dispatch to workflows that primarily use on.schedule — it enables branch-level testing at any time"
|
|
94
|
+
- "Use act (nektos/act) locally to simulate scheduled workflow runs during development"
|
|
95
|
+
- "Add a comment in the workflow file noting that schedule: only fires from the default branch"
|
|
96
|
+
- "Treat schedule-only workflows as default-branch-only code — validate in staging branches before merging"
|
|
97
|
+
docs:
|
|
98
|
+
- url: "https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule"
|
|
99
|
+
label: "GitHub Docs — schedule event"
|
|
100
|
+
- url: "https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch"
|
|
101
|
+
label: "GitHub Docs — workflow_dispatch event (for manual testing)"
|
|
102
|
+
- url: "https://github.com/nektos/act"
|
|
103
|
+
label: "nektos/act — Run Actions locally"
|
|
104
|
+
- url: "https://github.com/orgs/community/discussions/21536"
|
|
105
|
+
label: "GitHub Community — Scheduled workflow only runs on default branch"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
id: triggers-042
|
|
2
|
+
title: "workflow_run trigger only activates when the listener workflow file exists on the default branch"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_run
|
|
7
|
+
- default-branch
|
|
8
|
+
- trigger
|
|
9
|
+
- silent-failure
|
|
10
|
+
- feature-branch
|
|
11
|
+
- testing
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on:\s*[\r\n]+\s*workflow_run'
|
|
14
|
+
flags: 'is'
|
|
15
|
+
- regex: 'workflow_run.*workflows.*completed'
|
|
16
|
+
flags: 'is'
|
|
17
|
+
error_messages: []
|
|
18
|
+
root_cause: |
|
|
19
|
+
GitHub only reads the `on: workflow_run:` trigger definition from workflow files
|
|
20
|
+
that exist on the **default branch** of the repository. If a workflow file
|
|
21
|
+
containing `on: workflow_run:` exists only on a feature branch, pull request branch,
|
|
22
|
+
or any non-default branch, the trigger is silently ignored — no error is reported,
|
|
23
|
+
the workflow file is valid, and GitHub simply never fires the workflow.
|
|
24
|
+
|
|
25
|
+
This means that changes to a `workflow_run` listener workflow cannot be exercised
|
|
26
|
+
on a branch before merging to the default branch. A brand-new `workflow_run` listener
|
|
27
|
+
added on a feature branch will not activate even when the triggering workflow
|
|
28
|
+
(listed under `workflows:`) completes successfully.
|
|
29
|
+
|
|
30
|
+
This behavior is consistent with other event-driven triggers that GitHub reads only
|
|
31
|
+
from the default branch:
|
|
32
|
+
- `on: schedule` — cron schedule (documented, see entry triggers-041)
|
|
33
|
+
- `on: workflow_dispatch` — manual trigger (known-unsolved entry covers UI visibility)
|
|
34
|
+
- `on: workflow_run` — this entry
|
|
35
|
+
|
|
36
|
+
The distinction matters most for `workflow_run` because developers often add a
|
|
37
|
+
`workflow_run` listener to automate post-CI steps (deploy, notifications, releases)
|
|
38
|
+
and naturally develop and test the listener on a branch before merging it.
|
|
39
|
+
|
|
40
|
+
Note: The `branches:` filter inside `on: workflow_run:` controls WHICH branches the
|
|
41
|
+
triggering workflow must run on — this is separate from where the listener workflow
|
|
42
|
+
itself must reside (always the default branch).
|
|
43
|
+
fix: |
|
|
44
|
+
Merge the workflow file containing `on: workflow_run:` to the default branch before
|
|
45
|
+
expecting it to activate. To test the workflow logic before merging:
|
|
46
|
+
|
|
47
|
+
1. Add a temporary `on: workflow_dispatch:` trigger alongside `on: workflow_run:`.
|
|
48
|
+
This allows manual test runs on any branch after the file lands on the default branch.
|
|
49
|
+
2. Use `act` locally to simulate `workflow_run` events before pushing.
|
|
50
|
+
3. Test using a throw-away default branch in a fork during development.
|
|
51
|
+
|
|
52
|
+
Once the file is on the default branch, the `workflow_run` trigger activates
|
|
53
|
+
immediately for all future triggering workflow completions.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: "Add workflow_dispatch alongside workflow_run for pre-merge testing"
|
|
57
|
+
code: |
|
|
58
|
+
name: Post-CI Deploy
|
|
59
|
+
|
|
60
|
+
on:
|
|
61
|
+
# Temporary: enables manual test runs after this file merges to default branch
|
|
62
|
+
workflow_dispatch:
|
|
63
|
+
|
|
64
|
+
# Primary trigger: only active after this file is on the default branch
|
|
65
|
+
workflow_run:
|
|
66
|
+
workflows: ["CI"] # Must match the exact workflow name: field value
|
|
67
|
+
types: [completed]
|
|
68
|
+
branches: [main] # Only triggers when CI ran on these branches
|
|
69
|
+
|
|
70
|
+
jobs:
|
|
71
|
+
deploy:
|
|
72
|
+
# Only deploy when CI actually passed
|
|
73
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- run: echo "Deploying..."
|
|
78
|
+
|
|
79
|
+
- language: yaml
|
|
80
|
+
label: "Minimal workflow_run listener (file must be on default branch)"
|
|
81
|
+
code: |
|
|
82
|
+
name: Notify on CI Complete
|
|
83
|
+
|
|
84
|
+
on:
|
|
85
|
+
workflow_run:
|
|
86
|
+
workflows: ["Build and Test"] # Exact name from the other workflow's 'name:' field
|
|
87
|
+
types: [completed]
|
|
88
|
+
|
|
89
|
+
jobs:
|
|
90
|
+
notify:
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
steps:
|
|
93
|
+
- name: Report conclusion
|
|
94
|
+
run: |
|
|
95
|
+
echo "Triggered by: ${{ github.event.workflow_run.name }}"
|
|
96
|
+
echo "Conclusion: ${{ github.event.workflow_run.conclusion }}"
|
|
97
|
+
echo "Head branch: ${{ github.event.workflow_run.head_branch }}"
|
|
98
|
+
prevention:
|
|
99
|
+
- "Merge workflow files with on: workflow_run: to the default branch before expecting them to fire — there is no way to test this trigger on a feature branch"
|
|
100
|
+
- "Add on: workflow_dispatch: temporarily for manual testing after the file lands on the default branch"
|
|
101
|
+
- "Use act (https://github.com/nektos/act) locally to simulate workflow_run events during development"
|
|
102
|
+
- "Document in team runbooks that workflow_run, schedule, and workflow_dispatch triggers require the workflow file on the default branch"
|
|
103
|
+
- "Verify the workflows: list uses the exact value of the triggering workflow's name: field — a mismatch causes the same silent non-firing behavior"
|
|
104
|
+
docs:
|
|
105
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run"
|
|
106
|
+
label: "GitHub Docs — workflow_run trigger"
|
|
107
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#triggering-a-workflow-from-a-workflow"
|
|
108
|
+
label: "GitHub Docs — Triggering a workflow from a workflow"
|
|
109
|
+
- url: "https://github.com/nektos/act"
|
|
110
|
+
label: "act — Run GitHub Actions locally for testing"
|