@htekdev/actions-debugger 1.0.52 → 1.0.53

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,102 @@
1
+ id: concurrency-timing-030
2
+ title: 'github.sha concurrency group key makes every run unique — cancel-in-progress never fires'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - concurrency
7
+ - cancel-in-progress
8
+ - github-sha
9
+ - unique-key
10
+ - workflow-cancellation
11
+ - silent-failure
12
+ patterns:
13
+ - regex: 'group:\s*.*\$\{\{[^}]*github\.sha[^}]*\}\}'
14
+ flags: 'i'
15
+ - regex: 'cancel-in-progress:\s*true'
16
+ flags: 'i'
17
+ error_messages:
18
+ - 'Old workflow runs are not being cancelled despite cancel-in-progress: true'
19
+ - 'All workflow runs queue independently instead of cancelling previous runs'
20
+ - 'Concurrent workflows are running in parallel when they should be cancelled'
21
+ root_cause: |
22
+ github.sha is unique per commit. When used as (any part of) the concurrency group
23
+ key, every workflow run gets a different group name. Because no two runs ever share
24
+ the same group, the cancel-in-progress mechanism has nothing to act on — all runs
25
+ proceed independently and simultaneously.
26
+
27
+ This is the inverse of the intended behavior. Developers add ${{ github.sha }}
28
+ thinking "this will identify runs for the same commit and cancel old ones," but
29
+ since each commit produces a unique SHA, each run is placed in its own group with
30
+ no overlap. The result is effectively no concurrency control at all.
31
+
32
+ Common mistake patterns:
33
+ group: ${{ github.workflow }}-${{ github.sha }} # unique per commit — never cancels
34
+ group: ${{ github.ref }}-${{ github.sha }} # unique per commit — never cancels
35
+ group: deploy-${{ github.sha }} # unique per commit — never cancels
36
+
37
+ The correct key for "cancel older runs on the same branch" is a value that stays
38
+ constant across pushes to the same branch — github.ref, github.head_ref (for PRs),
39
+ or a composite like github.workflow-github.ref.
40
+
41
+ Source: Frequently reported on Stack Overflow [github-actions] tag and GitHub
42
+ Community discussions. GitHub Docs concurrency examples explicitly use github.ref,
43
+ not github.sha.
44
+ fix: |
45
+ Replace github.sha with github.ref (or github.head_ref for pull request workflows)
46
+ in the concurrency group key. Use a composite that stays constant for all pushes
47
+ to the same branch:
48
+
49
+ - For branch workflows: ${{ github.workflow }}-${{ github.ref }}
50
+ - For PR workflows: ${{ github.workflow }}-${{ github.head_ref }}
51
+ - For global uniqueness: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
52
+
53
+ Only use github.sha in the group key when you explicitly want every run to be
54
+ isolated (e.g., no cancellation ever — but then cancel-in-progress: true is meaningless).
55
+ fix_code:
56
+ - language: yaml
57
+ label: 'Wrong: github.sha makes every run unique — cancel-in-progress never fires'
58
+ code: |
59
+ # WRONG: every push creates a new unique group — nothing is ever cancelled
60
+ concurrency:
61
+ group: ${{ github.workflow }}-${{ github.sha }}
62
+ cancel-in-progress: true # dead code — no prior run shares this group
63
+
64
+ jobs:
65
+ build:
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+
70
+ - language: yaml
71
+ label: 'Correct: github.ref stays constant per branch — prior runs are cancelled'
72
+ code: |
73
+ # CORRECT: all pushes to the same branch share one group
74
+ # cancel-in-progress: true cancels any in-flight run when a new push arrives
75
+ concurrency:
76
+ group: ${{ github.workflow }}-${{ github.ref }}
77
+ cancel-in-progress: true
78
+
79
+ jobs:
80
+ build:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - uses: actions/checkout@v4
84
+
85
+ - language: yaml
86
+ label: 'PR-specific: use github.head_ref to scope to the PR branch'
87
+ code: |
88
+ # For pull_request workflows, head_ref is the PR branch name (stable per PR)
89
+ concurrency:
90
+ group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
91
+ cancel-in-progress: true
92
+ prevention:
93
+ - 'Never use github.sha in a concurrency group key when the goal is to cancel old runs — it makes every run unique'
94
+ - 'For branch-scoped cancellation use github.ref; for PR-scoped cancellation use github.head_ref'
95
+ - 'If cancel-in-progress: true is set, verify the group key stays constant across multiple pushes to the same branch'
96
+ - 'Use actionlint to catch unreachable concurrency group patterns'
97
+ - 'Read the GitHub Docs concurrency examples — they all use github.ref, not github.sha'
98
+ docs:
99
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs'
100
+ label: 'GitHub Docs: Controlling concurrency of workflows and jobs'
101
+ - url: 'https://stackoverflow.com/questions/66335225/how-to-cancel-previous-runs-in-the-pr-when-you-push-new-commitschanges'
102
+ label: 'Stack Overflow: How to cancel previous runs when you push new commits'
@@ -0,0 +1,102 @@
1
+ id: concurrency-timing-031
2
+ title: 'cancel-in-progress: false does not create a FIFO queue — only one run can be pending'
3
+ category: concurrency-timing
4
+ severity: warning
5
+ tags:
6
+ - concurrency
7
+ - cancel-in-progress
8
+ - queue
9
+ - pending-run
10
+ - fifo
11
+ - workflow-queuing
12
+ patterns:
13
+ - regex: 'cancel-in-progress:\s*false'
14
+ flags: 'i'
15
+ error_messages:
16
+ - 'Queued workflow run was cancelled unexpectedly even though cancel-in-progress is false'
17
+ - 'Expected runs to queue up but intermediate runs are being dropped'
18
+ - 'Second queued run cancelled when a third run was triggered'
19
+ root_cause: |
20
+ When cancel-in-progress: false is set, many developers expect GitHub Actions to
21
+ maintain a FIFO queue of pending runs: Run 1 active → Run 2 queued → Run 3 waits
22
+ behind Run 2. This is not how it works.
23
+
24
+ GitHub Actions maintains at most ONE pending (queued) run per concurrency group.
25
+ The behavior with cancel-in-progress: false is:
26
+
27
+ 1. Run 1: active (running)
28
+ 2. Run 2 arrives: Run 2 enters pending state
29
+ 3. Run 3 arrives: Run 2 is CANCELLED, Run 3 becomes the new pending run
30
+ 4. Run 1 completes: Run 3 (latest) starts — Run 2 was silently dropped
31
+
32
+ This means: with three or more rapid commits, only the first (currently running)
33
+ and the last (most recently pushed) will ever execute. All intermediate runs are
34
+ cancelled and lost — even though cancel-in-progress is explicitly false.
35
+
36
+ This behavior is documented in GitHub Docs ("only the latest queued run will
37
+ start") but is widely misunderstood. The cancel-in-progress: false setting only
38
+ prevents cancelling the ACTIVE run — it does not prevent intermediate pending
39
+ runs from being replaced by newer ones.
40
+
41
+ Source: GitHub Docs concurrency documentation. Discussed in GitHub Community
42
+ discussions and multiple Stack Overflow questions about "runs being unexpectedly
43
+ cancelled with cancel-in-progress: false".
44
+ fix: |
45
+ There is no built-in FIFO queue in GitHub Actions concurrency. To process every
46
+ run without dropping intermediates:
47
+
48
+ 1. For pull requests: use GitHub's merge queue — it processes PRs sequentially
49
+ 2. For deployments: use a separate queuing action (e.g., softprops/turnstyle) to
50
+ serialize runs without skipping intermediates
51
+ 3. For simple "don't cancel active, run latest after": accept that cancel-in-progress:
52
+ false means intermediates are dropped and design accordingly
53
+ 4. If every commit must be processed: remove the concurrency block entirely and
54
+ accept parallel runs, or implement idempotent jobs that can run concurrently
55
+ fix_code:
56
+ - language: yaml
57
+ label: 'Misunderstood: cancel-in-progress: false does NOT queue all runs'
58
+ code: |
59
+ # MISUNDERSTOOD: developers expect runs 1, 2, 3 to all execute sequentially
60
+ # ACTUAL behavior: run 2 is cancelled when run 3 arrives
61
+ concurrency:
62
+ group: ${{ github.workflow }}-${{ github.ref }}
63
+ cancel-in-progress: false # protects ACTIVE run only; pending run is replaced
64
+
65
+ jobs:
66
+ deploy:
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ - run: echo "Deploying..."
71
+
72
+ - language: yaml
73
+ label: 'Explicit: accept one-pending semantics and design for idempotent jobs'
74
+ code: |
75
+ # If intermediate commits can be skipped (e.g., deploy only latest):
76
+ # cancel-in-progress: false ensures the active deploy finishes, then
77
+ # only the LATEST pending commit deploys next (intermediates dropped)
78
+ concurrency:
79
+ group: deploy-${{ github.ref }}
80
+ cancel-in-progress: false
81
+
82
+ jobs:
83
+ deploy:
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@v4
87
+ - name: Deploy latest commit
88
+ run: |
89
+ echo "Deploying ${{ github.sha }}"
90
+ # Idempotent: deploying latest SHA is always safe
91
+ prevention:
92
+ - 'Understand that cancel-in-progress: false only protects the active run — the pending slot still holds at most one run'
93
+ - 'Do not rely on concurrency groups for FIFO queuing — they are a deduplication mechanism, not a queue'
94
+ - 'For sequential processing of every commit, use merge queues or external serialization tooling'
95
+ - 'If every commit must be processed, either remove the concurrency block or use an event-based queue (e.g., repository_dispatch)'
96
+ docs:
97
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs'
98
+ label: 'GitHub Docs: Controlling concurrency of workflows and jobs'
99
+ - url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue'
100
+ label: 'GitHub Docs: Managing a merge queue'
101
+ - url: 'https://github.com/softprops/turnstyle'
102
+ label: 'softprops/turnstyle: Wait for in-progress workflows'
@@ -0,0 +1,147 @@
1
+ id: concurrency-timing-032
2
+ title: 'Reusable workflow concurrency: is not inherited from caller — parallel instances can run simultaneously'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - concurrency
7
+ - reusable-workflow
8
+ - workflow-call
9
+ - parallel-runs
10
+ - concurrency-inheritance
11
+ - deployment
12
+ patterns:
13
+ - regex: 'jobs\.\w+\.uses:\s*'
14
+ flags: 'i'
15
+ - regex: '^concurrency:'
16
+ flags: 'im'
17
+ error_messages:
18
+ - 'Multiple simultaneous deployments triggered despite concurrency group on caller workflow'
19
+ - 'Reusable workflow running in parallel when caller has cancel-in-progress: true'
20
+ - 'Concurrency group on calling workflow does not prevent parallel reusable workflow runs'
21
+ root_cause: |
22
+ When a workflow defines concurrency: at the workflow level and calls a reusable
23
+ workflow via jobs.<id>.uses:, the concurrency configuration is NOT propagated
24
+ to the called (callee) workflow. The reusable workflow runs with its own
25
+ independent concurrency context.
26
+
27
+ This means:
28
+ - Workflow A has concurrency: group: deploy-${{ github.ref }}
29
+ - Workflow A calls Reusable-Deploy.yml via jobs.deploy.uses
30
+ - If two branches both trigger Workflow A simultaneously, each instance of Workflow A
31
+ serializes itself via its own concurrency group — but both instances of
32
+ Reusable-Deploy.yml run in parallel with no concurrency restriction
33
+
34
+ The concurrency group defined in the CALLER protects the caller's run from
35
+ duplicate callers on the same ref. It does NOT create any concurrency group for
36
+ the callee. The callee executes as a distinct workflow run with no inherited
37
+ concurrency settings.
38
+
39
+ This is especially problematic for shared deployment reusable workflows:
40
+ - Multiple repos or branches can call the same reusable deployment workflow
41
+ - Each caller serializes itself per its own ref, but the shared callee runs
42
+ multiple parallel instances simultaneously
43
+ - Parallel deploys to the same environment proceed without conflict detection
44
+
45
+ Source: GitHub Docs explicitly states concurrency groups are scoped to the
46
+ specific workflow run. GitHub Community confirmed expected behavior in multiple
47
+ discussions about parallel reusable workflow instances.
48
+ fix: |
49
+ Define concurrency: inside the reusable workflow itself, or accept a
50
+ concurrency-key input and use it within the callee:
51
+
52
+ Option 1 — Static group in reusable workflow:
53
+ Add concurrency: group: deploy-reusable to the callee workflow. This serializes
54
+ ALL calls to that reusable workflow globally.
55
+
56
+ Option 2 — Dynamic group via input:
57
+ Add a concurrency-key input to the reusable workflow. The caller passes
58
+ its own group key. The callee uses it: group: ${{ inputs.concurrency_key }}.
59
+ This gives callers control over which callee instances serialize against each other.
60
+
61
+ Option 3 — Environment-level protection:
62
+ Use GitHub deployment environments with required reviewers. Only one deployment
63
+ to an environment can be in progress at a time (pending review blocks others).
64
+ fix_code:
65
+ - language: yaml
66
+ label: 'Caller: concurrency here only protects the caller — not the callee'
67
+ code: |
68
+ # caller-workflow.yml
69
+ # This concurrency group only prevents duplicate CALLERS for the same ref.
70
+ # It does NOT propagate to the reusable workflow.
71
+ concurrency:
72
+ group: ${{ github.workflow }}-${{ github.ref }}
73
+ cancel-in-progress: true
74
+
75
+ jobs:
76
+ deploy:
77
+ uses: ./.github/workflows/reusable-deploy.yml
78
+ with:
79
+ environment: production
80
+ secrets: inherit
81
+
82
+ - language: yaml
83
+ label: 'Fix option 1: define concurrency inside the reusable workflow'
84
+ code: |
85
+ # reusable-deploy.yml
86
+ on:
87
+ workflow_call:
88
+ inputs:
89
+ environment:
90
+ type: string
91
+ required: true
92
+
93
+ # Add concurrency here to serialize all calls to this reusable workflow
94
+ concurrency:
95
+ group: reusable-deploy-${{ inputs.environment }}
96
+ cancel-in-progress: false # let active deploy finish; queue latest
97
+
98
+ jobs:
99
+ deploy:
100
+ runs-on: ubuntu-latest
101
+ environment: ${{ inputs.environment }}
102
+ steps:
103
+ - uses: actions/checkout@v4
104
+ - run: echo "Deploying to ${{ inputs.environment }}"
105
+
106
+ - language: yaml
107
+ label: 'Fix option 2: caller passes concurrency key as input'
108
+ code: |
109
+ # reusable-deploy.yml — accepts caller-controlled concurrency key
110
+ on:
111
+ workflow_call:
112
+ inputs:
113
+ concurrency_key:
114
+ type: string
115
+ required: false
116
+ default: 'reusable-deploy'
117
+
118
+ concurrency:
119
+ group: ${{ inputs.concurrency_key }}
120
+ cancel-in-progress: false
121
+
122
+ jobs:
123
+ deploy:
124
+ runs-on: ubuntu-latest
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - run: echo "Deploying..."
128
+
129
+ ---
130
+ # caller-workflow.yml — passes its own ref as the key
131
+ jobs:
132
+ deploy:
133
+ uses: ./.github/workflows/reusable-deploy.yml
134
+ with:
135
+ concurrency_key: deploy-${{ github.ref }}
136
+ prevention:
137
+ - 'Never assume the caller''s concurrency: group propagates to called reusable workflows — it does not'
138
+ - 'Define concurrency: inside every reusable workflow that manages shared resources (deployments, releases, package publishes)'
139
+ - 'Use deployment environments with required reviewers as an additional serialization layer for production deploys'
140
+ - 'Test reusable workflows by triggering two parallel calls and verifying only one runs at a time'
141
+ docs:
142
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs'
143
+ label: 'GitHub Docs: Controlling concurrency of workflows and jobs'
144
+ - url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows'
145
+ label: 'GitHub Docs: Reusing workflows'
146
+ - url: 'https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment'
147
+ label: 'GitHub Docs: Managing environments for deployment'
@@ -0,0 +1,105 @@
1
+ id: yaml-syntax-037
2
+ title: 'Step cannot have both uses: and run: — mutually exclusive keys cause workflow validation failure'
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - yaml-syntax
7
+ - uses
8
+ - run
9
+ - step-definition
10
+ - workflow-validation
11
+ - mutually-exclusive
12
+ patterns:
13
+ - regex: 'uses:\s*.+\n\s+run:'
14
+ flags: 'im'
15
+ - regex: 'run:\s*.+\n\s+uses:'
16
+ flags: 'im'
17
+ error_messages:
18
+ - "A step must define one of ['run', 'uses']"
19
+ - 'Unexpected value ''run'''
20
+ - 'Workflow file is not valid: .github/workflows/ci.yml (Line: N, Col: M): Unexpected value ''run'''
21
+ - "A step must not define both 'uses' and 'run'"
22
+ root_cause: |
23
+ A GitHub Actions workflow step is either an action reference (uses:) or a
24
+ shell command (run:), but never both simultaneously. These two keys are mutually
25
+ exclusive by design: uses: runs a pre-built action with its own entry point, while
26
+ run: executes arbitrary shell commands in the runner's default shell.
27
+
28
+ Common scenarios that produce this error:
29
+
30
+ 1. Adding a quick debug command to an existing action step:
31
+ - uses: actions/checkout@v4
32
+ run: echo "debug" # INVALID — cannot add run: to a uses: step
33
+
34
+ 2. Copy-paste error merging two separate steps into one:
35
+ - name: Setup and verify
36
+ uses: actions/setup-node@v4
37
+ run: node --version # INVALID — should be two separate steps
38
+
39
+ 3. Attempting to run commands after an action in a single step:
40
+ - uses: docker/build-push-action@v6
41
+ with:
42
+ push: true
43
+ run: docker image ls # INVALID
44
+
45
+ The workflow is rejected at parse time with a validation error before any
46
+ job runs. The exact error message varies by GitHub Actions version but always
47
+ indicates that the step cannot define both keys.
48
+ fix: |
49
+ Split the step into two separate steps: one with uses: for the action, and
50
+ a new step with run: for the shell command. Each step in a job must be
51
+ exclusively one type.
52
+ fix_code:
53
+ - language: yaml
54
+ label: 'Wrong: uses: and run: in the same step — workflow validation fails'
55
+ code: |
56
+ jobs:
57
+ build:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - name: Checkout and show files # INVALID
61
+ uses: actions/checkout@v4
62
+ run: ls -la # INVALID: cannot add run: to uses: step
63
+
64
+ - language: yaml
65
+ label: 'Correct: split into two separate steps'
66
+ code: |
67
+ jobs:
68
+ build:
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - name: Checkout
72
+ uses: actions/checkout@v4 # action step
73
+
74
+ - name: Show files # separate shell step
75
+ run: ls -la
76
+
77
+ - language: yaml
78
+ label: 'Common mistake: adding debug run: to an action step'
79
+ code: |
80
+ jobs:
81
+ build:
82
+ runs-on: ubuntu-latest
83
+ steps:
84
+ - uses: actions/setup-node@v4
85
+ with:
86
+ node-version: '20'
87
+ # WRONG: adding run: here is invalid
88
+ # run: node --version
89
+
90
+ # CORRECT: add a separate step after the action
91
+ - name: Verify Node version
92
+ run: node --version
93
+ prevention:
94
+ - 'Remember: every step is either uses: (action) OR run: (shell) — never both'
95
+ - 'To run commands after an action, always create a new step with its own name: and run:'
96
+ - 'Use actionlint locally to catch uses:/run: conflicts before pushing'
97
+ - 'Use the GitHub Actions VS Code extension — it highlights mutually exclusive keys in the editor'
98
+ - 'When copying steps from documentation, verify each step has only one of uses: or run:'
99
+ docs:
100
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses'
101
+ label: 'GitHub Docs: jobs.<job_id>.steps[*].uses'
102
+ - url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun'
103
+ label: 'GitHub Docs: jobs.<job_id>.steps[*].run'
104
+ - url: 'https://rhysd.github.io/actionlint/'
105
+ label: 'actionlint: Static checker for GitHub Actions workflow files'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
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",