@htekdev/actions-debugger 1.0.0 → 1.0.2
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/LICENSE +21 -21
- package/README.md +108 -108
- package/errors/_schema.json +89 -89
- package/errors/caching-artifacts/artifact-storage-quota-exceeded.yml +118 -0
- package/errors/caching-artifacts/cache-miss.yml +56 -56
- package/errors/caching-artifacts/cache-save-cancelled-job.yml +82 -0
- package/errors/caching-artifacts/cache-v3-to-v4-breaking-changes.yml +95 -0
- package/errors/caching-artifacts/cross-repo-artifacts-not-supported.yml +102 -0
- package/errors/caching-artifacts/upload-artifact-no-files-found.yml +92 -0
- package/errors/caching-artifacts/upload-artifact-v4-breaking.yml +67 -67
- package/errors/concurrency-timing/cancel-in-progress-deploy-drops.yml +97 -0
- package/errors/concurrency-timing/jobs-cancelled-unexpectedly.yml +60 -60
- package/errors/concurrency-timing/skipped-needs-cascade.yml +103 -0
- package/errors/concurrency-timing/workflow-run-conclusion-unchecked.yml +100 -0
- package/errors/known-unsolved/composite-input-env-vars-missing.yml +91 -0
- package/errors/known-unsolved/composite-nested-outputs-null.yml +101 -0
- package/errors/known-unsolved/no-dynamic-secret-access.yml +111 -0
- package/errors/known-unsolved/no-step-level-rerun.yml +94 -0
- package/errors/known-unsolved/no-step-retry.yml +53 -53
- package/errors/known-unsolved/workflow-rerun-limit.yml +101 -0
- package/errors/permissions-auth/checkout-submodule-private-auth.yml +91 -0
- package/errors/permissions-auth/fork-pr-secrets-unavailable.yml +97 -0
- package/errors/permissions-auth/gcp-oidc-workload-identity-misconfigured.yml +130 -0
- package/errors/permissions-auth/github-token-403.yml +64 -64
- package/errors/permissions-auth/github-token-protected-branch-push.yml +109 -0
- package/errors/permissions-auth/oidc-aws-failure.yml +85 -85
- package/errors/permissions-auth/oidc-azure-subject-mismatch.yml +91 -0
- package/errors/runner-environment/disk-space.yml +57 -57
- package/errors/runner-environment/docker-buildx-not-setup.yml +106 -0
- package/errors/runner-environment/macos-homebrew-path.yml +90 -0
- package/errors/runner-environment/node-runtime-deprecation.yml +56 -56
- package/errors/runner-environment/node20-to-node24-migration.yml +118 -0
- package/errors/runner-environment/npm-ci-lockfile-mismatch.yml +112 -0
- package/errors/runner-environment/self-hosted-stale-toolcache.yml +73 -0
- package/errors/runner-environment/setup-node-version-file-missing.yml +105 -0
- package/errors/runner-environment/windows-execution-policy.yml +83 -0
- package/errors/silent-failures/add-mask-no-retroactive-masking.yml +75 -0
- package/errors/silent-failures/composite-boolean-inputs-as-strings.yml +110 -0
- package/errors/silent-failures/conditional-output-null-downstream.yml +82 -0
- package/errors/silent-failures/continue-on-error-masks-failure.yml +86 -0
- package/errors/silent-failures/github-token-no-trigger.yml +57 -57
- package/errors/silent-failures/reusable-workflow-env-secrets-empty.yml +90 -0
- package/errors/silent-failures/scheduled-workflow-disabled.yml +59 -59
- package/errors/silent-failures/sparse-checkout-sticky-cone-mode.yml +120 -0
- package/errors/triggers/cron-schedule-late.yml +59 -59
- package/errors/triggers/pull-request-target-rce-risk.yml +117 -0
- package/errors/triggers/workflow-not-triggering.yml +60 -60
- package/errors/triggers/workflow-run-default-branch-requirement.yml +78 -0
- package/errors/yaml-syntax/anchors-not-supported.yml +95 -0
- package/errors/yaml-syntax/dynamic-matrix-fromjson-failure.yml +99 -0
- package/errors/yaml-syntax/if-always-true.yml +52 -52
- package/errors/yaml-syntax/missing-expression-wrapper.yml +67 -0
- package/errors/yaml-syntax/needs-indirect-outputs.yml +91 -0
- package/errors/yaml-syntax/reusable-workflow-missing-output-declaration.yml +140 -0
- package/errors/yaml-syntax/secrets-in-if.yml +55 -55
- package/errors/yaml-syntax/unexpected-yaml-key.yml +69 -69
- package/errors/yaml-syntax/working-directory-ignored-on-uses.yml +66 -0
- package/package.json +70 -67
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
id: silent-failures-002
|
|
2
|
-
title: "GITHUB_TOKEN Cannot Trigger Downstream Workflows"
|
|
3
|
-
category: silent-failures
|
|
4
|
-
severity: silent-failure
|
|
5
|
-
tags:
|
|
6
|
-
- github-token
|
|
7
|
-
- triggers
|
|
8
|
-
- downstream
|
|
9
|
-
- workflow-chain
|
|
10
|
-
- authentication
|
|
11
|
-
patterns:
|
|
12
|
-
- regex: "events triggered by the GITHUB_TOKEN.*will not create a new workflow run"
|
|
13
|
-
flags: "i"
|
|
14
|
-
- regex: "github-actions\\[bot\\]"
|
|
15
|
-
flags: "i"
|
|
16
|
-
- regex: "GITHUB_TOKEN"
|
|
17
|
-
flags: "i"
|
|
18
|
-
error_messages:
|
|
19
|
-
- "Events triggered by the GITHUB_TOKEN will not create a new workflow run."
|
|
20
|
-
root_cause: |
|
|
21
|
-
Commits, tags, and releases created with the repository `GITHUB_TOKEN` are intentionally
|
|
22
|
-
prevented from triggering most downstream workflows. This is a platform safety feature to
|
|
23
|
-
stop accidental recursion and workflow loops.
|
|
24
|
-
|
|
25
|
-
The upstream workflow appears to succeed, but the dependent workflow never starts, which
|
|
26
|
-
makes this feel like a silent trigger failure.
|
|
27
|
-
fix: |
|
|
28
|
-
Use a GitHub App installation token or a PAT when you intentionally need one workflow to
|
|
29
|
-
trigger another via `push`, `create`, or `release`. Keep `GITHUB_TOKEN` for normal repo
|
|
30
|
-
automation that should not fan out into new workflow runs.
|
|
31
|
-
fix_code:
|
|
32
|
-
- language: yaml
|
|
33
|
-
label: "Use a non-GITHUB_TOKEN credential for recursive automation"
|
|
34
|
-
code: |
|
|
35
|
-
jobs:
|
|
36
|
-
release:
|
|
37
|
-
runs-on: ubuntu-latest
|
|
38
|
-
steps:
|
|
39
|
-
- uses: actions/checkout@v4
|
|
40
|
-
- name: Create tag with a PAT
|
|
41
|
-
env:
|
|
42
|
-
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
|
43
|
-
run: |
|
|
44
|
-
git tag v${{ github.run_number }}
|
|
45
|
-
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git --tags
|
|
46
|
-
prevention:
|
|
47
|
-
- "Assume `GITHUB_TOKEN` will not fan out into downstream workflow runs unless you are using `workflow_dispatch` or `repository_dispatch`."
|
|
48
|
-
- "Document which automations require a GitHub App token or PAT."
|
|
49
|
-
- "Avoid recursive workflow designs that depend on implicit `push` triggers from `github-actions[bot]`."
|
|
50
|
-
docs:
|
|
51
|
-
- url: "https://docs.github.com/en/actions/security-guides/automatic-token-authentication"
|
|
52
|
-
label: "Automatic token authentication"
|
|
53
|
-
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows"
|
|
54
|
-
label: "Events that trigger workflows"
|
|
55
|
-
source:
|
|
56
|
-
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
57
|
-
section: "GITHUB_TOKEN downstream trigger limitations"
|
|
1
|
+
id: silent-failures-002
|
|
2
|
+
title: "GITHUB_TOKEN Cannot Trigger Downstream Workflows"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- github-token
|
|
7
|
+
- triggers
|
|
8
|
+
- downstream
|
|
9
|
+
- workflow-chain
|
|
10
|
+
- authentication
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "events triggered by the GITHUB_TOKEN.*will not create a new workflow run"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "github-actions\\[bot\\]"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "GITHUB_TOKEN"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Events triggered by the GITHUB_TOKEN will not create a new workflow run."
|
|
20
|
+
root_cause: |
|
|
21
|
+
Commits, tags, and releases created with the repository `GITHUB_TOKEN` are intentionally
|
|
22
|
+
prevented from triggering most downstream workflows. This is a platform safety feature to
|
|
23
|
+
stop accidental recursion and workflow loops.
|
|
24
|
+
|
|
25
|
+
The upstream workflow appears to succeed, but the dependent workflow never starts, which
|
|
26
|
+
makes this feel like a silent trigger failure.
|
|
27
|
+
fix: |
|
|
28
|
+
Use a GitHub App installation token or a PAT when you intentionally need one workflow to
|
|
29
|
+
trigger another via `push`, `create`, or `release`. Keep `GITHUB_TOKEN` for normal repo
|
|
30
|
+
automation that should not fan out into new workflow runs.
|
|
31
|
+
fix_code:
|
|
32
|
+
- language: yaml
|
|
33
|
+
label: "Use a non-GITHUB_TOKEN credential for recursive automation"
|
|
34
|
+
code: |
|
|
35
|
+
jobs:
|
|
36
|
+
release:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- name: Create tag with a PAT
|
|
41
|
+
env:
|
|
42
|
+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
|
|
43
|
+
run: |
|
|
44
|
+
git tag v${{ github.run_number }}
|
|
45
|
+
git push https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git --tags
|
|
46
|
+
prevention:
|
|
47
|
+
- "Assume `GITHUB_TOKEN` will not fan out into downstream workflow runs unless you are using `workflow_dispatch` or `repository_dispatch`."
|
|
48
|
+
- "Document which automations require a GitHub App token or PAT."
|
|
49
|
+
- "Avoid recursive workflow designs that depend on implicit `push` triggers from `github-actions[bot]`."
|
|
50
|
+
docs:
|
|
51
|
+
- url: "https://docs.github.com/en/actions/security-guides/automatic-token-authentication"
|
|
52
|
+
label: "Automatic token authentication"
|
|
53
|
+
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows"
|
|
54
|
+
label: "Events that trigger workflows"
|
|
55
|
+
source:
|
|
56
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
57
|
+
section: "GITHUB_TOKEN downstream trigger limitations"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
id: silent-failures-003
|
|
2
|
+
title: "Reusable Workflow Environment-Scoped Secrets Resolve as Empty String"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- reusable-workflows
|
|
7
|
+
- secrets
|
|
8
|
+
- environments
|
|
9
|
+
- workflow-call
|
|
10
|
+
- secrets-inherit
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "MY_SECRET length: 0"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "secrets\\.[A-Z_]+\\s*:\\s*$"
|
|
15
|
+
flags: "m"
|
|
16
|
+
error_messages:
|
|
17
|
+
- "MY_SECRET length: 0"
|
|
18
|
+
- "EMPTY"
|
|
19
|
+
root_cause: |
|
|
20
|
+
When a reusable workflow (called via `workflow_call`) declares `environment: ${{ inputs.environment_name }}`
|
|
21
|
+
on its job, the GitHub Actions documentation states that environment-scoped secrets should resolve from
|
|
22
|
+
that environment. In practice, secrets resolve to empty string unless the caller adds `secrets: inherit`.
|
|
23
|
+
|
|
24
|
+
The `secrets: inherit` flag passes all of the caller's accessible secrets to the reusable workflow,
|
|
25
|
+
including environment-scoped secrets. Without it, the called workflow's secret resolution context is
|
|
26
|
+
narrowly scoped to explicitly mapped secrets — and environment-scoped secrets are not injected even when
|
|
27
|
+
the job correctly declares the environment name.
|
|
28
|
+
|
|
29
|
+
This is especially dangerous in matrix-based CI/CD pipelines where different matrix entries target
|
|
30
|
+
different environments (e.g., per-tenant deployments). The reusable workflow appears to run normally but
|
|
31
|
+
every secret it tries to use is silently empty. The failure can be masked for days if downstream systems
|
|
32
|
+
tolerate empty credentials during a grace period (e.g., pods using in-cluster Kubernetes Secrets).
|
|
33
|
+
fix: |
|
|
34
|
+
Add `secrets: inherit` to the caller's job definition. This enables the reusable workflow to resolve
|
|
35
|
+
environment-scoped secrets from the environment declared on its job:
|
|
36
|
+
|
|
37
|
+
Alternatively, explicitly enumerate each secret you need to pass via the `secrets:` mapping block.
|
|
38
|
+
The explicit form is more secure for cross-repository reusable workflows.
|
|
39
|
+
fix_code:
|
|
40
|
+
- language: yaml
|
|
41
|
+
label: "Add secrets: inherit so environment-scoped secrets resolve in the reusable workflow"
|
|
42
|
+
code: |
|
|
43
|
+
# Caller workflow
|
|
44
|
+
jobs:
|
|
45
|
+
deploy:
|
|
46
|
+
uses: ./.github/workflows/deploy.yml
|
|
47
|
+
with:
|
|
48
|
+
target_environment: production
|
|
49
|
+
secrets: inherit # required — without this, env-scoped secrets are empty
|
|
50
|
+
|
|
51
|
+
# Reusable workflow (.github/workflows/deploy.yml) — no changes needed
|
|
52
|
+
on:
|
|
53
|
+
workflow_call:
|
|
54
|
+
inputs:
|
|
55
|
+
target_environment:
|
|
56
|
+
required: true
|
|
57
|
+
type: string
|
|
58
|
+
jobs:
|
|
59
|
+
worker:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
environment: ${{ inputs.target_environment }}
|
|
62
|
+
steps:
|
|
63
|
+
- name: Use secret
|
|
64
|
+
env:
|
|
65
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
66
|
+
run: |
|
|
67
|
+
echo "API_KEY length: ${#API_KEY}"
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Explicit secrets mapping (more secure for cross-repo reusable workflows)"
|
|
70
|
+
code: |
|
|
71
|
+
jobs:
|
|
72
|
+
deploy:
|
|
73
|
+
uses: org/infra/.github/workflows/deploy.yml@main
|
|
74
|
+
with:
|
|
75
|
+
target_environment: production
|
|
76
|
+
secrets:
|
|
77
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
78
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
79
|
+
prevention:
|
|
80
|
+
- "Always add `secrets: inherit` when a reusable workflow needs environment-scoped secrets."
|
|
81
|
+
- "Verify secret resolution by printing the secret length: `echo \"length ${#MY_SECRET}\"` — never print the value."
|
|
82
|
+
- "In matrix deployments targeting different environments, confirm each matrix item resolves its secrets before shipping."
|
|
83
|
+
- "Consider explicit secrets mapping for cross-repository workflows to avoid accidentally leaking unrelated secrets."
|
|
84
|
+
docs:
|
|
85
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows#passing-secrets-to-called-workflows"
|
|
86
|
+
label: "Passing secrets to called workflows"
|
|
87
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-environments-for-deployment"
|
|
88
|
+
label: "Using environments for deployment"
|
|
89
|
+
- url: "https://github.com/actions/runner/issues/4453"
|
|
90
|
+
label: "actions/runner#4453 — Environment-scoped secrets unreachable without secrets: inherit"
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
id: silent-failures-001
|
|
2
|
-
title: "Scheduled Workflows Silently Disabled After 60 Days"
|
|
3
|
-
category: silent-failures
|
|
4
|
-
severity: silent-failure
|
|
5
|
-
tags:
|
|
6
|
-
- schedule
|
|
7
|
-
- cron
|
|
8
|
-
- inactivity
|
|
9
|
-
- disabled
|
|
10
|
-
- automation
|
|
11
|
-
patterns:
|
|
12
|
-
- regex: "This scheduled workflow is disabled because there hasn't been activity in this repository for at least 60 days"
|
|
13
|
-
flags: "i"
|
|
14
|
-
- regex: "schedule.*workflow.*disabled"
|
|
15
|
-
flags: "i"
|
|
16
|
-
error_messages:
|
|
17
|
-
- "This scheduled workflow is disabled because there hasn't been activity in this repository for at least 60 days"
|
|
18
|
-
root_cause: |
|
|
19
|
-
In public repositories, GitHub automatically disables scheduled workflows after about
|
|
20
|
-
60 days with no repository activity. Nothing in the workflow YAML is technically wrong,
|
|
21
|
-
but the cron job stops firing until someone re-enables it or new activity occurs.
|
|
22
|
-
|
|
23
|
-
This feels like a silent failure because the expected schedule disappears rather than
|
|
24
|
-
producing a normal failed run.
|
|
25
|
-
fix: |
|
|
26
|
-
Re-enable the workflow in the Actions UI and keep the repository active. If the schedule
|
|
27
|
-
must stay alive in a low-activity repository, add a keepalive workflow that periodically
|
|
28
|
-
creates harmless activity.
|
|
29
|
-
fix_code:
|
|
30
|
-
- language: yaml
|
|
31
|
-
label: "Keep scheduled workflows active"
|
|
32
|
-
code: |
|
|
33
|
-
name: Keepalive
|
|
34
|
-
|
|
35
|
-
on:
|
|
36
|
-
schedule:
|
|
37
|
-
- cron: '0 6 * * 1'
|
|
38
|
-
workflow_dispatch:
|
|
39
|
-
|
|
40
|
-
jobs:
|
|
41
|
-
keepalive:
|
|
42
|
-
runs-on: ubuntu-latest
|
|
43
|
-
steps:
|
|
44
|
-
- uses: gautamkrishnar/keepalive-workflow@v2
|
|
45
|
-
with:
|
|
46
|
-
committer_username: github-actions[bot]
|
|
47
|
-
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
|
|
48
|
-
prevention:
|
|
49
|
-
- "Check the Actions tab periodically for disabled scheduled workflows in low-activity repositories."
|
|
50
|
-
- "Pair important cron automations with `workflow_dispatch` for manual recovery."
|
|
51
|
-
- "Use a keepalive strategy for public repos that rely on unattended schedules."
|
|
52
|
-
docs:
|
|
53
|
-
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
|
|
54
|
-
label: "schedule event"
|
|
55
|
-
- url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
|
|
56
|
-
label: "Disable and enable a workflow"
|
|
57
|
-
source:
|
|
58
|
-
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
59
|
-
section: "Scheduled workflows disabled after inactivity"
|
|
1
|
+
id: silent-failures-001
|
|
2
|
+
title: "Scheduled Workflows Silently Disabled After 60 Days"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- cron
|
|
8
|
+
- inactivity
|
|
9
|
+
- disabled
|
|
10
|
+
- automation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "This scheduled workflow is disabled because there hasn't been activity in this repository for at least 60 days"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "schedule.*workflow.*disabled"
|
|
15
|
+
flags: "i"
|
|
16
|
+
error_messages:
|
|
17
|
+
- "This scheduled workflow is disabled because there hasn't been activity in this repository for at least 60 days"
|
|
18
|
+
root_cause: |
|
|
19
|
+
In public repositories, GitHub automatically disables scheduled workflows after about
|
|
20
|
+
60 days with no repository activity. Nothing in the workflow YAML is technically wrong,
|
|
21
|
+
but the cron job stops firing until someone re-enables it or new activity occurs.
|
|
22
|
+
|
|
23
|
+
This feels like a silent failure because the expected schedule disappears rather than
|
|
24
|
+
producing a normal failed run.
|
|
25
|
+
fix: |
|
|
26
|
+
Re-enable the workflow in the Actions UI and keep the repository active. If the schedule
|
|
27
|
+
must stay alive in a low-activity repository, add a keepalive workflow that periodically
|
|
28
|
+
creates harmless activity.
|
|
29
|
+
fix_code:
|
|
30
|
+
- language: yaml
|
|
31
|
+
label: "Keep scheduled workflows active"
|
|
32
|
+
code: |
|
|
33
|
+
name: Keepalive
|
|
34
|
+
|
|
35
|
+
on:
|
|
36
|
+
schedule:
|
|
37
|
+
- cron: '0 6 * * 1'
|
|
38
|
+
workflow_dispatch:
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
keepalive:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: gautamkrishnar/keepalive-workflow@v2
|
|
45
|
+
with:
|
|
46
|
+
committer_username: github-actions[bot]
|
|
47
|
+
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
|
|
48
|
+
prevention:
|
|
49
|
+
- "Check the Actions tab periodically for disabled scheduled workflows in low-activity repositories."
|
|
50
|
+
- "Pair important cron automations with `workflow_dispatch` for manual recovery."
|
|
51
|
+
- "Use a keepalive strategy for public repos that rely on unattended schedules."
|
|
52
|
+
docs:
|
|
53
|
+
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
|
|
54
|
+
label: "schedule event"
|
|
55
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
|
|
56
|
+
label: "Disable and enable a workflow"
|
|
57
|
+
source:
|
|
58
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
59
|
+
section: "Scheduled workflows disabled after inactivity"
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
id: silent-failures-008
|
|
2
|
+
title: "Sparse Checkout Persists to Subsequent Checkout Steps (Sticky Cone Mode)"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- sparse-checkout
|
|
8
|
+
- cone-mode
|
|
9
|
+
- composite-actions
|
|
10
|
+
- file-missing
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "sparse.*checkout.*persist"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "core\\.sparseCheckout.*true"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "sparse-checkout.*not disabled"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Run actions/checkout@v4"
|
|
20
|
+
- "Expected full checkout but only sparse tree present"
|
|
21
|
+
root_cause: |
|
|
22
|
+
When `actions/checkout` is called with `sparse-checkout:` options, it sets the git
|
|
23
|
+
repository's `core.sparseCheckout = true` config. A subsequent call to `actions/checkout`
|
|
24
|
+
in the same job — even WITHOUT specifying sparse-checkout — re-uses the existing git
|
|
25
|
+
repository and inherits the sticky `core.sparseCheckout = true` setting.
|
|
26
|
+
|
|
27
|
+
Result: the second checkout appears to succeed (no error), but the working directory
|
|
28
|
+
still contains only the sparse subset from the first checkout. Files outside the
|
|
29
|
+
original sparse pattern are silently absent.
|
|
30
|
+
|
|
31
|
+
This most commonly occurs in two scenarios:
|
|
32
|
+
1. A workflow calls `actions/checkout` with sparse-checkout for a fast initial clone,
|
|
33
|
+
then calls `actions/checkout` again (different ref, different path spec) expecting
|
|
34
|
+
a full checkout.
|
|
35
|
+
2. A workflow uses sparse-checkout, then calls a composite action that internally runs
|
|
36
|
+
its own `actions/checkout`. The composite action's checkout inherits the sparse
|
|
37
|
+
setting from the parent workflow's checkout.
|
|
38
|
+
|
|
39
|
+
Root cause: a bug in `actions/checkout` — the `disableSparseCheckout()` method does not
|
|
40
|
+
explicitly set `core.sparseCheckout false` when `sparse-checkout` input is absent. A
|
|
41
|
+
fix PR (#2034) exists in the repo but has not been merged as of 2026.
|
|
42
|
+
fix: |
|
|
43
|
+
**Option 1 (recommended): Explicitly reset sparse-checkout between checkouts**
|
|
44
|
+
Add a `git sparse-checkout disable` step between the sparse and full checkouts. This
|
|
45
|
+
clears the sticky `core.sparseCheckout` flag and ensures subsequent checkouts are full.
|
|
46
|
+
|
|
47
|
+
**Option 2: Use separate `path:` directories**
|
|
48
|
+
Checkout into a unique subdirectory with the `path:` input to prevent git config
|
|
49
|
+
sharing. Each `path:` gets its own `.git` config.
|
|
50
|
+
|
|
51
|
+
**Option 3: Use `sparse-checkout-cone-mode: false` carefully**
|
|
52
|
+
When in non-cone mode, review that patterns don't accidentally match more or fewer
|
|
53
|
+
files than expected. Non-cone mode with incorrect patterns is its own source of
|
|
54
|
+
silent failures.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Reset sparse-checkout before a subsequent full checkout"
|
|
58
|
+
code: |
|
|
59
|
+
steps:
|
|
60
|
+
# First checkout: sparse (fast clone for config files only)
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
with:
|
|
63
|
+
sparse-checkout: |
|
|
64
|
+
.github
|
|
65
|
+
config/
|
|
66
|
+
|
|
67
|
+
- name: Reset sparse-checkout so next checkout is full
|
|
68
|
+
shell: bash
|
|
69
|
+
run: git sparse-checkout disable
|
|
70
|
+
|
|
71
|
+
# Second checkout (in composite action or next step): now gets full tree
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
with:
|
|
74
|
+
ref: ${{ github.sha }}
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: "Use separate path: directories to avoid shared git config"
|
|
77
|
+
code: |
|
|
78
|
+
steps:
|
|
79
|
+
# Sparse checkout into 'config-only/' subdirectory
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
with:
|
|
82
|
+
path: config-only
|
|
83
|
+
sparse-checkout: |
|
|
84
|
+
config/
|
|
85
|
+
|
|
86
|
+
# Full checkout into 'full-repo/' — completely separate git repo config
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
with:
|
|
89
|
+
path: full-repo
|
|
90
|
+
- language: yaml
|
|
91
|
+
label: "Composite action defensive reset (add to composite action's beginning)"
|
|
92
|
+
code: |
|
|
93
|
+
# In your composite action's action.yml — reset sparse before own checkout
|
|
94
|
+
runs:
|
|
95
|
+
using: composite
|
|
96
|
+
steps:
|
|
97
|
+
- name: Reset any inherited sparse-checkout
|
|
98
|
+
shell: bash
|
|
99
|
+
run: |
|
|
100
|
+
if git rev-parse --git-dir > /dev/null 2>&1; then
|
|
101
|
+
git sparse-checkout disable 2>/dev/null || true
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
with:
|
|
106
|
+
ref: ${{ inputs.ref }}
|
|
107
|
+
prevention:
|
|
108
|
+
- "Never assume a subsequent `actions/checkout` step gets a full tree if any earlier step used sparse-checkout."
|
|
109
|
+
- "Add `git sparse-checkout disable` as an explicit step between sparse and full checkouts in the same job."
|
|
110
|
+
- "When writing composite actions that call `actions/checkout`, add a defensive `git sparse-checkout disable` before your checkout step."
|
|
111
|
+
- "Use the `path:` input to isolate checkouts that need different content into separate directories."
|
|
112
|
+
docs:
|
|
113
|
+
- url: "https://github.com/actions/checkout/issues/1498"
|
|
114
|
+
label: "actions/checkout#1498: Sparse checkout persists in composite action"
|
|
115
|
+
- url: "https://github.com/actions/checkout/pull/2034"
|
|
116
|
+
label: "actions/checkout#2034: Fix — disable sparse-checkout on subsequent checkout (open PR)"
|
|
117
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses"
|
|
118
|
+
label: "Workflow syntax: steps.uses"
|
|
119
|
+
- url: "https://github.com/actions/checkout#usage"
|
|
120
|
+
label: "actions/checkout: Usage and sparse-checkout options"
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
id: triggers-003
|
|
2
|
-
title: "Cron Schedule Running Late or Not Running"
|
|
3
|
-
category: triggers
|
|
4
|
-
severity: warning
|
|
5
|
-
tags:
|
|
6
|
-
- cron
|
|
7
|
-
- schedule
|
|
8
|
-
- delay
|
|
9
|
-
- forks
|
|
10
|
-
- best-effort
|
|
11
|
-
patterns:
|
|
12
|
-
- regex: "schedule"
|
|
13
|
-
flags: "i"
|
|
14
|
-
- regex: "Delayed"
|
|
15
|
-
flags: "i"
|
|
16
|
-
- regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
|
|
17
|
-
flags: "i"
|
|
18
|
-
error_messages:
|
|
19
|
-
- "Scheduled workflows can be delayed during periods of high loads of GitHub Actions workflow runs."
|
|
20
|
-
root_cause: |
|
|
21
|
-
GitHub Actions cron is best-effort, not real-time scheduling. During peak usage windows,
|
|
22
|
-
scheduled workflows can start 15 to 45 minutes late. In forks, scheduled workflows are also
|
|
23
|
-
disabled by default, so a copied workflow may never run until someone explicitly enables it.
|
|
24
|
-
|
|
25
|
-
The YAML can be correct and the repository can still see timing drift because the platform
|
|
26
|
-
does not guarantee precise minute-by-minute execution.
|
|
27
|
-
fix: |
|
|
28
|
-
Treat cron timing as approximate. Schedule important automations away from the top of the
|
|
29
|
-
hour, add tolerance for startup drift, and provide a `workflow_dispatch` fallback for jobs
|
|
30
|
-
that sometimes need manual recovery.
|
|
31
|
-
fix_code:
|
|
32
|
-
- language: yaml
|
|
33
|
-
label: "Add jitter-friendly scheduling and manual fallback"
|
|
34
|
-
code: |
|
|
35
|
-
name: Nightly maintenance
|
|
36
|
-
|
|
37
|
-
on:
|
|
38
|
-
schedule:
|
|
39
|
-
- cron: '17 3 * * *'
|
|
40
|
-
workflow_dispatch:
|
|
41
|
-
|
|
42
|
-
jobs:
|
|
43
|
-
maintain:
|
|
44
|
-
runs-on: ubuntu-latest
|
|
45
|
-
steps:
|
|
46
|
-
- uses: actions/checkout@v4
|
|
47
|
-
- run: ./scripts/maintenance.sh
|
|
48
|
-
prevention:
|
|
49
|
-
- "Avoid scheduling critical jobs exactly at minute 0 because that is a common contention window."
|
|
50
|
-
- "Use external monitoring if a schedule must be near-real-time."
|
|
51
|
-
- "Enable scheduled workflows manually after creating a fork or after long inactivity."
|
|
52
|
-
docs:
|
|
53
|
-
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
|
|
54
|
-
label: "schedule event"
|
|
55
|
-
- url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
|
|
56
|
-
label: "Disable and enable a workflow"
|
|
57
|
-
source:
|
|
58
|
-
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
59
|
-
section: "Cron schedules running late"
|
|
1
|
+
id: triggers-003
|
|
2
|
+
title: "Cron Schedule Running Late or Not Running"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- cron
|
|
7
|
+
- schedule
|
|
8
|
+
- delay
|
|
9
|
+
- forks
|
|
10
|
+
- best-effort
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "schedule"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "Delayed"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Scheduled workflows can be delayed during periods of high loads of GitHub Actions workflow runs."
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions cron is best-effort, not real-time scheduling. During peak usage windows,
|
|
22
|
+
scheduled workflows can start 15 to 45 minutes late. In forks, scheduled workflows are also
|
|
23
|
+
disabled by default, so a copied workflow may never run until someone explicitly enables it.
|
|
24
|
+
|
|
25
|
+
The YAML can be correct and the repository can still see timing drift because the platform
|
|
26
|
+
does not guarantee precise minute-by-minute execution.
|
|
27
|
+
fix: |
|
|
28
|
+
Treat cron timing as approximate. Schedule important automations away from the top of the
|
|
29
|
+
hour, add tolerance for startup drift, and provide a `workflow_dispatch` fallback for jobs
|
|
30
|
+
that sometimes need manual recovery.
|
|
31
|
+
fix_code:
|
|
32
|
+
- language: yaml
|
|
33
|
+
label: "Add jitter-friendly scheduling and manual fallback"
|
|
34
|
+
code: |
|
|
35
|
+
name: Nightly maintenance
|
|
36
|
+
|
|
37
|
+
on:
|
|
38
|
+
schedule:
|
|
39
|
+
- cron: '17 3 * * *'
|
|
40
|
+
workflow_dispatch:
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
maintain:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- run: ./scripts/maintenance.sh
|
|
48
|
+
prevention:
|
|
49
|
+
- "Avoid scheduling critical jobs exactly at minute 0 because that is a common contention window."
|
|
50
|
+
- "Use external monitoring if a schedule must be near-real-time."
|
|
51
|
+
- "Enable scheduled workflows manually after creating a fork or after long inactivity."
|
|
52
|
+
docs:
|
|
53
|
+
- url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
|
|
54
|
+
label: "schedule event"
|
|
55
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
|
|
56
|
+
label: "Disable and enable a workflow"
|
|
57
|
+
source:
|
|
58
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
59
|
+
section: "Cron schedules running late"
|