@htekdev/actions-debugger 1.0.14 → 1.0.16
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/dist/db/search.js +3 -1
- package/dist/db/search.js.map +1 -1
- package/dist/tools/suggest-fix.d.ts.map +1 -1
- package/dist/tools/suggest-fix.js +5 -1
- package/dist/tools/suggest-fix.js.map +1 -1
- package/errors/caching-artifacts/cache-key-too-long.yml +93 -0
- package/errors/caching-artifacts/cache-path-not-exist-skipped.yml +152 -0
- package/errors/caching-artifacts/cache-save-same-key-html-conflict.yml +109 -0
- package/errors/caching-artifacts/docker-buildx-gha-cache-capacity.yml +107 -0
- package/errors/caching-artifacts/setup-ruby-bundler-ephemeral-workdir-cache-miss.yml +147 -0
- package/errors/caching-artifacts/upload-artifact-v3-retirement-blocked.yml +123 -0
- package/errors/caching-artifacts/upload-artifact-v4-large-file-macos-hang.yml +111 -0
- package/errors/concurrency-timing/always-cleanup-5min-forced-kill.yml +140 -0
- package/errors/concurrency-timing/concurrency-group-env-context-undefined.yml +99 -0
- package/errors/concurrency-timing/required-check-pending-path-filter-skip.yml +160 -0
- package/errors/concurrency-timing/wait-timer-cancel-in-progress-starvation.yml +125 -0
- package/errors/known-unsolved/composite-action-step-timeout-minutes-ignored.yml +146 -0
- package/errors/known-unsolved/reusable-workflow-no-composite-action-call.yml +116 -0
- package/errors/known-unsolved/schedule-trigger-default-branch-only.yml +113 -0
- package/errors/known-unsolved/secrets-not-allowed-in-if-conditions.yml +149 -0
- package/errors/known-unsolved/workflow-50-rerun-limit.yml +110 -0
- package/errors/permissions-auth/check-run-status-modification-blocked.yml +134 -0
- package/errors/permissions-auth/dependabot-pr-secrets-unavailable.yml +133 -0
- package/errors/permissions-auth/fine-grained-pat-deployment-write-required.yml +146 -0
- package/errors/permissions-auth/github-app-installation-token-new-format.yml +124 -0
- package/errors/permissions-auth/github-packages-read-requires-packages-permission.yml +128 -0
- package/errors/permissions-auth/oidc-id-token-write-permission-missing.yml +169 -0
- package/errors/permissions-auth/permissions-empty-block-removes-contents-read.yml +97 -0
- package/errors/permissions-auth/reusable-workflow-permissions-not-inherited.yml +114 -0
- package/errors/runner-environment/checkout-windows-ebusy-lock.yml +124 -0
- package/errors/runner-environment/deprecated-action-version-auto-rejected.yml +89 -0
- package/errors/runner-environment/github-hosted-runner-disk-space-full.yml +85 -0
- package/errors/runner-environment/github-path-same-step-not-found.yml +114 -0
- package/errors/runner-environment/github-script-v6-octokit-rest-actions-not-function.yml +87 -0
- package/errors/runner-environment/macos-13-deprecation-brownout.yml +93 -0
- package/errors/runner-environment/macos-15-mono-nuget-removed.yml +151 -0
- package/errors/runner-environment/macos-15-xcode-simulator-sdk-policy.yml +141 -0
- package/errors/runner-environment/multi-runtime-nov2025-removal.yml +120 -0
- package/errors/runner-environment/runner-oom-exit-code-137.yml +117 -0
- package/errors/runner-environment/setup-go-go123-telemetry-cache-failure.yml +92 -0
- package/errors/runner-environment/setup-java-distribution-required.yml +108 -0
- package/errors/runner-environment/ubuntu-2004-retirement-brownout.yml +107 -0
- package/errors/runner-environment/windows-latest-d-drive-removed.yml +104 -0
- package/errors/runner-environment/windows-vs2026-cuda-host-compiler-unsupported.yml +145 -0
- package/errors/silent-failures/event-commits-empty-on-workflow-dispatch.yml +110 -0
- package/errors/silent-failures/fetch-tags-depth-one-silent-no-op.yml +77 -0
- package/errors/silent-failures/github-env-multiline-value-truncated.yml +127 -0
- package/errors/silent-failures/github-sha-pr-merge-commit-not-head.yml +150 -0
- package/errors/silent-failures/job-output-masked-as-secret-empty.yml +147 -0
- package/errors/silent-failures/upload-artifact-permissions-stripped.yml +98 -0
- package/errors/triggers/pull-request-branches-filter-matches-base-not-head.yml +140 -0
- package/errors/triggers/push-event-fires-on-branch-delete.yml +129 -0
- package/errors/triggers/push-first-commit-before-sha-zeros.yml +160 -0
- package/errors/yaml-syntax/continue-on-error-env-context-rejected.yml +130 -0
- package/errors/yaml-syntax/fromjson-empty-string-crash.yml +99 -0
- package/errors/yaml-syntax/if-bang-negation-yaml-tag.yml +145 -0
- package/errors/yaml-syntax/local-action-path-always-top-level.yml +142 -0
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
id: concurrency-timing-013
|
|
2
|
+
title: "Required Status Check Stuck in Pending When Workflow Skipped by Path or Branch Filter"
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- required-status-check
|
|
7
|
+
- path-filter
|
|
8
|
+
- branch-filter
|
|
9
|
+
- pending
|
|
10
|
+
- pull-request
|
|
11
|
+
- branch-protection
|
|
12
|
+
- paths
|
|
13
|
+
- blocked-pr
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: "Some checks haven't completed yet|Required status check.*pending"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "Waiting for status:.*pending"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Some checks haven't completed yet"
|
|
21
|
+
- "Required status check is pending"
|
|
22
|
+
- "Waiting for status: CI / test (pending)"
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub Actions workflows that use `paths:`, `paths-ignore:`, `branches:`, or
|
|
25
|
+
`branches-ignore:` filters will NOT run — and will NOT report ANY status — for
|
|
26
|
+
commits that don't match the filter criteria.
|
|
27
|
+
|
|
28
|
+
When a required status check is configured in a branch protection rule and the
|
|
29
|
+
workflow providing that check is skipped by a filter:
|
|
30
|
+
- The check is NEVER created for that commit — it remains in "Pending" state indefinitely
|
|
31
|
+
- The PR is blocked from merging with "Some checks haven't completed yet"
|
|
32
|
+
- The check CANNOT be manually re-triggered without pushing a commit that matches the filter
|
|
33
|
+
|
|
34
|
+
Common scenario:
|
|
35
|
+
A repository has a `ci.yml` workflow with `paths: ['src/**', '*.ts']` and
|
|
36
|
+
`CI / test` configured as a required status check. A developer opens a PR that only
|
|
37
|
+
changes `README.md` or `.github/docs/`. The `CI / test` check never runs, shows as
|
|
38
|
+
"Pending" forever, and the PR is permanently blocked from merging without an admin
|
|
39
|
+
override or a dummy code commit to trigger the workflow.
|
|
40
|
+
|
|
41
|
+
This is explicitly documented behavior but frequently misunderstood:
|
|
42
|
+
- The workflow appears to work correctly for code-change PRs (the common case)
|
|
43
|
+
- The bug only surfaces on documentation-only, config-only, or administrative PRs
|
|
44
|
+
- Developers and reviewers see a pending check with no way to trigger it
|
|
45
|
+
|
|
46
|
+
Note: This is distinct from skipped-needs-cascade (job dependency skipping) — this
|
|
47
|
+
is specifically about the WORKFLOW TRIGGER filter preventing the run from ever starting,
|
|
48
|
+
so no job status is reported at all.
|
|
49
|
+
|
|
50
|
+
Source: GitHub Docs — Troubleshooting required status checks: "If a workflow is skipped
|
|
51
|
+
due to path filtering, branch filtering or a commit message, then checks associated
|
|
52
|
+
with that workflow will remain in a 'Pending' state. A pull request that requires those
|
|
53
|
+
checks to be successful will be blocked from merging."
|
|
54
|
+
fix: |
|
|
55
|
+
Two main approaches:
|
|
56
|
+
|
|
57
|
+
1. **Always-succeeding bypass job** — remove path filters from the workflow trigger,
|
|
58
|
+
run the workflow for all PRs, use `dorny/paths-filter` or `tj-actions/changed-files`
|
|
59
|
+
to detect changes inside the workflow, and add a sentinel job that always produces a
|
|
60
|
+
status. Configure the required check to point at the sentinel job name.
|
|
61
|
+
|
|
62
|
+
2. **Split workflow** — keep the path-filtered workflow for actual CI work, and add a
|
|
63
|
+
separate always-running workflow that provides the required status check name
|
|
64
|
+
(succeeds immediately for non-code PRs, waits for CI for code PRs).
|
|
65
|
+
fix_code:
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: "Fix: always-running workflow with internal path detection and sentinel job"
|
|
68
|
+
code: |
|
|
69
|
+
name: CI
|
|
70
|
+
# No path filter — workflow always runs for all PRs
|
|
71
|
+
on:
|
|
72
|
+
pull_request:
|
|
73
|
+
branches: [main]
|
|
74
|
+
|
|
75
|
+
jobs:
|
|
76
|
+
changes:
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
outputs:
|
|
79
|
+
code: ${{ steps.filter.outputs.code }}
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
- uses: dorny/paths-filter@v3
|
|
83
|
+
id: filter
|
|
84
|
+
with:
|
|
85
|
+
filters: |
|
|
86
|
+
code:
|
|
87
|
+
- 'src/**'
|
|
88
|
+
- '*.ts'
|
|
89
|
+
- 'package*.json'
|
|
90
|
+
|
|
91
|
+
test:
|
|
92
|
+
needs: changes
|
|
93
|
+
if: needs.changes.outputs.code == 'true'
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
steps:
|
|
96
|
+
- uses: actions/checkout@v4
|
|
97
|
+
- run: npm ci && npm test
|
|
98
|
+
|
|
99
|
+
# Branch protection required check: "CI / ci-gate" (not "CI / test")
|
|
100
|
+
# Always produces a status — green for docs PRs, waits for test on code PRs
|
|
101
|
+
ci-gate:
|
|
102
|
+
needs: [changes, test]
|
|
103
|
+
if: always()
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
steps:
|
|
106
|
+
- name: Confirm CI passed or code was not changed
|
|
107
|
+
run: |
|
|
108
|
+
CODE_CHANGED="${{ needs.changes.outputs.code }}"
|
|
109
|
+
TEST_RESULT="${{ needs.test.result }}"
|
|
110
|
+
if [[ "$CODE_CHANGED" == "false" ]]; then
|
|
111
|
+
echo "✅ No code changes — CI gate passes automatically"
|
|
112
|
+
elif [[ "$TEST_RESULT" == "success" ]]; then
|
|
113
|
+
echo "✅ Tests passed"
|
|
114
|
+
else
|
|
115
|
+
echo "❌ Tests $TEST_RESULT"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
- language: yaml
|
|
120
|
+
label: "Alternative: run all steps always but skip expensive ones via filter"
|
|
121
|
+
code: |
|
|
122
|
+
name: CI
|
|
123
|
+
on:
|
|
124
|
+
pull_request:
|
|
125
|
+
branches: [main]
|
|
126
|
+
# No workflow-level path filter — status always reported
|
|
127
|
+
|
|
128
|
+
jobs:
|
|
129
|
+
test:
|
|
130
|
+
runs-on: ubuntu-latest
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/checkout@v4
|
|
133
|
+
|
|
134
|
+
- uses: dorny/paths-filter@v3
|
|
135
|
+
id: filter
|
|
136
|
+
with:
|
|
137
|
+
filters: |
|
|
138
|
+
code: ['src/**', '*.ts', 'package*.json']
|
|
139
|
+
|
|
140
|
+
- name: Install dependencies
|
|
141
|
+
if: steps.filter.outputs.code == 'true'
|
|
142
|
+
run: npm ci
|
|
143
|
+
|
|
144
|
+
- name: Run tests
|
|
145
|
+
if: steps.filter.outputs.code == 'true'
|
|
146
|
+
run: npm test
|
|
147
|
+
# Job always completes with success — check is always reported
|
|
148
|
+
prevention:
|
|
149
|
+
- "Never use workflow-level `paths:` or `branches:` filters as the sole trigger for a required status check."
|
|
150
|
+
- "Use `dorny/paths-filter` or `tj-actions/changed-files` INSIDE an always-running workflow instead of workflow-level path filters."
|
|
151
|
+
- "Name required status checks after jobs that always produce a status, even on non-code PRs."
|
|
152
|
+
- "Test branch protection rules by opening a documentation-only PR to verify all required checks complete."
|
|
153
|
+
- "Consider admin overrides as a last resort, not a workflow fix — the root cause will keep happening."
|
|
154
|
+
docs:
|
|
155
|
+
- url: "https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks"
|
|
156
|
+
label: "Troubleshooting required status checks (path filter skip documented)"
|
|
157
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore"
|
|
158
|
+
label: "Workflow syntax: paths and paths-ignore filters"
|
|
159
|
+
- url: "https://github.com/dorny/paths-filter"
|
|
160
|
+
label: "dorny/paths-filter — detect changed files inside workflow"
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
id: concurrency-timing-012
|
|
2
|
+
title: "Deployment wait-timer + cancel-in-progress: true Creates Permanent Deployment Starvation Loop"
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- wait-timer
|
|
7
|
+
- cancel-in-progress
|
|
8
|
+
- deployment
|
|
9
|
+
- environment
|
|
10
|
+
- starvation
|
|
11
|
+
- concurrency
|
|
12
|
+
- production
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: "Run was cancelled|Canceling since a higher priority waiting request"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "wait.?timer|waiting for environment.*approval"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Run was cancelled"
|
|
20
|
+
- "Canceling since a higher priority waiting request for 'production' exists"
|
|
21
|
+
root_cause: |
|
|
22
|
+
When a deployment workflow combines `concurrency.cancel-in-progress: true` with a
|
|
23
|
+
deployment environment that has a `wait-timer` configured (a mandatory delay before
|
|
24
|
+
deployment proceeds), every new commit to the branch creates a starvation loop where
|
|
25
|
+
no deployment ever reaches the execution phase:
|
|
26
|
+
|
|
27
|
+
1. Run A starts → deployment job begins waiting out the environment wait-timer (e.g., 5 min)
|
|
28
|
+
2. A new commit is pushed → Run B starts in the same concurrency group
|
|
29
|
+
3. `cancel-in-progress: true` fires → Run A is cancelled while still in the wait-timer
|
|
30
|
+
4. Run B now begins its own wait-timer countdown
|
|
31
|
+
5. Another commit arrives → Run B is cancelled during its timer
|
|
32
|
+
6. This repeats indefinitely — no deployment ever executes
|
|
33
|
+
|
|
34
|
+
This loop is particularly insidious because:
|
|
35
|
+
- All cancellations appear expected and benign in the Actions UI (no failures shown)
|
|
36
|
+
- The repository looks healthy — CI passes, deployments start — but production is
|
|
37
|
+
silently never updated
|
|
38
|
+
- Active development repos where commits arrive faster than the wait-timer duration
|
|
39
|
+
are especially vulnerable
|
|
40
|
+
|
|
41
|
+
Note: GitHub's concurrency model allows only ONE pending run per group. With
|
|
42
|
+
`cancel-in-progress: true`, a new run cancels the RUNNING run (not just queues) —
|
|
43
|
+
so even a very short wait-timer cannot escape this if commits arrive frequently.
|
|
44
|
+
fix: |
|
|
45
|
+
Do not combine `cancel-in-progress: true` with deployment environment `wait-timer`
|
|
46
|
+
on the same workflow. Use `cancel-in-progress: false` (the default) for deploy
|
|
47
|
+
workflows — this queues runs so each commit eventually deploys in order.
|
|
48
|
+
|
|
49
|
+
If you want fast feedback for CI but reliable deployments for CD, split them into
|
|
50
|
+
separate workflow files with different concurrency strategies.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: "Fix: disable cancel-in-progress for deploy workflow with wait-timer"
|
|
54
|
+
code: |
|
|
55
|
+
name: Deploy to Production
|
|
56
|
+
on:
|
|
57
|
+
push:
|
|
58
|
+
branches: [main]
|
|
59
|
+
|
|
60
|
+
concurrency:
|
|
61
|
+
group: deploy-production
|
|
62
|
+
cancel-in-progress: false # Queue — never starve a deployment with wait-timer
|
|
63
|
+
|
|
64
|
+
jobs:
|
|
65
|
+
deploy:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
environment: production # Has wait-timer: 5 configured
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/checkout@v4
|
|
70
|
+
- run: ./deploy.sh
|
|
71
|
+
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "Split pipeline: CI cancels freely; deploy queues safely after CI"
|
|
74
|
+
code: |
|
|
75
|
+
# ci.yml — fast feedback, cancel stale runs is fine
|
|
76
|
+
name: CI
|
|
77
|
+
on:
|
|
78
|
+
push:
|
|
79
|
+
branches: [main]
|
|
80
|
+
concurrency:
|
|
81
|
+
group: ci-${{ github.ref }}
|
|
82
|
+
cancel-in-progress: true # OK: no side effects
|
|
83
|
+
|
|
84
|
+
jobs:
|
|
85
|
+
test:
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v4
|
|
89
|
+
- run: npm test
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
# deploy.yml — triggered after CI, environment has wait-timer
|
|
93
|
+
name: Deploy
|
|
94
|
+
on:
|
|
95
|
+
workflow_run:
|
|
96
|
+
workflows: ["CI"]
|
|
97
|
+
types: [completed]
|
|
98
|
+
branches: [main]
|
|
99
|
+
|
|
100
|
+
concurrency:
|
|
101
|
+
group: deploy-production
|
|
102
|
+
cancel-in-progress: false # Queue; every successful CI run gets deployed
|
|
103
|
+
|
|
104
|
+
jobs:
|
|
105
|
+
deploy:
|
|
106
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
environment: production # wait-timer is safe — no cancel-in-progress racing it
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
with:
|
|
112
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
113
|
+
- run: ./deploy.sh
|
|
114
|
+
prevention:
|
|
115
|
+
- "Never combine `cancel-in-progress: true` with a deployment environment `wait-timer` in the same workflow."
|
|
116
|
+
- "Use `cancel-in-progress: false` for any workflow that deploys to environments with protection rules."
|
|
117
|
+
- "Decouple CI (cancel-ok, fast) from CD (queued, reliable) into separate workflow files."
|
|
118
|
+
- "Monitor the Actions tab for a pattern of all deployments showing as CANCELLED — this is a sign of starvation."
|
|
119
|
+
docs:
|
|
120
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/using-concurrency"
|
|
121
|
+
label: "GitHub Docs: Using concurrency in GitHub Actions"
|
|
122
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment#wait-timer"
|
|
123
|
+
label: "GitHub Docs: Managing environments — wait timer"
|
|
124
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run"
|
|
125
|
+
label: "workflow_run event — decouple CI and CD pipelines"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
id: known-unsolved-017
|
|
2
|
+
title: "timeout-minutes Is Silently Ignored on Steps Inside Composite Actions"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- timeout-minutes
|
|
7
|
+
- composite-action
|
|
8
|
+
- limitation
|
|
9
|
+
- step-timeout
|
|
10
|
+
- composite
|
|
11
|
+
- hung-step
|
|
12
|
+
- action-yml
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: "timeout.?minutes.*composite|using.*composite.*timeout"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "Unexpected value 'timeout-minutes'"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Unexpected value 'timeout-minutes'"
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions supports `timeout-minutes` at the job level and at individual step level
|
|
22
|
+
inside regular workflow files. However, `timeout-minutes` is NOT enforced on steps
|
|
23
|
+
inside composite actions (action.yml files using `runs.using: composite`).
|
|
24
|
+
|
|
25
|
+
Adding `timeout-minutes` to a step within a composite action's `steps:` block is
|
|
26
|
+
accepted by the YAML parser without error, but the timeout is silently NOT applied
|
|
27
|
+
at runtime. A hung step inside a composite action will run until the parent job's
|
|
28
|
+
overall `timeout-minutes` is exhausted — up to 6 hours on GitHub-hosted runners by
|
|
29
|
+
default.
|
|
30
|
+
|
|
31
|
+
This creates a dangerous gap:
|
|
32
|
+
- Network-dependent steps (downloads, API calls, package installs) inside a composite
|
|
33
|
+
action can hang indefinitely if the network is slow or unresponsive
|
|
34
|
+
- `actions/cache` intermittently stalls; wrapping it in a composite action with a
|
|
35
|
+
step `timeout-minutes: 5` provides NO protection
|
|
36
|
+
- Composite action authors cannot provide defensive step timeouts — users of the
|
|
37
|
+
composite action must rely entirely on the calling job's `timeout-minutes`
|
|
38
|
+
- Since the step timeout appears to be set (the YAML is valid), developers believe
|
|
39
|
+
they have a guard when they actually have none
|
|
40
|
+
|
|
41
|
+
The limitation has been tracked and discussed in the community since 2021
|
|
42
|
+
(actions/runner#1979) and remains unresolved as of 2026.
|
|
43
|
+
|
|
44
|
+
Source: actions/runner#1979 — "We would like to see timeout-minutes supported on
|
|
45
|
+
steps in composite actions. Occasionally actions like actions/cache bug out and run
|
|
46
|
+
for the default timeout time (6 hours) which causes unnecessary costs."
|
|
47
|
+
fix: |
|
|
48
|
+
Since step-level timeouts cannot be enforced inside composite actions, use these
|
|
49
|
+
workarounds:
|
|
50
|
+
|
|
51
|
+
1. **Shell-level timeout**: Wrap commands in `timeout <seconds>` (bash) or `Start-Sleep`
|
|
52
|
+
+ `Start-Job` with timeout (PowerShell) to kill hung processes from within the script.
|
|
53
|
+
2. **Job-level `timeout-minutes`**: Set a conservative timeout on the calling job as a
|
|
54
|
+
safety net — it limits the composite action's maximum runtime.
|
|
55
|
+
3. **Step-level timeout in the calling workflow**: When calling a composite action as
|
|
56
|
+
a step in a workflow file, `timeout-minutes` IS supported at the step level in the
|
|
57
|
+
workflow (not inside the composite itself). This provides a per-call timeout.
|
|
58
|
+
4. **Split into multiple reusable actions**: Replace a large composite action with
|
|
59
|
+
multiple smaller ones, each invoked as a separate workflow step — where step-level
|
|
60
|
+
`timeout-minutes` IS enforced.
|
|
61
|
+
fix_code:
|
|
62
|
+
- language: yaml
|
|
63
|
+
label: "WRONG — timeout-minutes inside composite action step is silently ignored"
|
|
64
|
+
code: |
|
|
65
|
+
# action.yml — composite action
|
|
66
|
+
name: "Setup with Timeout"
|
|
67
|
+
runs:
|
|
68
|
+
using: composite
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download dependencies
|
|
71
|
+
timeout-minutes: 5 # ← SILENTLY IGNORED — not enforced at runtime
|
|
72
|
+
shell: bash
|
|
73
|
+
run: curl -o deps.tar.gz "${{ inputs.deps-url }}"
|
|
74
|
+
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: "Workaround: shell-level timeout inside composite action"
|
|
77
|
+
code: |
|
|
78
|
+
# action.yml — composite action
|
|
79
|
+
name: "Setup with Shell Timeout"
|
|
80
|
+
runs:
|
|
81
|
+
using: composite
|
|
82
|
+
steps:
|
|
83
|
+
- name: Download dependencies with shell timeout
|
|
84
|
+
shell: bash
|
|
85
|
+
run: |
|
|
86
|
+
# timeout-minutes on this step would be ignored — use shell timeout instead
|
|
87
|
+
timeout 300 curl -o deps.tar.gz "${{ inputs.deps-url }}" || {
|
|
88
|
+
echo "::error::Download timed out after 5 minutes"
|
|
89
|
+
exit 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- name: Cache restore with timeout guard
|
|
93
|
+
shell: bash
|
|
94
|
+
run: |
|
|
95
|
+
timeout 120 ./restore-cache.sh || {
|
|
96
|
+
echo "::warning::Cache restore timed out — continuing without cache"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
- language: yaml
|
|
100
|
+
label: "Workaround: step-level timeout in the calling WORKFLOW (not inside composite)"
|
|
101
|
+
code: |
|
|
102
|
+
# workflow.yml — timeout-minutes at step level in workflow DOES work
|
|
103
|
+
jobs:
|
|
104
|
+
build:
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
timeout-minutes: 30 # Job-level safety net
|
|
107
|
+
steps:
|
|
108
|
+
- uses: actions/checkout@v4
|
|
109
|
+
|
|
110
|
+
# timeout-minutes here is at the WORKFLOW step level — this IS enforced
|
|
111
|
+
- name: Run composite action
|
|
112
|
+
uses: ./.github/actions/my-composite-action
|
|
113
|
+
timeout-minutes: 10 # Works: this is a workflow step, not inside the composite
|
|
114
|
+
|
|
115
|
+
- run: npm run build
|
|
116
|
+
|
|
117
|
+
- language: yaml
|
|
118
|
+
label: "Workaround: split composite into multiple actions — each gets timeout"
|
|
119
|
+
code: |
|
|
120
|
+
# workflow.yml — multiple smaller actions instead of one large composite
|
|
121
|
+
jobs:
|
|
122
|
+
build:
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
steps:
|
|
125
|
+
# Each step in the workflow gets enforceable timeout-minutes
|
|
126
|
+
- uses: ./.github/actions/setup-node
|
|
127
|
+
timeout-minutes: 5 # Enforced — workflow step level
|
|
128
|
+
|
|
129
|
+
- uses: ./.github/actions/restore-cache
|
|
130
|
+
timeout-minutes: 3 # Enforced
|
|
131
|
+
|
|
132
|
+
- uses: ./.github/actions/install-deps
|
|
133
|
+
timeout-minutes: 10 # Enforced
|
|
134
|
+
prevention:
|
|
135
|
+
- "Never rely on `timeout-minutes` inside composite action `steps:` — the field is accepted but silently ignored."
|
|
136
|
+
- "Use `timeout <seconds>` (bash) or PowerShell job timeouts as shell-level guards for long-running composite steps."
|
|
137
|
+
- "Apply `timeout-minutes` at the step level in the CALLING WORKFLOW (not inside the composite) for per-invocation limits."
|
|
138
|
+
- "Always set a job-level `timeout-minutes` on any job that calls composite actions, as a safety net against runaway steps."
|
|
139
|
+
- "Composite action authors should document the expected maximum runtime so callers can set appropriate job-level timeouts."
|
|
140
|
+
docs:
|
|
141
|
+
- url: "https://github.com/actions/runner/issues/1979"
|
|
142
|
+
label: "actions/runner#1979 — timeout-minutes not enforced in composite action steps (open since 2021)"
|
|
143
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes"
|
|
144
|
+
label: "GitHub Docs: steps[*].timeout-minutes (workflow-level steps — works)"
|
|
145
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#runs-for-composite-actions"
|
|
146
|
+
label: "GitHub Docs: Metadata syntax for composite actions"
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
id: known-unsolved-014
|
|
2
|
+
title: "Reusable Workflows Cannot Be Called from Composite Actions"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- composite-action
|
|
7
|
+
- reusable-workflow
|
|
8
|
+
- workflow-call
|
|
9
|
+
- limitation
|
|
10
|
+
- uses-key
|
|
11
|
+
- no-fix
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "Reusable workflows cannot be referenced from composite actions"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "uses:.*\\.github/workflows/.*\\.yml.*composite"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "A reusable workflow cannot be used as a step in a composite action"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Error: Reusable workflows cannot be referenced from composite actions."
|
|
21
|
+
- "A reusable workflow cannot be used as a step in a composite action."
|
|
22
|
+
root_cause: |
|
|
23
|
+
GitHub Actions enforces a strict separation between two different abstraction levels:
|
|
24
|
+
- **Actions** (composite, JavaScript, Docker) — called from workflow steps via `uses:`
|
|
25
|
+
- **Reusable workflows** (called via `workflow_call` trigger) — called from workflow jobs
|
|
26
|
+
via `uses:` at the JOB level, not the step level
|
|
27
|
+
|
|
28
|
+
A composite action runs within a step inside a job. A reusable workflow, however, IS a
|
|
29
|
+
job (or set of jobs). You cannot embed a job inside a step — this is a fundamental
|
|
30
|
+
architectural constraint, not a configuration error.
|
|
31
|
+
|
|
32
|
+
This fails at validation time with a clear error message. The error is triggered when:
|
|
33
|
+
- A composite action's `runs.steps` contains a `uses:` entry that points to a
|
|
34
|
+
`.github/workflows/*.yml` file (a reusable workflow)
|
|
35
|
+
- An organization tried to nest workflow-level orchestration inside a step-level action
|
|
36
|
+
|
|
37
|
+
There is no workaround that preserves both the composite action wrapping and the reusable
|
|
38
|
+
workflow invocation. GitHub has confirmed this is by design and has no plans to change it.
|
|
39
|
+
The GitHub Actions team's rationale: reusable workflows operate at the job/runner level
|
|
40
|
+
and require job-level context (needs:, outputs:, secrets: inheritance) that cannot exist
|
|
41
|
+
inside a step.
|
|
42
|
+
fix: |
|
|
43
|
+
There is no fix — this is a platform limitation.
|
|
44
|
+
|
|
45
|
+
The recommended alternatives are:
|
|
46
|
+
1. **Restructure to call the reusable workflow at the job level** (not from inside a
|
|
47
|
+
composite action). Move the reusable workflow call to a dedicated job in your workflow
|
|
48
|
+
file using `jobs.my-job.uses:`.
|
|
49
|
+
2. **Convert the reusable workflow into a composite action** if the logic does not require
|
|
50
|
+
job-level features (matrix, needs: dependencies, explicit runner selection, secrets
|
|
51
|
+
inheritance via `secrets: inherit`).
|
|
52
|
+
3. **Inline the reusable workflow steps** into the composite action directly. While this
|
|
53
|
+
loses the DRY benefit of the reusable workflow, it resolves the structural constraint.
|
|
54
|
+
fix_code:
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: "Call reusable workflow at job level (not from composite action)"
|
|
57
|
+
code: |
|
|
58
|
+
# ❌ This FAILS — cannot call reusable workflow from composite action step
|
|
59
|
+
# In: .github/actions/my-composite/action.yml
|
|
60
|
+
# runs:
|
|
61
|
+
# using: "composite"
|
|
62
|
+
# steps:
|
|
63
|
+
# - uses: ./.github/workflows/shared-build.yml # ERROR: not allowed
|
|
64
|
+
# with:
|
|
65
|
+
# environment: staging
|
|
66
|
+
|
|
67
|
+
# ✅ Call reusable workflow at job level in workflow file instead
|
|
68
|
+
jobs:
|
|
69
|
+
setup:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
steps:
|
|
72
|
+
# Composite action can still do pre/post steps
|
|
73
|
+
- uses: ./.github/actions/my-composite
|
|
74
|
+
with:
|
|
75
|
+
param: value
|
|
76
|
+
|
|
77
|
+
shared-build:
|
|
78
|
+
needs: setup
|
|
79
|
+
uses: ./.github/workflows/shared-build.yml
|
|
80
|
+
with:
|
|
81
|
+
environment: staging
|
|
82
|
+
secrets: inherit
|
|
83
|
+
- language: yaml
|
|
84
|
+
label: "Convert reusable workflow to composite action (when job-level features not needed)"
|
|
85
|
+
code: |
|
|
86
|
+
# .github/actions/shared-build/action.yml
|
|
87
|
+
name: "Shared Build"
|
|
88
|
+
description: "Composite action equivalent of the shared-build reusable workflow"
|
|
89
|
+
inputs:
|
|
90
|
+
environment:
|
|
91
|
+
required: true
|
|
92
|
+
description: "Target environment"
|
|
93
|
+
runs:
|
|
94
|
+
using: "composite"
|
|
95
|
+
steps:
|
|
96
|
+
- name: Setup Node
|
|
97
|
+
uses: actions/setup-node@v4
|
|
98
|
+
with:
|
|
99
|
+
node-version: '20'
|
|
100
|
+
- name: Build
|
|
101
|
+
shell: bash
|
|
102
|
+
run: npm run build:${{ inputs.environment }}
|
|
103
|
+
prevention:
|
|
104
|
+
- "Understand the GitHub Actions hierarchy: Docker/JS/composite actions run WITHIN steps; reusable workflows run AS jobs."
|
|
105
|
+
- "Use composite actions when you want to share steps across workflows within a single job."
|
|
106
|
+
- "Use reusable workflows (workflow_call) when you want to share an entire job or multi-job sequence."
|
|
107
|
+
- "If you need both abstraction levels, combine them: a job calls a reusable workflow, and within that workflow's jobs, steps call composite actions."
|
|
108
|
+
docs:
|
|
109
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#limitations"
|
|
110
|
+
label: "GitHub docs — Reusing workflows: limitations (composite action restriction)"
|
|
111
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action"
|
|
112
|
+
label: "GitHub docs — Creating a composite action"
|
|
113
|
+
- url: "https://github.com/orgs/community/discussions/11834"
|
|
114
|
+
label: "Community discussion #11834 — reusable workflow called from composite action error"
|
|
115
|
+
- url: "https://stackoverflow.com/questions/70421268/how-to-reference-a-reusable-workflow-from-a-composite-action"
|
|
116
|
+
label: "Stack Overflow — reference reusable workflow from composite action"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
id: known-unsolved-015
|
|
2
|
+
title: "Scheduled Workflows Only Run on the Default Branch — Cannot Target Arbitrary Branches"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- cron
|
|
8
|
+
- default-branch
|
|
9
|
+
- trigger
|
|
10
|
+
- branches-filter
|
|
11
|
+
- platform-limitation
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "schedule.*branches|branches.*schedule"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "cron.*not.*branch|branch.*cron.*ignored"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "Unexpected value 'branches'"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "You have an error in your yaml syntax on line X"
|
|
21
|
+
- "Unexpected value 'branches'"
|
|
22
|
+
- "The 'branches' filter is not supported under 'schedule'"
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub Actions schedule (cron) triggers **always run the workflow file from the
|
|
25
|
+
repository's default branch** (typically `main`). This is an intentional platform
|
|
26
|
+
security constraint.
|
|
27
|
+
|
|
28
|
+
There is no supported way to target a non-default branch with a `schedule:` trigger:
|
|
29
|
+
- Adding `branches:` under `schedule:` causes a YAML syntax error ("Unexpected value
|
|
30
|
+
'branches'") — it is not a valid key in that position.
|
|
31
|
+
- Placing the schedule workflow on a non-default branch does not cause it to run.
|
|
32
|
+
- The `github.ref` and `github.head_ref` values will always reflect the default branch
|
|
33
|
+
when triggered by a schedule event.
|
|
34
|
+
|
|
35
|
+
**Why GitHub enforces this:**
|
|
36
|
+
If scheduled workflows could target arbitrary branches, an attacker who pushes
|
|
37
|
+
malicious code to a feature branch could have it execute on a cron schedule with the
|
|
38
|
+
repository's full secrets — a significant supply-chain risk. By restricting cron runs
|
|
39
|
+
to the default branch, GitHub ensures only reviewed/merged code runs on schedule.
|
|
40
|
+
|
|
41
|
+
**Platform impact:**
|
|
42
|
+
- Cannot create separate nightly build schedules for multiple branches from one repo.
|
|
43
|
+
- Cannot run a scheduled integration test on a long-lived release branch without
|
|
44
|
+
special workarounds.
|
|
45
|
+
- Community has requested this capability since 2021 (community discussion #16107).
|
|
46
|
+
|
|
47
|
+
Sources: Stack Overflow Q/A, GitHub Community #16107, GitHub Docs
|
|
48
|
+
fix: |
|
|
49
|
+
There is no direct fix — this is a platform constraint. Use these workarounds:
|
|
50
|
+
|
|
51
|
+
**Workaround 1 (Recommended): Check out the target branch in the scheduled workflow**
|
|
52
|
+
Keep the scheduled workflow on the default branch but have the job check out the
|
|
53
|
+
desired branch in its `actions/checkout` step:
|
|
54
|
+
|
|
55
|
+
**Workaround 2: Trigger via `workflow_dispatch` from an external scheduler**
|
|
56
|
+
Use the GitHub REST API or `gh workflow run` from a controlled external system
|
|
57
|
+
(another scheduled job, GitHub App, etc.) to trigger a `workflow_dispatch` event
|
|
58
|
+
targeting a specific `ref` (branch or tag).
|
|
59
|
+
|
|
60
|
+
**Workaround 3: `workflow_run` chaining**
|
|
61
|
+
Not a direct substitute, but a workflow triggered by `workflow_run` on a push to
|
|
62
|
+
the target branch can perform scheduled-like follow-up logic.
|
|
63
|
+
|
|
64
|
+
**For release branches:** Merge a scheduled workflow file into each release branch
|
|
65
|
+
as part of the branch maintenance process — each branch can independently schedule
|
|
66
|
+
its own cron.
|
|
67
|
+
fix_code:
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Broken — branches filter not valid under schedule, causes YAML error"
|
|
70
|
+
code: |
|
|
71
|
+
# ❌ BROKEN: 'branches' is not a valid key under schedule — YAML syntax error
|
|
72
|
+
on:
|
|
73
|
+
schedule:
|
|
74
|
+
- cron: '0 1 * * *'
|
|
75
|
+
branches: # ← invalid key, causes: Unexpected value 'branches'
|
|
76
|
+
- release/v2
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: "Workaround — check out target branch in the scheduled workflow step"
|
|
79
|
+
code: |
|
|
80
|
+
# ✅ WORKAROUND: workflow lives on default branch, checks out the desired ref
|
|
81
|
+
on:
|
|
82
|
+
schedule:
|
|
83
|
+
- cron: '0 1 * * *' # runs from default branch (main)
|
|
84
|
+
|
|
85
|
+
jobs:
|
|
86
|
+
nightly-test:
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
steps:
|
|
89
|
+
- uses: actions/checkout@v4
|
|
90
|
+
with:
|
|
91
|
+
ref: 'release/v2' # explicitly check out the target branch
|
|
92
|
+
# All subsequent steps operate on release/v2 code
|
|
93
|
+
- run: ./run-tests.sh
|
|
94
|
+
- language: yaml
|
|
95
|
+
label: "Workaround — trigger workflow_dispatch against a specific branch via gh CLI"
|
|
96
|
+
code: |
|
|
97
|
+
# ✅ WORKAROUND: External script that fires workflow_dispatch targeting a branch
|
|
98
|
+
# Run this from another scheduled job (e.g., a GitHub App or a cron runner)
|
|
99
|
+
gh workflow run nightly.yml \
|
|
100
|
+
--repo owner/repo \
|
|
101
|
+
--ref release/v2 # workflow_dispatch supports specifying a branch ref
|
|
102
|
+
prevention:
|
|
103
|
+
- "Do not add `branches:` under `schedule:` — it is not a valid key and will cause YAML syntax errors."
|
|
104
|
+
- "Design scheduled workflows to check out the target branch explicitly via `actions/checkout` with the `ref:` input."
|
|
105
|
+
- "For complex multi-branch scheduling needs, consider using a GitHub App or external scheduler that fires `workflow_dispatch` events targeting specific refs."
|
|
106
|
+
- "Document this platform limitation in team wikis to prevent repeated attempts at adding `branches:` to schedule triggers."
|
|
107
|
+
docs:
|
|
108
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule"
|
|
109
|
+
label: "GitHub Docs: Events that trigger workflows — schedule"
|
|
110
|
+
- url: "https://github.com/orgs/community/discussions/16107"
|
|
111
|
+
label: "GitHub Community #16107 — Schedule trigger target branch request"
|
|
112
|
+
- url: "https://stackoverflow.com/questions/63612155/how-do-i-trigger-a-scheduled-action-on-a-specific-branch"
|
|
113
|
+
label: "Stack Overflow: How to trigger a scheduled action on a specific branch"
|