@htekdev/actions-debugger 1.0.125 → 1.0.126

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.
@@ -0,0 +1,146 @@
1
+ id: concurrency-timing-059
2
+ title: 'Skipped downstream job satisfies required status check — PR merges despite upstream job failure'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - needs
7
+ - skipped
8
+ - required-status-check
9
+ - branch-protection
10
+ - dependency
11
+ - silent-merge
12
+ - job-conclusion
13
+ patterns:
14
+ - regex: 'This job was skipped'
15
+ flags: 'i'
16
+ - regex: 'Result: skipped'
17
+ flags: 'i'
18
+ - regex: 'needs\.[a-zA-Z0-9_-]+\.result.*skipped'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "This job was skipped."
22
+ - "Skipping this job because a previous job in the chain was skipped or failed."
23
+ root_cause: |
24
+ GitHub Actions marks a downstream job as `skipped` (not `failure`) when an upstream
25
+ `needs:` dependency fails or is cancelled and the downstream job has no explicit `if:`
26
+ condition to handle that state.
27
+
28
+ Since April 2023, GitHub's branch protection rules treat a `skipped` check conclusion
29
+ as **passing** — equivalent to `success` — to support the common "aggregator job"
30
+ pattern. This means:
31
+
32
+ 1. `build` job fails.
33
+ 2. `test-results` job (which `needs: [build]`) is marked `skipped`.
34
+ 3. Branch protection rule requires `test-results` to pass.
35
+ 4. GitHub sees conclusion = `skipped` → treats it as satisfied → merge is allowed.
36
+
37
+ The PR can be merged even though the `build` step failed. There is no warning or
38
+ error in the UI — the required check shows a green checkmark (or neutral status)
39
+ rather than a red blocking indicator.
40
+
41
+ This behavior is distinct from:
42
+ - `cancel-in-progress` cancelling a required check (conclusion = `cancelled`, which
43
+ blocks merging — a separate issue).
44
+ - Path-filter causing a workflow to never run (check stays "Expected" / pending).
45
+ - The general skipped-needs cascade (which documents that downstream jobs skip, but
46
+ not the branch protection bypass consequence).
47
+ fix: |
48
+ Add an explicit **catch-all aggregator job** that runs whenever any dependency failed
49
+ or was cancelled, and exits with a non-zero code:
50
+
51
+ ```yaml
52
+ ci-gate:
53
+ if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
54
+ needs: [build, test, lint]
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - name: Fail — one or more required jobs did not succeed
58
+ run: |
59
+ echo "Required jobs: ${{ toJSON(needs.*.result) }}"
60
+ exit 1
61
+ ```
62
+
63
+ Set `ci-gate` as the sole required status check in branch protection. This aggregator
64
+ job only runs (and fails) when something upstream fails. When all upstream jobs
65
+ succeed, `ci-gate` is `skipped` → satisfies the required check. When any upstream
66
+ fails, `ci-gate` runs and explicitly fails → blocks the merge.
67
+
68
+ Alternatively, use the pattern that always runs the aggregator:
69
+
70
+ ```yaml
71
+ ci-gate:
72
+ if: always()
73
+ needs: [build, test, lint]
74
+ runs-on: ubuntu-latest
75
+ steps:
76
+ - name: Check all required jobs passed
77
+ run: |
78
+ results='${{ toJSON(needs.*.result) }}'
79
+ if echo "$results" | grep -qE '"failure"|"cancelled"'; then
80
+ echo "One or more jobs failed: $results"
81
+ exit 1
82
+ fi
83
+ echo "All required jobs passed."
84
+ ```
85
+ fix_code:
86
+ - language: yaml
87
+ label: 'Broken — downstream skipped check silently satisfies branch protection'
88
+ code: |
89
+ jobs:
90
+ build:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - run: ./build.sh # Fails
94
+
95
+ test-results:
96
+ needs: [build] # Skipped when build fails
97
+ runs-on: ubuntu-latest
98
+ # ⚠ No if: condition — job is skipped when build fails
99
+ # ⚠ Branch protection requires test-results to "pass"
100
+ # ⚠ skipped = passing in GitHub's branch protection evaluation
101
+ steps:
102
+ - run: ./test.sh
103
+
104
+ - language: yaml
105
+ label: 'Fixed — explicit catch-all aggregator job that fails on upstream failure'
106
+ code: |
107
+ jobs:
108
+ build:
109
+ runs-on: ubuntu-latest
110
+ steps:
111
+ - run: ./build.sh
112
+
113
+ test:
114
+ runs-on: ubuntu-latest
115
+ steps:
116
+ - run: ./test.sh
117
+
118
+ ci-gate:
119
+ if: always()
120
+ needs: [build, test]
121
+ runs-on: ubuntu-latest
122
+ steps:
123
+ - name: All required jobs must succeed
124
+ run: |
125
+ results='${{ toJSON(needs.*.result) }}'
126
+ if echo "$results" | grep -qE '"failure"|"cancelled"'; then
127
+ echo "Pipeline failed: $results"
128
+ exit 1
129
+ fi
130
+ echo "All jobs passed: $results"
131
+
132
+ # In branch protection: require "ci-gate" — not "build" or "test" individually
133
+
134
+ prevention:
135
+ - 'Use a single aggregator job (`ci-gate`) as the required status check instead of individual job names.'
136
+ - 'Always include `if: always()` on the aggregator and explicitly check `needs.*.result` for failure/cancelled.'
137
+ - 'Do NOT rely on `needs:` skipping to propagate failures to branch protection — skipped is treated as passing.'
138
+ - 'After changing which jobs are required status checks, verify by letting a build fail and confirm the PR is blocked.'
139
+ - 'actionlint does not detect this misconfiguration — manual testing is required.'
140
+ docs:
141
+ - url: 'https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks'
142
+ label: 'GitHub Docs: Troubleshooting required status checks'
143
+ - url: 'https://github.com/actions/runner/issues/2566'
144
+ label: 'actions/runner#2566: Skipped jobs satisfy required checks (many reactions)'
145
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-conditions-to-control-job-execution'
146
+ label: 'GitHub Docs: Using conditions to control job execution'
@@ -0,0 +1,144 @@
1
+ id: concurrency-timing-060
2
+ title: 'cancel-in-progress: false still silently drops older pending run when third concurrent dispatch arrives — GitHub enforces a 1-active + 1-pending hard limit per concurrency group'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - concurrency
7
+ - cancel-in-progress
8
+ - pending
9
+ - silent-cancel
10
+ - dispatch
11
+ - queue
12
+ - lost-run
13
+ patterns:
14
+ - regex: 'This run was cancelled'
15
+ flags: 'i'
16
+ - regex: 'cancel-in-progress:\s*false'
17
+ flags: 'i'
18
+ - regex: 'Run was cancelled'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "This run was cancelled."
22
+ - "Run was cancelled."
23
+ root_cause: |
24
+ Setting `cancel-in-progress: false` in a concurrency group does NOT mean "queue all
25
+ runs indefinitely." It means "do not cancel the currently *in-progress* run."
26
+
27
+ GitHub Actions enforces a hard internal limit of **1 in-progress + 1 pending** run
28
+ per concurrency group (when using the default or `cancel-in-progress: false`).
29
+ When a third concurrent run arrives:
30
+
31
+ 1. Run A is **in-progress** (held in slot 1).
32
+ 2. Run B is **pending** (held in slot 2, waiting for Run A to finish).
33
+ 3. Run C arrives → GitHub silently cancels Run B to free slot 2, then queues Run C.
34
+
35
+ The net effect: Run B is lost. Run C is now pending and will eventually execute.
36
+ With `cancel-in-progress: false`, *only Run A is protected* from cancellation — not
37
+ Run B. The arriving run always displaces the previously pending one.
38
+
39
+ This surprises developers who read `cancel-in-progress: false` as "let all runs
40
+ queue and execute in order." The actual semantics are: "don't kill the in-flight
41
+ run, but still replace any queued-but-not-yet-running run."
42
+
43
+ There is no log entry showing Run B was cancelled by Run C's arrival; the cancellation
44
+ shows as "This run was cancelled" with no attribution.
45
+
46
+ To allow more than 1 pending run, use `queue: max` (up to 100 pending slots) — but
47
+ note that `queue: max` and `cancel-in-progress: true` cannot be combined.
48
+ fix: |
49
+ Choose the concurrency strategy that matches your desired behavior:
50
+
51
+ **Option 1 — Allow up to 100 queued runs (strict ordering, no drops):**
52
+ Use `queue: max`. Every run is preserved and executes in arrival order. The 101st
53
+ concurrent run silently cancels the oldest pending run (see concurrency-timing-058
54
+ for the queue:max overflow edge case).
55
+
56
+ **Option 2 — Cancel stale runs, always run the latest (most common for CI):**
57
+ Use `cancel-in-progress: true`. Stale pending runs are cancelled; only the most
58
+ recent push/dispatch runs.
59
+
60
+ **Option 3 — Fine-grained concurrency keys to prevent grouping:**
61
+ Include `github.sha` or `github.run_id` in the group key so each run gets its own
62
+ isolated slot and nothing is ever cancelled or queued against another run.
63
+
64
+ **Option 4 — Accept the default behavior:**
65
+ Understand that `cancel-in-progress: false` means 1-active + 1-pending, and design
66
+ your pipeline around this. For example, if you need every commit tested, use
67
+ `cancel-in-progress: true` so you always test the latest commit, or `queue: max`
68
+ if you need every commit tested in order.
69
+ fix_code:
70
+ - language: yaml
71
+ label: 'Broken — cancel-in-progress: false does NOT queue all runs; 3rd run drops 2nd'
72
+ code: |
73
+ on: push
74
+
75
+ concurrency:
76
+ group: deploy-${{ github.ref }}
77
+ cancel-in-progress: false # ❌ Only protects the in-progress run (slot 1)
78
+ # ❌ Slot 2 (pending) is still replaced when a 3rd run arrives
79
+
80
+ jobs:
81
+ deploy:
82
+ runs-on: ubuntu-latest
83
+ steps:
84
+ - run: ./deploy.sh
85
+
86
+ - language: yaml
87
+ label: 'Fixed — use queue: max to preserve all pending runs (up to 100)'
88
+ code: |
89
+ on: push
90
+
91
+ concurrency:
92
+ group: deploy-${{ github.ref }}
93
+ queue: max # ✅ Up to 100 pending runs preserved, executed in order
94
+ # ✅ No run is silently dropped until the 101st arrives
95
+
96
+ jobs:
97
+ deploy:
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - run: ./deploy.sh
101
+
102
+ - language: yaml
103
+ label: 'Alternative — cancel stale runs, keep only the latest (typical CI pattern)'
104
+ code: |
105
+ on: push
106
+
107
+ concurrency:
108
+ group: ${{ github.workflow }}-${{ github.ref }}
109
+ cancel-in-progress: true # ✅ Latest commit always runs; stale runs cancelled
110
+
111
+ jobs:
112
+ ci:
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - run: ./test.sh
116
+
117
+ - language: yaml
118
+ label: 'Alternative — unique key per run (no cancellation, no queueing)'
119
+ code: |
120
+ on: push
121
+
122
+ concurrency:
123
+ group: ${{ github.workflow }}-${{ github.run_id }} # ✅ Each run is isolated
124
+ cancel-in-progress: false
125
+
126
+ jobs:
127
+ ci:
128
+ runs-on: ubuntu-latest
129
+ steps:
130
+ - run: ./test.sh
131
+
132
+ prevention:
133
+ - 'Read `cancel-in-progress: false` as "protect the running job, not all pending jobs."'
134
+ - 'Use `queue: max` when you need every dispatched run to eventually execute.'
135
+ - '`queue: max` and `cancel-in-progress: true` cannot be combined — validation error.'
136
+ - 'Include `github.sha` or `github.run_id` in the group key to give each run its own isolated slot.'
137
+ - 'Monitor the Actions tab for unexplained cancellations after rapid pushes to confirm you are hitting the 1-pending limit.'
138
+ docs:
139
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs'
140
+ label: 'GitHub Docs: Control the concurrency of workflows and jobs'
141
+ - url: 'https://github.com/orgs/community/discussions/5435'
142
+ label: 'GitHub Community #5435: cancel-in-progress: false still cancels pending runs'
143
+ - url: 'https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency'
144
+ label: 'GitHub Docs: Workflow syntax — concurrency'
@@ -0,0 +1,172 @@
1
+ id: known-unsolved-073
2
+ title: '`timeout-minutes:` does not accept expressions — must be a literal integer; variables, inputs, and secrets are unsupported'
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - timeout-minutes
7
+ - expressions
8
+ - limitation
9
+ - no-dynamic-value
10
+ - job-timeout
11
+ - step-timeout
12
+ - vars-context
13
+ patterns:
14
+ - regex: 'timeout-minutes:\s*\$\{\{'
15
+ flags: 'i'
16
+ - regex: 'timeout-minutes.*vars\.|timeout-minutes.*inputs\.|timeout-minutes.*env\.'
17
+ flags: 'i'
18
+ - regex: 'Unexpected value.*timeout-minutes|timeout-minutes.*invalid'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "The workflow is not valid. .github/workflows/ci.yml (Line: N, Col: M): Unexpected value 'true'"
22
+ - "timeout-minutes: unexpected value"
23
+ - "Expected Integer, String"
24
+ root_cause: |
25
+ The `timeout-minutes:` field at both the **job level** and the **step level** only
26
+ accepts a **literal integer** (e.g., `30`, `120`). GitHub Actions does not evaluate
27
+ expressions (`${{ }}`) in this field.
28
+
29
+ Attempting to use any of the following will either produce a YAML validation error
30
+ or silently fall back to the default timeout (6 hours for jobs, unlimited for steps):
31
+
32
+ - `timeout-minutes: ${{ vars.JOB_TIMEOUT }}` — variables context
33
+ - `timeout-minutes: ${{ inputs.timeout }}` — inputs context
34
+ - `timeout-minutes: ${{ env.TIMEOUT_MINUTES }}` — env context
35
+ - `timeout-minutes: ${{ 30 * 2 }}` — arithmetic expression
36
+ - `timeout-minutes: ${{ matrix.timeout }}` — matrix value
37
+
38
+ This is a long-standing feature request (actions/runner#1242, opened 2021, 500+
39
+ reactions) with no official resolution as of June 2026. GitHub's position is that
40
+ expression support in `timeout-minutes:` is not currently planned.
41
+
42
+ actionlint (>=1.6.x) correctly flags `timeout-minutes: ${{ ... }}` as a type error:
43
+ "type of expression at 'timeout-minutes' must be number but found string type."
44
+ However, some older actionlint versions or CI linting setups may not catch this,
45
+ allowing the invalid value to reach production where it silently uses the default.
46
+
47
+ Common motivation for wanting dynamic timeouts:
48
+ - Different environments (staging vs production) need different timeouts.
49
+ - A reusable workflow caller wants to pass a timeout as an input.
50
+ - Matrix jobs with different test suites need proportional timeouts.
51
+ - Organizational policies want to set timeouts via repository variables.
52
+ fix: |
53
+ There is no native fix — expressions in `timeout-minutes:` are not supported.
54
+
55
+ **Workaround 1 — Hardcode multiple job variants with `if:` conditions:**
56
+ Create separate job definitions for each timeout scenario, each with a hardcoded
57
+ `timeout-minutes:` and an `if:` condition that selects the appropriate one based
58
+ on the context. This is verbose but fully supported.
59
+
60
+ **Workaround 2 — Use a wrapper script with a timeout command:**
61
+ Instead of relying on job-level timeout, implement a timeout inside the step's run
62
+ script using the OS `timeout` command (Linux/macOS) or PowerShell's `Wait-Process`:
63
+ ```yaml
64
+ steps:
65
+ - name: Build with dynamic timeout
66
+ env:
67
+ BUILD_TIMEOUT: ${{ vars.BUILD_TIMEOUT_SECONDS || '1800' }}
68
+ run: timeout "$BUILD_TIMEOUT" ./build.sh
69
+ ```
70
+ This gives dynamic timeout behavior but does NOT release the runner immediately —
71
+ the job continues running (idle) after the script times out until the job-level
72
+ `timeout-minutes:` (hardcoded) fires.
73
+
74
+ **Workaround 3 — Hardcode a generous upper bound:**
75
+ Set `timeout-minutes:` to the maximum acceptable value for any scenario, and rely
76
+ on internal script logic to exit early when needed. Ensures the runner is eventually
77
+ released even in worst-case scenarios.
78
+
79
+ **Workaround 4 — For reusable workflows, hardcode per caller:**
80
+ If a reusable workflow needs caller-specified timeouts, duplicate the job definition
81
+ per timeout tier or create multiple reusable workflows with different timeouts.
82
+ fix_code:
83
+ - language: yaml
84
+ label: 'Does NOT work — expression in timeout-minutes is not supported'
85
+ code: |
86
+ jobs:
87
+ build:
88
+ runs-on: ubuntu-latest
89
+ timeout-minutes: ${{ vars.JOB_TIMEOUT_MINUTES }} # ❌ Expression not supported
90
+ steps:
91
+ - run: ./build.sh
92
+
93
+ # Also does NOT work:
94
+ jobs:
95
+ test:
96
+ runs-on: ubuntu-latest
97
+ timeout-minutes: ${{ inputs.timeout || 30 }} # ❌ inputs not supported here
98
+ steps:
99
+ - run: ./test.sh
100
+
101
+ - language: yaml
102
+ label: 'Workaround — use OS timeout command inside step for dynamic behavior'
103
+ code: |
104
+ jobs:
105
+ build:
106
+ runs-on: ubuntu-latest
107
+ timeout-minutes: 60 # ✅ Hardcoded upper bound (releases runner if script hangs)
108
+ steps:
109
+ - uses: actions/checkout@v4
110
+ - name: Build with dynamic timeout from variable
111
+ env:
112
+ # Variable in seconds: default 1800 (30 min), override via repo var
113
+ BUILD_TIMEOUT: ${{ vars.BUILD_TIMEOUT_SECONDS || '1800' }}
114
+ run: timeout "$BUILD_TIMEOUT" ./build.sh
115
+ # The job-level 60-minute timeout catches runaway cases
116
+
117
+ - language: yaml
118
+ label: 'Workaround — conditional job variants for different timeout requirements'
119
+ code: |
120
+ jobs:
121
+ build-short:
122
+ if: ${{ github.event_name == 'pull_request' }}
123
+ runs-on: ubuntu-latest
124
+ timeout-minutes: 15 # ✅ Short timeout for PR checks
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - run: ./build.sh --quick
128
+
129
+ build-full:
130
+ if: ${{ github.ref == 'refs/heads/main' }}
131
+ runs-on: ubuntu-latest
132
+ timeout-minutes: 60 # ✅ Full timeout for main branch builds
133
+ steps:
134
+ - uses: actions/checkout@v4
135
+ - run: ./build.sh --full
136
+
137
+ - language: yaml
138
+ label: 'Reusable workflow workaround — hardcode tiers, expose as separate workflows'
139
+ code: |
140
+ # .github/workflows/ci-fast.yml — for PRs
141
+ on:
142
+ workflow_call:
143
+ jobs:
144
+ ci:
145
+ runs-on: ubuntu-latest
146
+ timeout-minutes: 15 # ✅ Fast tier
147
+ steps:
148
+ - run: ./ci.sh
149
+
150
+ # .github/workflows/ci-full.yml — for main branch
151
+ on:
152
+ workflow_call:
153
+ jobs:
154
+ ci:
155
+ runs-on: ubuntu-latest
156
+ timeout-minutes: 60 # ✅ Full tier
157
+ steps:
158
+ - run: ./ci.sh
159
+
160
+ prevention:
161
+ - 'Accept that `timeout-minutes:` requires a literal integer — design your timeouts with hardcoded values from the start.'
162
+ - 'Use actionlint in CI to catch `timeout-minutes: ${{ ... }}` during PR review before it reaches production.'
163
+ - 'Set `timeout-minutes:` to a safe upper bound to ensure runners are eventually released even when scripts hang.'
164
+ - 'Use step-level `run: timeout N command` for dynamic per-step timeouts without changing job-level `timeout-minutes:`.'
165
+ - 'Upvote actions/runner#1242 — expression support in timeout-minutes is a high-demand feature request.'
166
+ docs:
167
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes'
168
+ label: 'GitHub Docs: Workflow syntax — jobs.<id>.timeout-minutes (literal integer only)'
169
+ - url: 'https://github.com/actions/runner/issues/1242'
170
+ label: 'actions/runner#1242: Support expressions in timeout-minutes (500+ reactions, open since 2021)'
171
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes'
172
+ label: 'GitHub Docs: Workflow syntax — jobs.<id>.steps[*].timeout-minutes'
@@ -0,0 +1,150 @@
1
+ id: triggers-072
2
+ title: 'Scheduled workflow silently disabled after 60 days of public repository inactivity — distinct from the fork schedule disabling rule'
3
+ category: triggers
4
+ severity: silent-failure
5
+ tags:
6
+ - schedule
7
+ - cron
8
+ - disabled
9
+ - inactivity
10
+ - public-repo
11
+ - silent-disable
12
+ - 60-days
13
+ patterns:
14
+ - regex: 'on:\s*\n\s+schedule:'
15
+ flags: 'im'
16
+ - regex: 'This workflow was disabled'
17
+ flags: 'i'
18
+ - regex: 'Workflow.*disabled.*inactiv'
19
+ flags: 'i'
20
+ error_messages:
21
+ - "This workflow was disabled."
22
+ - "Scheduled workflows are disabled for repositories with no recent activity."
23
+ root_cause: |
24
+ GitHub automatically disables scheduled (`on: schedule:`) workflows in **public
25
+ repositories** that have had no activity for more than **60 days**. "Activity"
26
+ includes pushes, pull requests, issue comments, or other events that generate
27
+ a repository event payload. Pure time-passing without any interaction does not
28
+ reset the timer.
29
+
30
+ The disabling happens silently:
31
+ - No email notification is sent to repository owners or workflow authors.
32
+ - No GitHub notification appears in the repository's notification feed.
33
+ - The scheduled run simply does not appear in the Actions run history.
34
+ - The workflow file is not changed — the `on: schedule:` trigger is still present
35
+ in the YAML, but the schedule is inactive in GitHub's internal scheduler.
36
+
37
+ The Actions tab will show the workflow listed but with a note that it has been
38
+ disabled. The **"Enable workflow"** button must be clicked manually to re-activate it.
39
+
40
+ This behavior is separate from the fork schedule-disabling rule (where ALL
41
+ scheduled workflows are disabled on forked repos regardless of activity). The
42
+ inactivity rule applies to non-fork public repositories.
43
+
44
+ Common scenarios:
45
+ - Maintenance scripts (e.g., weekly dependency updates, cleanup jobs) in repos
46
+ that receive no push activity between runs.
47
+ - Documentation or static-site repos where content is rarely updated but a nightly
48
+ build/deploy workflow is expected to run.
49
+ - Monitoring or alerting workflows in repos with no other CI triggers.
50
+ - Open-source libraries with stable codebases and monthly or quarterly releases.
51
+ fix: |
52
+ **Immediate fix:** Navigate to the repository → Actions tab → find the disabled
53
+ workflow → click "Enable workflow."
54
+
55
+ **Prevent future silent disabling — Option 1 (Recommended): Add a dummy commit
56
+ or workflow_dispatch trigger:**
57
+ Add `workflow_dispatch:` to the workflow so it can be triggered manually and also
58
+ resets the inactivity timer. Then use `gh workflow run` or the UI to manually
59
+ trigger it if needed.
60
+
61
+ **Prevent future silent disabling — Option 2: Artificially keep the repo active:**
62
+ Add a companion workflow that runs on schedule and updates a file (e.g., a
63
+ `last-run.txt` with the current timestamp) via a commit. This creates repository
64
+ activity and resets the 60-day timer. Warning: this generates commit noise.
65
+
66
+ **Prevent future silent disabling — Option 3: Convert to a self-hosted or
67
+ external scheduler:**
68
+ Use an external cron service (e.g., a cron job in a cloud provider) to trigger
69
+ the workflow via `workflow_dispatch` or `repository_dispatch`. External triggers
70
+ count as activity and keep the workflow enabled.
71
+
72
+ **Long-term design:** For workflows that are truly meant to run on a schedule
73
+ without any other repo activity, always add `workflow_dispatch:` as a co-trigger.
74
+ This makes the workflow manually invocable AND provides a path to re-enable it if
75
+ it is ever silently disabled.
76
+ fix_code:
77
+ - language: yaml
78
+ label: 'At-risk — schedule-only workflow in a low-activity public repo'
79
+ code: |
80
+ # ⚠ This workflow will be silently disabled after 60 days with no repo activity
81
+ name: Weekly Dependency Update
82
+
83
+ on:
84
+ schedule:
85
+ - cron: '0 9 * * 1' # Every Monday at 09:00 UTC
86
+
87
+ jobs:
88
+ update:
89
+ runs-on: ubuntu-latest
90
+ steps:
91
+ - uses: actions/checkout@v4
92
+ - run: npm update && npm audit fix
93
+
94
+ - language: yaml
95
+ label: 'Fixed — add workflow_dispatch to keep the workflow manageable and prevent inactivity disable'
96
+ code: |
97
+ name: Weekly Dependency Update
98
+
99
+ on:
100
+ schedule:
101
+ - cron: '0 9 * * 1' # Every Monday at 09:00 UTC
102
+ workflow_dispatch: # ✅ Allows manual trigger; manual runs also reset the inactivity timer
103
+
104
+ jobs:
105
+ update:
106
+ runs-on: ubuntu-latest
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+ - run: npm update && npm audit fix
110
+
111
+ - language: yaml
112
+ label: 'Alternative — heartbeat workflow that commits a timestamp to keep the repo active'
113
+ code: |
114
+ name: Activity Heartbeat (prevents schedule auto-disable)
115
+
116
+ on:
117
+ schedule:
118
+ - cron: '0 0 * * 0' # Every Sunday midnight UTC — reset 60-day timer
119
+ workflow_dispatch:
120
+
121
+ permissions:
122
+ contents: write
123
+
124
+ jobs:
125
+ heartbeat:
126
+ runs-on: ubuntu-latest
127
+ steps:
128
+ - uses: actions/checkout@v4
129
+ - name: Update heartbeat timestamp
130
+ run: |
131
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .github/heartbeat.txt
132
+ git config user.name 'github-actions[bot]'
133
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
134
+ git add .github/heartbeat.txt
135
+ git diff --staged --quiet || git commit -m 'chore: activity heartbeat [skip ci]'
136
+ git push
137
+
138
+ prevention:
139
+ - 'Always add `workflow_dispatch:` alongside `on: schedule:` for any workflow that should run reliably in a low-activity repo.'
140
+ - 'Check the Actions tab after a period of low repository activity to verify scheduled workflows are still enabled.'
141
+ - 'The 60-day inactivity rule applies to non-fork public repositories — private repos are NOT subject to this rule.'
142
+ - 'This is distinct from the fork schedule-disabling rule: forks disable schedules immediately on fork creation, not after inactivity.'
143
+ - 'Set up a GitHub Actions alert or monitoring workflow that notifies you if expected scheduled runs stop appearing.'
144
+ docs:
145
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule'
146
+ label: 'GitHub Docs: Events that trigger workflows — schedule (inactivity disable note)'
147
+ - url: 'https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow'
148
+ label: 'GitHub Docs: Disabling and enabling a workflow'
149
+ - url: 'https://github.com/orgs/community/discussions/134086'
150
+ label: 'GitHub Community #134086: Scheduled workflows not running — inactivity disable reports'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.125",
3
+ "version": "1.0.126",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. CLI + MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",