@htekdev/actions-debugger 1.0.83 → 1.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/errors/caching-artifacts/docker-buildx-gha-cache-mode-min-partial-layers.yml +91 -0
- package/errors/concurrency-timing/event-number-empty-on-push-collapses-concurrency.yml +90 -0
- package/errors/concurrency-timing/schedule-cron-no-concurrency-run-overlap.yml +87 -0
- package/errors/known-unsolved/workflow-dispatch-api-no-run-id-in-response.yml +101 -0
- package/errors/permissions-auth/github-token-no-cross-org-private-action-access.yml +95 -0
- package/errors/silent-failures/skipped-step-job-output-silently-empty.yml +119 -0
- package/errors/triggers/schedule-paths-filter-silently-ignored.yml +111 -0
- package/errors/yaml-syntax/step-output-boolean-string-coercion-if-condition.yml +100 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
id: caching-artifacts-049
|
|
2
|
+
title: 'Docker buildx GHA cache mode:min only caches final layer — rebuilds remain slow'
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- docker
|
|
7
|
+
- buildx
|
|
8
|
+
- gha-cache
|
|
9
|
+
- layer-cache
|
|
10
|
+
- mode-min
|
|
11
|
+
- silent-failure
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'cache-to.*type=gha'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'importing\s+cache\s+manifest\s+from\s+ghcr\.io'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'exporting\s+cache\s+.*\s+type=gha'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- 'cache-to: type=gha'
|
|
21
|
+
- 'exporting cache to GitHub Actions Cache Service'
|
|
22
|
+
- 'CACHED [stage 2/4]'
|
|
23
|
+
root_cause: |
|
|
24
|
+
docker/build-push-action uses cache-to: type=gha with mode=min by default when
|
|
25
|
+
no explicit mode is specified. In min mode, BuildKit exports ONLY the layers of
|
|
26
|
+
the final image (the last build stage), discarding all intermediate layer cache
|
|
27
|
+
data. This means:
|
|
28
|
+
|
|
29
|
+
- Multi-stage Dockerfiles get no benefit from the GHA cache for base/dependency
|
|
30
|
+
stages (the expensive ones) — only the final artifact stage is cached.
|
|
31
|
+
- Subsequent runs re-execute all intermediate stages from scratch while the
|
|
32
|
+
cache appears to "work" (export/import steps succeed in the logs).
|
|
33
|
+
- Developers see 80-90% cache miss rates and slow builds despite believing the
|
|
34
|
+
GHA cache is active.
|
|
35
|
+
|
|
36
|
+
The silent aspect: the workflow logs show cache export and import succeeding,
|
|
37
|
+
and BuildKit reports "CACHED" for layers it finds, with no warning that intermediate
|
|
38
|
+
stages were excluded from the cache.
|
|
39
|
+
fix: |
|
|
40
|
+
Explicitly set mode=max on the cache-to option. This instructs BuildKit to export
|
|
41
|
+
ALL layer metadata — including every intermediate build stage — into the GHA cache.
|
|
42
|
+
Combined with cache-from: type=gha, all previously-built stages will be restored
|
|
43
|
+
on the next run.
|
|
44
|
+
|
|
45
|
+
Note: mode=max requires more GHA cache storage (subject to the 10 GB repository
|
|
46
|
+
limit). Use separate cache scopes per Dockerfile or build target to prevent
|
|
47
|
+
cross-contamination and LRU eviction pressure.
|
|
48
|
+
fix_code:
|
|
49
|
+
- language: yaml
|
|
50
|
+
label: 'Set mode=max for full layer caching in multi-stage builds'
|
|
51
|
+
code: |
|
|
52
|
+
- name: Build and push
|
|
53
|
+
uses: docker/build-push-action@v6
|
|
54
|
+
with:
|
|
55
|
+
context: .
|
|
56
|
+
push: true
|
|
57
|
+
tags: ghcr.io/${{ github.repository }}:latest
|
|
58
|
+
cache-from: type=gha,scope=main
|
|
59
|
+
cache-to: type=gha,scope=main,mode=max
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: 'Use separate scopes per Dockerfile to isolate caches'
|
|
62
|
+
code: |
|
|
63
|
+
- name: Build API image
|
|
64
|
+
uses: docker/build-push-action@v6
|
|
65
|
+
with:
|
|
66
|
+
context: ./api
|
|
67
|
+
push: true
|
|
68
|
+
tags: ghcr.io/${{ github.repository }}/api:latest
|
|
69
|
+
cache-from: type=gha,scope=api
|
|
70
|
+
cache-to: type=gha,scope=api,mode=max
|
|
71
|
+
|
|
72
|
+
- name: Build Frontend image
|
|
73
|
+
uses: docker/build-push-action@v6
|
|
74
|
+
with:
|
|
75
|
+
context: ./frontend
|
|
76
|
+
push: true
|
|
77
|
+
tags: ghcr.io/${{ github.repository }}/frontend:latest
|
|
78
|
+
cache-from: type=gha,scope=frontend
|
|
79
|
+
cache-to: type=gha,scope=frontend,mode=max
|
|
80
|
+
prevention:
|
|
81
|
+
- 'Always explicitly set mode=max in cache-to: type=gha for multi-stage Dockerfiles'
|
|
82
|
+
- 'Use the scope= parameter to isolate caches per Dockerfile or build target'
|
|
83
|
+
- 'Monitor cache size with the GitHub Actions Cache API if approaching the 10 GB repository limit'
|
|
84
|
+
- 'Verify cache effectiveness by checking that intermediate build stages show CACHED in buildx output'
|
|
85
|
+
docs:
|
|
86
|
+
- url: 'https://docs.docker.com/build/cache/backends/gha/'
|
|
87
|
+
label: 'Docker BuildKit — GitHub Actions cache backend'
|
|
88
|
+
- url: 'https://github.com/docker/build-push-action#cache'
|
|
89
|
+
label: 'docker/build-push-action — Cache inputs'
|
|
90
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy'
|
|
91
|
+
label: 'GitHub Actions — Cache usage limits and eviction policy'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
id: concurrency-timing-042
|
|
2
|
+
title: "github.event.number empty on push events collapses concurrency group across all branches"
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- concurrency
|
|
7
|
+
- github-event-number
|
|
8
|
+
- push-event
|
|
9
|
+
- cancel-in-progress
|
|
10
|
+
- cross-branch
|
|
11
|
+
- pull-request
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'github\.event\.number\b'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Run was cancelled due to concurrency group collision between push events on different branches"
|
|
17
|
+
- "Unexpected cancellation of push workflow by unrelated branch push"
|
|
18
|
+
root_cause: |
|
|
19
|
+
`github.event.number` contains the pull request or issue number for events that have one
|
|
20
|
+
(e.g., `pull_request`, `pull_request_review`, `issue_comment`). On `push` events,
|
|
21
|
+
`github.event.number` is undefined and evaluates to an empty string in expressions.
|
|
22
|
+
|
|
23
|
+
When a workflow is triggered by both `push` and `pull_request` events, and the
|
|
24
|
+
concurrency group key uses `github.event.number`, all push-triggered runs share a
|
|
25
|
+
single concurrency group key (the suffix becomes empty):
|
|
26
|
+
|
|
27
|
+
group: "${{ github.workflow }}-${{ github.event.number }}"
|
|
28
|
+
# PR run: "CI-123" (unique per PR number)
|
|
29
|
+
# Push run: "CI-" (ALL branch pushes share this single group!)
|
|
30
|
+
|
|
31
|
+
With `cancel-in-progress: true`, pushing to any branch cancels all other in-progress
|
|
32
|
+
push runs — even if they are on completely different branches. Developers intend to
|
|
33
|
+
serialize CI per PR but instead unintentionally cancel unrelated branch pushes.
|
|
34
|
+
fix: |
|
|
35
|
+
Use the `||` operator to fall back to `github.ref` when `github.event.number` is empty.
|
|
36
|
+
`github.ref` is always populated (e.g., `refs/heads/main`, `refs/pull/123/merge`) and
|
|
37
|
+
produces a unique key per branch or PR.
|
|
38
|
+
|
|
39
|
+
Alternatively, use `github.event.number || github.sha` if you want push runs to each
|
|
40
|
+
be independent (no cancellation between pushes) while still serializing per PR.
|
|
41
|
+
fix_code:
|
|
42
|
+
- language: yaml
|
|
43
|
+
label: "Wrong — all push runs share the same concurrency group key"
|
|
44
|
+
code: |
|
|
45
|
+
on:
|
|
46
|
+
push:
|
|
47
|
+
branches: [main, 'feature/**']
|
|
48
|
+
pull_request:
|
|
49
|
+
|
|
50
|
+
concurrency:
|
|
51
|
+
group: ${{ github.workflow }}-${{ github.event.number }}
|
|
52
|
+
# On push: group = "CI-" (empty number → all pushes share one group)
|
|
53
|
+
# On PR: group = "CI-123" (unique per PR)
|
|
54
|
+
cancel-in-progress: true
|
|
55
|
+
- language: yaml
|
|
56
|
+
label: "Correct — fall back to github.ref so push runs are keyed per branch"
|
|
57
|
+
code: |
|
|
58
|
+
on:
|
|
59
|
+
push:
|
|
60
|
+
branches: [main, 'feature/**']
|
|
61
|
+
pull_request:
|
|
62
|
+
|
|
63
|
+
concurrency:
|
|
64
|
+
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
|
|
65
|
+
# On push: group = "CI-refs/heads/main" (unique per branch)
|
|
66
|
+
# On PR: group = "CI-123" (unique per PR number)
|
|
67
|
+
cancel-in-progress: true
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Alternative — use github.ref for both triggers (no per-PR serialization)"
|
|
70
|
+
code: |
|
|
71
|
+
on:
|
|
72
|
+
push:
|
|
73
|
+
branches: [main, 'feature/**']
|
|
74
|
+
pull_request:
|
|
75
|
+
|
|
76
|
+
concurrency:
|
|
77
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
78
|
+
# On push: group = "CI-refs/heads/main"
|
|
79
|
+
# On PR: group = "CI-refs/pull/123/merge"
|
|
80
|
+
cancel-in-progress: true
|
|
81
|
+
prevention:
|
|
82
|
+
- "Always test concurrency group keys against all event types in the on: block"
|
|
83
|
+
- "Use github.event.number || github.ref as a safe pattern for mixed push/PR workflows"
|
|
84
|
+
- "Check that github.event.number is not undefined by printing it in a debug step when first setting up concurrency"
|
|
85
|
+
- "Prefer github.ref or github.ref_name as the base concurrency discriminator for branch-level serialization"
|
|
86
|
+
docs:
|
|
87
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idconcurrency"
|
|
88
|
+
label: "Workflow syntax: concurrency"
|
|
89
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context"
|
|
90
|
+
label: "GitHub context — github.event.number availability by event type"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
id: concurrency-timing-043
|
|
2
|
+
title: 'Scheduled cron jobs overlap when no concurrency group is configured'
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- cron
|
|
8
|
+
- overlap
|
|
9
|
+
- race-condition
|
|
10
|
+
- concurrency
|
|
11
|
+
- data-corruption
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on:\s*schedule'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'triggered by:\s*schedule'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'Multiple workflow runs triggered by schedule are running concurrently'
|
|
19
|
+
- 'triggered by: schedule'
|
|
20
|
+
root_cause: |
|
|
21
|
+
When a scheduled workflow has no concurrency group configured, GitHub Actions
|
|
22
|
+
starts a new run at each scheduled time regardless of whether a previous run is
|
|
23
|
+
still in progress. Two conditions make this especially common:
|
|
24
|
+
|
|
25
|
+
1. The job takes longer than the cron interval (e.g., a 10-minute job on a
|
|
26
|
+
every-5-minute schedule).
|
|
27
|
+
2. GitHub's scheduler fires queued runs in rapid succession after a platform
|
|
28
|
+
outage or maintenance window, catching up on missed intervals.
|
|
29
|
+
|
|
30
|
+
Both scenarios result in multiple simultaneous runs sharing external resources—
|
|
31
|
+
databases, S3 buckets, deployment targets, or branch state—causing race
|
|
32
|
+
conditions, duplicate data writes, and data corruption. No error is surfaced
|
|
33
|
+
in the workflow logs; the runs both appear green while the downstream system
|
|
34
|
+
silently degrades.
|
|
35
|
+
fix: |
|
|
36
|
+
Add a concurrency group to the scheduled workflow. The correct cancel-in-progress
|
|
37
|
+
value depends on whether the job is idempotent:
|
|
38
|
+
|
|
39
|
+
- Non-idempotent jobs (database migrations, state mutations): set
|
|
40
|
+
cancel-in-progress: false to queue runs sequentially.
|
|
41
|
+
- Idempotent jobs (report generation, cache refreshes): set
|
|
42
|
+
cancel-in-progress: true to drop stale queued runs and keep only the latest.
|
|
43
|
+
fix_code:
|
|
44
|
+
- language: yaml
|
|
45
|
+
label: 'Queue scheduled runs for non-idempotent jobs (e.g., database migrations)'
|
|
46
|
+
code: |
|
|
47
|
+
on:
|
|
48
|
+
schedule:
|
|
49
|
+
- cron: '*/5 * * * *'
|
|
50
|
+
|
|
51
|
+
concurrency:
|
|
52
|
+
group: ${{ github.workflow }}-scheduled
|
|
53
|
+
cancel-in-progress: false
|
|
54
|
+
|
|
55
|
+
jobs:
|
|
56
|
+
migrate:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- run: ./scripts/db-migrate.sh
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: 'Cancel stale runs for idempotent jobs (e.g., report generation)'
|
|
63
|
+
code: |
|
|
64
|
+
on:
|
|
65
|
+
schedule:
|
|
66
|
+
- cron: '*/5 * * * *'
|
|
67
|
+
|
|
68
|
+
concurrency:
|
|
69
|
+
group: ${{ github.workflow }}-scheduled
|
|
70
|
+
cancel-in-progress: true
|
|
71
|
+
|
|
72
|
+
jobs:
|
|
73
|
+
report:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- run: ./scripts/generate-report.sh
|
|
78
|
+
prevention:
|
|
79
|
+
- 'Add a concurrency group to every workflow with an on.schedule trigger'
|
|
80
|
+
- 'For jobs longer than the cron interval, always use cancel-in-progress: false to serialize execution'
|
|
81
|
+
- 'Monitor the Actions tab for multiple simultaneous scheduled runs as an early warning sign'
|
|
82
|
+
- 'Use actionlint to audit workflows that have on.schedule but no concurrency group'
|
|
83
|
+
docs:
|
|
84
|
+
- url: 'https://docs.github.com/en/actions/using-jobs/using-concurrency'
|
|
85
|
+
label: 'GitHub Actions — Using concurrency'
|
|
86
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule'
|
|
87
|
+
label: 'GitHub Actions — Schedule event'
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
id: known-unsolved-050
|
|
2
|
+
title: "workflow_dispatch REST API dispatch returns 204 No Content — triggered run_id unavailable"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- workflow_dispatch
|
|
7
|
+
- rest-api
|
|
8
|
+
- run-id
|
|
9
|
+
- polling
|
|
10
|
+
- automation
|
|
11
|
+
- cd-pipeline
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'POST.*dispatches.*204|dispatches.*No Content'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "204 No Content"
|
|
17
|
+
- "Unable to correlate triggered workflow run"
|
|
18
|
+
- "no run_id in dispatch response"
|
|
19
|
+
root_cause: |
|
|
20
|
+
The GitHub Actions REST API endpoint
|
|
21
|
+
`POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches`
|
|
22
|
+
always returns HTTP 204 No Content on success — there is no run_id or any identifier
|
|
23
|
+
in the response body. GitHub creates the workflow run asynchronously after the API call
|
|
24
|
+
returns, so the run may not exist in the system for several seconds. There is no built-in
|
|
25
|
+
way to correlate a `workflow_dispatch` API call to the resulting run_id.
|
|
26
|
+
|
|
27
|
+
This is a persistent limitation reported since 2020 on the GitHub feedback forum with
|
|
28
|
+
thousands of upvotes. Developers building CD pipelines that need to trigger a workflow
|
|
29
|
+
and then monitor or gate on its result are forced to use fragile polling heuristics.
|
|
30
|
+
fix: |
|
|
31
|
+
No direct fix — this is a GitHub platform limitation. Use one of these workarounds:
|
|
32
|
+
|
|
33
|
+
1. **Inject a correlation ID via inputs**: Pass a unique value (e.g., UUID) as a
|
|
34
|
+
`workflow_dispatch` input. After dispatch, poll `GET /repos/.../actions/runs?event=workflow_dispatch&branch=<branch>`
|
|
35
|
+
and match runs whose `inputs` contain the correlation ID.
|
|
36
|
+
|
|
37
|
+
2. **Use workflow_run event callback**: Design the triggered workflow to complete and
|
|
38
|
+
then fire a `workflow_run` event that your orchestrating workflow listens for.
|
|
39
|
+
|
|
40
|
+
3. **Switch to repository_dispatch**: `repository_dispatch` with a unique `client_payload`
|
|
41
|
+
field lets you include a correlation ID. The triggered workflow can write it to an artifact
|
|
42
|
+
or output for later retrieval.
|
|
43
|
+
|
|
44
|
+
4. **Third-party action**: Use `peter-evans/workflow-dispatch@v3` which implements polling
|
|
45
|
+
heuristics to approximate the run_id after dispatch.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: "Inject correlation ID via workflow_dispatch input — then poll to find run"
|
|
49
|
+
code: |
|
|
50
|
+
# Triggering workflow or script passes a unique correlation ID:
|
|
51
|
+
# POST /repos/{owner}/{repo}/actions/workflows/deploy.yml/dispatches
|
|
52
|
+
# { "ref": "main", "inputs": { "correlation_id": "abc-123-uuid" } }
|
|
53
|
+
|
|
54
|
+
# Triggered workflow (deploy.yml) writes ID to output artifact:
|
|
55
|
+
on:
|
|
56
|
+
workflow_dispatch:
|
|
57
|
+
inputs:
|
|
58
|
+
correlation_id:
|
|
59
|
+
description: 'Unique ID for run correlation'
|
|
60
|
+
required: false
|
|
61
|
+
default: ''
|
|
62
|
+
|
|
63
|
+
jobs:
|
|
64
|
+
deploy:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- run: echo "run_id=${{ github.run_id }}" >> correlation.txt
|
|
68
|
+
- uses: actions/upload-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: correlation-${{ inputs.correlation_id }}
|
|
71
|
+
path: correlation.txt
|
|
72
|
+
- language: yaml
|
|
73
|
+
label: "Use repository_dispatch with client_payload for reliable correlation"
|
|
74
|
+
code: |
|
|
75
|
+
# Trigger with unique client_payload via API:
|
|
76
|
+
# POST /repos/{owner}/{repo}/dispatches
|
|
77
|
+
# { "event_type": "deploy", "client_payload": { "job_id": "build-789" } }
|
|
78
|
+
|
|
79
|
+
on:
|
|
80
|
+
repository_dispatch:
|
|
81
|
+
types: [deploy]
|
|
82
|
+
|
|
83
|
+
jobs:
|
|
84
|
+
deploy:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- run: |
|
|
88
|
+
echo "Triggered by job: ${{ github.event.client_payload.job_id }}"
|
|
89
|
+
echo "This run ID: ${{ github.run_id }}"
|
|
90
|
+
prevention:
|
|
91
|
+
- "Design CD pipelines using workflow_run event callbacks instead of polling after dispatch"
|
|
92
|
+
- "Include a unique correlation_id input in all workflow_dispatch-triggered workflows"
|
|
93
|
+
- "Track the GitHub public roadmap — run_id in dispatch response is a long-requested feature"
|
|
94
|
+
- "Consider repository_dispatch for machine-to-machine triggers needing reliable correlation"
|
|
95
|
+
docs:
|
|
96
|
+
- url: "https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event"
|
|
97
|
+
label: "REST API: Create a workflow dispatch event (returns 204 No Content)"
|
|
98
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run"
|
|
99
|
+
label: "workflow_run event — callback pattern for monitoring triggered workflows"
|
|
100
|
+
- url: "https://github.com/peter-evans/workflow-dispatch"
|
|
101
|
+
label: "peter-evans/workflow-dispatch — community action with polling heuristics"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
id: permissions-auth-049
|
|
2
|
+
title: 'GITHUB_TOKEN cannot resolve private actions from a different organization'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-token
|
|
7
|
+
- cross-org
|
|
8
|
+
- private-action
|
|
9
|
+
- enterprise
|
|
10
|
+
- repository-not-found
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: 'Unable to resolve action .* repository not found'
|
|
13
|
+
flags: 'i'
|
|
14
|
+
- regex: 'Error: Unable to resolve action `[^`]+`'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'remote: Repository not found'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Error: Unable to resolve action `other-org/private-action@v1`, repository not found."
|
|
20
|
+
- "Error: remote: Repository not found."
|
|
21
|
+
- "fatal: repository 'https://github.com/other-org/private-action/' not found"
|
|
22
|
+
root_cause: |
|
|
23
|
+
GITHUB_TOKEN is automatically scoped to the repository where the workflow runs.
|
|
24
|
+
It cannot authenticate against repositories in a different GitHub organization,
|
|
25
|
+
even when both organizations are in the same GitHub Enterprise instance.
|
|
26
|
+
|
|
27
|
+
When a workflow references a private action from a different org
|
|
28
|
+
(e.g., uses: other-org/my-action@v1), the Actions runner attempts to clone
|
|
29
|
+
that action repository during the job setup phase using GITHUB_TOKEN. The
|
|
30
|
+
clone fails with "repository not found" because the token has no cross-org
|
|
31
|
+
read access. This is not a permissions: block misconfiguration — no amount of
|
|
32
|
+
permissions granted in the workflow YAML will fix it, since the token is
|
|
33
|
+
fundamentally scoped to the calling org.
|
|
34
|
+
|
|
35
|
+
This also affects actions/checkout when checking out code from a private repo
|
|
36
|
+
in another org unless a suitable alternative token is provided.
|
|
37
|
+
fix: |
|
|
38
|
+
Choose the appropriate strategy based on your setup:
|
|
39
|
+
|
|
40
|
+
1. Make the action repository public — simplest if the action contains no secrets.
|
|
41
|
+
2. Vendor the action into your organization — copy the action code into a repo
|
|
42
|
+
in the same org or into .github/actions/ in the calling repo and reference it
|
|
43
|
+
as a local path action.
|
|
44
|
+
3. Use a GitHub App installation token with cross-org permissions for
|
|
45
|
+
actions/checkout calls to cross-org repos (does NOT fix job-level uses: loading).
|
|
46
|
+
4. Use a Personal Access Token (PAT) with access to both orgs for checkout
|
|
47
|
+
(same limitation: cannot fix uses: loading, only checkout steps).
|
|
48
|
+
|
|
49
|
+
Note: The uses: field at the job-steps level (for external actions) is resolved
|
|
50
|
+
before the workflow starts executing, so token injection via steps is not possible
|
|
51
|
+
for the action loading stage — vendoring or making the action public is the only
|
|
52
|
+
reliable fix for job-level uses:.
|
|
53
|
+
fix_code:
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: 'Vendor private action as a local path action'
|
|
56
|
+
code: |
|
|
57
|
+
# Copy the action code to .github/actions/my-action/ in your repo.
|
|
58
|
+
# Then reference it as a local action — no cross-org token required:
|
|
59
|
+
jobs:
|
|
60
|
+
build:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
- uses: ./.github/actions/my-action
|
|
65
|
+
with:
|
|
66
|
+
param: value
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Use GitHub App token for cross-org repository checkout (not uses: loading)'
|
|
69
|
+
code: |
|
|
70
|
+
jobs:
|
|
71
|
+
cross-org-checkout:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/create-github-app-token@v2
|
|
75
|
+
id: app-token
|
|
76
|
+
with:
|
|
77
|
+
app-id: ${{ vars.APP_ID }}
|
|
78
|
+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
79
|
+
owner: other-org
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
with:
|
|
82
|
+
repository: other-org/private-repo
|
|
83
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
84
|
+
prevention:
|
|
85
|
+
- 'Prefer public actions for shared tooling to avoid cross-org token scope issues'
|
|
86
|
+
- 'Vendor private cross-org actions into .github/actions/ in the calling repo'
|
|
87
|
+
- 'Create an internal shared-actions org accessible to all teams where GITHUB_TOKEN works'
|
|
88
|
+
- 'Document which repos depend on cross-org private actions and track them for access reviews'
|
|
89
|
+
docs:
|
|
90
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token'
|
|
91
|
+
label: 'GitHub Actions — Automatic token authentication permissions'
|
|
92
|
+
- url: 'https://docs.github.com/en/actions/sharing-automations/creating-actions/sharing-actions-and-workflows-with-your-organization'
|
|
93
|
+
label: 'GitHub Actions — Sharing actions with your organization'
|
|
94
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses'
|
|
95
|
+
label: 'GitHub Actions — Workflow syntax for uses'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
id: silent-failures-078
|
|
2
|
+
title: "Skipped step leaves job-level outputs: silently empty — downstream jobs receive empty string"
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- job-outputs
|
|
7
|
+
- skipped-step
|
|
8
|
+
- outputs
|
|
9
|
+
- if-condition
|
|
10
|
+
- conditional
|
|
11
|
+
- downstream-jobs
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'steps\.[a-z_][a-z0-9_-]*\.outputs\.[a-z_]'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "needs.<job>.outputs.<key> is empty string when producing step was skipped"
|
|
17
|
+
- "Job output silently empty — step that writes output was conditionally skipped"
|
|
18
|
+
root_cause: |
|
|
19
|
+
A job's `outputs:` block maps keys to values using `${{ steps.<id>.outputs.<key> }}`
|
|
20
|
+
expressions. These expressions are evaluated at the end of the job, after all steps have
|
|
21
|
+
run. If the step referenced in the output mapping is skipped (due to an `if:` condition
|
|
22
|
+
evaluating to false), the step never runs and never writes to `$GITHUB_OUTPUT`. The
|
|
23
|
+
output expression evaluates to an empty string with no warning.
|
|
24
|
+
|
|
25
|
+
Downstream jobs that depend on this output via `needs.<job>.outputs.<key>` receive an
|
|
26
|
+
empty string and continue running without error. This silent empty propagation causes
|
|
27
|
+
incorrect behavior: deploy jobs receiving an empty version string, notification jobs
|
|
28
|
+
receiving an empty status, or matrix jobs receiving an empty configuration list.
|
|
29
|
+
|
|
30
|
+
This is distinct from the `skipped-job-outputs-empty-string` limitation (where the
|
|
31
|
+
entire job is skipped) — here the job itself runs successfully, but one or more of its
|
|
32
|
+
steps are conditionally skipped.
|
|
33
|
+
fix: |
|
|
34
|
+
Ensure the step that writes the output always runs, or provide a fallback value.
|
|
35
|
+
Use `if: always()` on the step if it should run regardless of prior step results.
|
|
36
|
+
Use a separate step to set a default/fallback output for the skipped case.
|
|
37
|
+
Alternatively, restructure the workflow so that the output-producing step is never
|
|
38
|
+
guarded by a condition that might prevent it from running.
|
|
39
|
+
fix_code:
|
|
40
|
+
- language: yaml
|
|
41
|
+
label: "Wrong — output step conditionally skipped, downstream sees empty string"
|
|
42
|
+
code: |
|
|
43
|
+
jobs:
|
|
44
|
+
build:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
outputs:
|
|
47
|
+
version: ${{ steps.get-version.outputs.version }}
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
|
|
51
|
+
- name: Get version (only on tags)
|
|
52
|
+
id: get-version
|
|
53
|
+
if: startsWith(github.ref, 'refs/tags/') # skipped on branch pushes!
|
|
54
|
+
run: echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
|
55
|
+
|
|
56
|
+
deploy:
|
|
57
|
+
needs: build
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- run: |
|
|
61
|
+
echo "Deploying version: ${{ needs.build.outputs.version }}"
|
|
62
|
+
# On branch push: prints "Deploying version: " (empty — step was skipped)
|
|
63
|
+
- language: yaml
|
|
64
|
+
label: "Correct — provide a fallback step to always set the output"
|
|
65
|
+
code: |
|
|
66
|
+
jobs:
|
|
67
|
+
build:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
outputs:
|
|
70
|
+
version: ${{ steps.get-version.outputs.version }}
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
|
|
74
|
+
- name: Get version from tag
|
|
75
|
+
id: get-version
|
|
76
|
+
run: |
|
|
77
|
+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
|
78
|
+
echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
|
79
|
+
else
|
|
80
|
+
echo "version=dev-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
|
81
|
+
fi
|
|
82
|
+
# Always runs — handles both tag and branch pushes
|
|
83
|
+
|
|
84
|
+
deploy:
|
|
85
|
+
needs: build
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
steps:
|
|
88
|
+
- run: echo "Deploying version ${{ needs.build.outputs.version }}"
|
|
89
|
+
- language: yaml
|
|
90
|
+
label: "Alternative — gate deploy job to only run when version is set"
|
|
91
|
+
code: |
|
|
92
|
+
jobs:
|
|
93
|
+
build:
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
outputs:
|
|
96
|
+
version: ${{ steps.get-version.outputs.version }}
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/checkout@v4
|
|
99
|
+
- name: Get version (only on tags)
|
|
100
|
+
id: get-version
|
|
101
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
102
|
+
run: echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
|
103
|
+
|
|
104
|
+
deploy:
|
|
105
|
+
needs: build
|
|
106
|
+
if: needs.build.outputs.version != '' # only run when version was set
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
steps:
|
|
109
|
+
- run: echo "Deploying ${{ needs.build.outputs.version }}"
|
|
110
|
+
prevention:
|
|
111
|
+
- "Audit all job outputs: blocks — for each step reference, verify it always runs"
|
|
112
|
+
- "Avoid conditional if: guards on steps that produce job-level outputs unless a fallback step provides the same output key"
|
|
113
|
+
- "Add an explicit check step at the end of each job that validates required outputs are non-empty"
|
|
114
|
+
- "Use actionlint — it warns when output-producing steps have if: conditions that may skip them"
|
|
115
|
+
docs:
|
|
116
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/passing-information-between-jobs"
|
|
117
|
+
label: "Passing information between jobs — job outputs"
|
|
118
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows"
|
|
119
|
+
label: "Events that trigger workflows — job outputs with conditional steps"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
id: triggers-058
|
|
2
|
+
title: "on.schedule ignores paths: filter — scheduled workflows always run"
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- schedule
|
|
7
|
+
- paths
|
|
8
|
+
- cron
|
|
9
|
+
- trigger
|
|
10
|
+
- filter
|
|
11
|
+
- paths-ignore
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'schedule.*paths:|paths:.*cron:'
|
|
14
|
+
flags: 'si'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Scheduled workflow runs even when no matching files changed"
|
|
17
|
+
- "paths: filter has no effect on scheduled events"
|
|
18
|
+
root_cause: |
|
|
19
|
+
The `paths:` and `paths-ignore:` filters are only evaluated for `push` and `pull_request`
|
|
20
|
+
events, where GitHub can compare the changed files in the triggering commit against the
|
|
21
|
+
filter patterns. `on.schedule` events are time-based and have no associated commit or
|
|
22
|
+
changeset — there are no paths to compare. GitHub silently ignores any `paths:` or
|
|
23
|
+
`paths-ignore:` configuration placed under a `schedule:` trigger.
|
|
24
|
+
|
|
25
|
+
This commonly happens when a developer copies a `push:` trigger block that includes
|
|
26
|
+
`paths:` filtering and applies it to a `schedule:` block expecting the same filtering
|
|
27
|
+
behavior. The workflow runs on every scheduled interval regardless of what files have
|
|
28
|
+
changed. The `paths:` keys are not flagged as invalid — they are simply ignored without
|
|
29
|
+
any warning in the workflow or run logs.
|
|
30
|
+
fix: |
|
|
31
|
+
Remove `paths:` and `paths-ignore:` from `on.schedule:` blocks — they have no effect.
|
|
32
|
+
|
|
33
|
+
If you need the scheduled workflow to skip execution when certain conditions are not
|
|
34
|
+
met, implement gate logic inside the workflow:
|
|
35
|
+
|
|
36
|
+
- Use an early job that checks file modification times or an external flag and sets
|
|
37
|
+
an output. Downstream jobs use `if: needs.check.outputs.should_run == 'true'`.
|
|
38
|
+
- Use the `dorny/paths-filter` action to detect file changes since a known reference
|
|
39
|
+
(e.g., the last successful run's commit SHA stored in a cache or artifact).
|
|
40
|
+
- If the schedule purpose is unrelated to file changes (e.g., dependency audits,
|
|
41
|
+
health checks), no conditional gate is needed — let it run on schedule.
|
|
42
|
+
fix_code:
|
|
43
|
+
- language: yaml
|
|
44
|
+
label: "Wrong — paths: under schedule: is silently ignored"
|
|
45
|
+
code: |
|
|
46
|
+
on:
|
|
47
|
+
schedule:
|
|
48
|
+
- cron: '0 2 * * *'
|
|
49
|
+
paths: # silently ignored — schedule always runs
|
|
50
|
+
- 'src/**'
|
|
51
|
+
- 'package.json'
|
|
52
|
+
push:
|
|
53
|
+
branches: [main]
|
|
54
|
+
paths: # this paths: applies only to the push: trigger
|
|
55
|
+
- 'src/**'
|
|
56
|
+
- 'package.json'
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: "Correct — paths: filter on push only; schedule unconditional or gated in-workflow"
|
|
59
|
+
code: |
|
|
60
|
+
on:
|
|
61
|
+
schedule:
|
|
62
|
+
- cron: '0 2 * * *' # no paths: here — schedule always runs
|
|
63
|
+
push:
|
|
64
|
+
branches: [main]
|
|
65
|
+
paths: # paths: filter applies to push trigger only
|
|
66
|
+
- 'src/**'
|
|
67
|
+
- 'package.json'
|
|
68
|
+
|
|
69
|
+
jobs:
|
|
70
|
+
build:
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
- run: npm run build
|
|
75
|
+
|
|
76
|
+
# To conditionally skip a scheduled run, use an in-workflow gate:
|
|
77
|
+
# jobs:
|
|
78
|
+
# check-changes:
|
|
79
|
+
# runs-on: ubuntu-latest
|
|
80
|
+
# outputs:
|
|
81
|
+
# changed: ${{ steps.filter.outputs.src }}
|
|
82
|
+
# steps:
|
|
83
|
+
# - uses: actions/checkout@v4
|
|
84
|
+
# with:
|
|
85
|
+
# fetch-depth: 2
|
|
86
|
+
# - uses: dorny/paths-filter@v3
|
|
87
|
+
# id: filter
|
|
88
|
+
# with:
|
|
89
|
+
# filters: |
|
|
90
|
+
# src:
|
|
91
|
+
# - 'src/**'
|
|
92
|
+
#
|
|
93
|
+
# build:
|
|
94
|
+
# needs: check-changes
|
|
95
|
+
# if: needs.check-changes.outputs.changed == 'true' || github.event_name == 'push'
|
|
96
|
+
# runs-on: ubuntu-latest
|
|
97
|
+
# steps:
|
|
98
|
+
# - uses: actions/checkout@v4
|
|
99
|
+
# - run: npm run build
|
|
100
|
+
prevention:
|
|
101
|
+
- "Only use paths: and paths-ignore: under push: and pull_request: trigger blocks"
|
|
102
|
+
- "Verify which filters apply to which events using the GitHub Actions syntax reference"
|
|
103
|
+
- "actionlint reports a warning when paths: is used under schedule: — run it in CI"
|
|
104
|
+
- "For scheduled conditional logic, gate using job if: conditions on in-workflow checks"
|
|
105
|
+
docs:
|
|
106
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpaths"
|
|
107
|
+
label: "Workflow syntax: paths filter (applies to push and pull_request only)"
|
|
108
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule"
|
|
109
|
+
label: "Events that trigger workflows: schedule"
|
|
110
|
+
- url: "https://github.com/dorny/paths-filter"
|
|
111
|
+
label: "dorny/paths-filter — detect file changes inside a scheduled workflow"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
id: yaml-syntax-053
|
|
2
|
+
title: 'Step output boolean string coercion — comparing to bare true/false silently never matches'
|
|
3
|
+
category: yaml-syntax
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- step-outputs
|
|
7
|
+
- GITHUB_OUTPUT
|
|
8
|
+
- boolean
|
|
9
|
+
- string-coercion
|
|
10
|
+
- if-condition
|
|
11
|
+
- silent-failure
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'outputs\.[a-z_][a-z0-9_]*\s*==\s*true\b'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'outputs\.[a-z_][a-z0-9_]*\s*==\s*false\b'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'needs\.[a-z_][a-z0-9_-]*\.outputs\.[a-z_]+ ==\s*(true|false)\b'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "if: steps.check.outputs.changed == true"
|
|
21
|
+
- "if: needs.build.outputs.has_changes == false"
|
|
22
|
+
- "if: steps.detect.outputs.skip == true"
|
|
23
|
+
root_cause: |
|
|
24
|
+
All values written to GITHUB_OUTPUT (via echo "key=value" >> $GITHUB_OUTPUT) are
|
|
25
|
+
stored and transmitted as strings, regardless of the intended type. When a step
|
|
26
|
+
outputs a boolean-like value such as echo "changed=true" >> $GITHUB_OUTPUT, the
|
|
27
|
+
output steps.id.outputs.changed is the string 'true', not the boolean true.
|
|
28
|
+
|
|
29
|
+
In GitHub Actions expression syntax, type comparison is strict:
|
|
30
|
+
- 'true' == true → false (string 'true' does not equal boolean true)
|
|
31
|
+
- 'false' == false → false (string 'false' does not equal boolean false)
|
|
32
|
+
- '' == false → true (empty string does equal boolean false — a separate gotcha)
|
|
33
|
+
|
|
34
|
+
This means if: steps.check.outputs.changed == true silently evaluates to false
|
|
35
|
+
even when the output is the string "true", and the step is silently skipped with
|
|
36
|
+
no error. The workflow log shows the step as skipped with no indication that the
|
|
37
|
+
condition evaluation was the cause.
|
|
38
|
+
|
|
39
|
+
The same pattern applies to job outputs (needs.job.outputs.flag) passed between
|
|
40
|
+
jobs via the outputs: block.
|
|
41
|
+
fix: |
|
|
42
|
+
Always compare step and job outputs as strings using single-quoted string literals:
|
|
43
|
+
- For true check: == 'true'
|
|
44
|
+
- For false check: != 'true' (preferred over == 'false' because empty string also needs handling)
|
|
45
|
+
- For numeric outputs: use fromJSON() to parse before arithmetic comparison
|
|
46
|
+
|
|
47
|
+
For boolean output authors: document that consumers must compare as strings, or
|
|
48
|
+
use '1'/'0' string conventions to make the string nature explicit.
|
|
49
|
+
fix_code:
|
|
50
|
+
- language: yaml
|
|
51
|
+
label: 'Correct: compare step outputs as strings'
|
|
52
|
+
code: |
|
|
53
|
+
steps:
|
|
54
|
+
- id: check
|
|
55
|
+
run: echo "changed=true" >> $GITHUB_OUTPUT
|
|
56
|
+
|
|
57
|
+
# WRONG — silently skipped because 'true' != true (string != boolean)
|
|
58
|
+
# - if: steps.check.outputs.changed == true
|
|
59
|
+
# run: echo "This never runs"
|
|
60
|
+
|
|
61
|
+
# CORRECT — compare output string to string literal
|
|
62
|
+
- if: steps.check.outputs.changed == 'true'
|
|
63
|
+
run: echo "Files changed, running deploy"
|
|
64
|
+
|
|
65
|
+
# CORRECT — inverse check
|
|
66
|
+
- if: steps.check.outputs.changed != 'true'
|
|
67
|
+
run: echo "No changes, skipping deploy"
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: 'Job-to-job boolean output — compare as string in downstream job'
|
|
70
|
+
code: |
|
|
71
|
+
jobs:
|
|
72
|
+
detect:
|
|
73
|
+
outputs:
|
|
74
|
+
has_changes: ${{ steps.diff.outputs.has_changes }}
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- id: diff
|
|
78
|
+
run: |
|
|
79
|
+
# Output is always a string
|
|
80
|
+
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
81
|
+
|
|
82
|
+
deploy:
|
|
83
|
+
needs: detect
|
|
84
|
+
# CORRECT: compare job output as string literal
|
|
85
|
+
if: needs.detect.outputs.has_changes == 'true'
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
steps:
|
|
88
|
+
- run: echo "Deploying"
|
|
89
|
+
prevention:
|
|
90
|
+
- "Always use single-quoted string literals in if: conditions when comparing step or job outputs: == 'true' not == true"
|
|
91
|
+
- 'Use fromJSON() to parse numeric step outputs before arithmetic or numeric comparisons'
|
|
92
|
+
- 'Audit workflows for bare == true or == false comparisons on steps.*.outputs.* and needs.*.outputs.*'
|
|
93
|
+
- 'Use actionlint — it detects type mismatches in expression comparisons involving outputs'
|
|
94
|
+
docs:
|
|
95
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/passing-information-between-jobs'
|
|
96
|
+
label: 'GitHub Actions — Passing information between jobs'
|
|
97
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#literals'
|
|
98
|
+
label: 'GitHub Actions — Expression literals and type coercion'
|
|
99
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/setting-an-output-parameter'
|
|
100
|
+
label: 'GitHub Actions — Setting an output parameter'
|
package/package.json
CHANGED