@htekdev/actions-debugger 1.0.47 → 1.0.49
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/permissions-auth/permissions-auth-036.yml +96 -0
- package/errors/permissions-auth/permissions-auth-037.yml +100 -0
- package/errors/runner-environment/runner-environment-103.yml +100 -0
- package/errors/runner-environment/runner-environment-104.yml +103 -0
- package/errors/silent-failures/silent-failures-051.yml +107 -0
- package/errors/silent-failures/silent-failures-052.yml +102 -0
- package/errors/triggers/triggers-036.yml +73 -0
- package/errors/triggers/triggers-037.yml +79 -0
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: permissions-auth-036
|
|
2
|
+
title: 'GITHUB_TOKEN cannot push commits to branches protected by "Require signed commits" — token identity cannot sign'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-token
|
|
7
|
+
- signed-commits
|
|
8
|
+
- branch-protection
|
|
9
|
+
- gpg
|
|
10
|
+
- push
|
|
11
|
+
- protected-branch
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'GH006.*Protected branch.*signed|Commits must have verified signatures'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'Changes must be signed|protected branch.*signed.*commits'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'remote: error: GH006: Protected branch update failed for refs/heads/main.'
|
|
19
|
+
- 'remote: error: Commits must have verified signatures.'
|
|
20
|
+
- 'remote: error: Changes must be signed.'
|
|
21
|
+
root_cause: |
|
|
22
|
+
GitHub's "Require signed commits" branch protection rule requires every pushed commit
|
|
23
|
+
to carry a verified GPG or SSH signature. The GITHUB_TOKEN is a short-lived bearer token
|
|
24
|
+
scoped to a workflow run — it has no associated GPG or SSH signing key and cannot produce
|
|
25
|
+
verified commit signatures.
|
|
26
|
+
|
|
27
|
+
This means any workflow that creates commits and pushes them using GITHUB_TOKEN will fail
|
|
28
|
+
on branches protected by "Require signed commits", regardless of the token's permission
|
|
29
|
+
level. Having contents: write permission is necessary but not sufficient: the signature
|
|
30
|
+
requirement is enforced by a separate push hook after permission checks pass.
|
|
31
|
+
|
|
32
|
+
Common workflow patterns that hit this:
|
|
33
|
+
- Auto-commit actions (stefanzweifel/auto-commit-action, EndBug/add-and-commit) that
|
|
34
|
+
commit generated files (changelogs, coverage badges, built assets) back to main
|
|
35
|
+
- Release workflows that bump version numbers in committed files before tagging
|
|
36
|
+
- Automated dependency update workflows that commit lockfile changes
|
|
37
|
+
- Documentation generation workflows that commit generated API docs or OpenAPI specs
|
|
38
|
+
|
|
39
|
+
The GH006 error appears in the push step's output but the job may not fail loudly if
|
|
40
|
+
the step's exit code is not checked carefully. In some auto-commit actions, the failure
|
|
41
|
+
surfaces as an unexpected empty push with no error surfaced to the workflow summary.
|
|
42
|
+
fix: |
|
|
43
|
+
Option 1 — Create a pull request instead of direct push (recommended): Use
|
|
44
|
+
peter-evans/create-pull-request or similar to push the auto-generated commit to a
|
|
45
|
+
feature branch, then open a PR. Feature branches are not subject to the main branch's
|
|
46
|
+
"Require signed commits" protection.
|
|
47
|
+
|
|
48
|
+
Option 2 — GitHub App with bypass: Create a GitHub App and add it to the branch
|
|
49
|
+
protection "Allow specific actors to bypass required pull request reviews and required
|
|
50
|
+
status checks" bypass list. Generate tokens for the App in the workflow and push with
|
|
51
|
+
the App token. The App's bot commits can bypass the signed-commits requirement if
|
|
52
|
+
explicitly listed as a bypass actor.
|
|
53
|
+
|
|
54
|
+
Option 3 — Signed commits via GitHub App SSH key: Configure a GitHub App with an
|
|
55
|
+
SSH signing key. Use the App installation token and the App's SSH key for commit
|
|
56
|
+
signing via GIT_COMMITTER_EMAIL and gpg.format = ssh configuration.
|
|
57
|
+
|
|
58
|
+
The GITHUB_TOKEN itself cannot be configured for commit signing — this is a platform
|
|
59
|
+
limitation with no in-token workaround.
|
|
60
|
+
fix_code:
|
|
61
|
+
- language: yaml
|
|
62
|
+
label: 'Create a pull request instead of pushing directly — avoids signed commits requirement on main'
|
|
63
|
+
code: |
|
|
64
|
+
jobs:
|
|
65
|
+
update-generated:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
permissions:
|
|
68
|
+
contents: write
|
|
69
|
+
pull-requests: write
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
|
|
73
|
+
- name: Regenerate files
|
|
74
|
+
run: make generate
|
|
75
|
+
|
|
76
|
+
- name: Create pull request for generated output
|
|
77
|
+
uses: peter-evans/create-pull-request@v7
|
|
78
|
+
with:
|
|
79
|
+
commit-message: 'chore: regenerate auto-generated files'
|
|
80
|
+
branch: automated/regenerate-output
|
|
81
|
+
title: 'chore: automated file regeneration'
|
|
82
|
+
body: 'Automated regeneration triggered by CI. Merge after review.'
|
|
83
|
+
# Commit targets the feature branch, not main — signed-commits rule on
|
|
84
|
+
# main does not apply to the automated/ branch
|
|
85
|
+
prevention:
|
|
86
|
+
- 'Before enabling "Require signed commits" on a branch, audit all workflows that push commits using GITHUB_TOKEN to that branch'
|
|
87
|
+
- 'Use the create-pull-request pattern for auto-generated commits rather than pushing directly to protected branches'
|
|
88
|
+
- 'Document in workflow files that GITHUB_TOKEN cannot push to branches with signed-commits protection'
|
|
89
|
+
- 'If a GitHub App bypass is used, rotate the App private key regularly and scope permissions to the minimum required'
|
|
90
|
+
docs:
|
|
91
|
+
- url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-signed-commits'
|
|
92
|
+
label: 'GitHub Docs: Require signed commits branch protection'
|
|
93
|
+
- url: 'https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification'
|
|
94
|
+
label: 'GitHub Docs: About commit signature verification'
|
|
95
|
+
- url: 'https://github.com/orgs/community/discussions/13836'
|
|
96
|
+
label: 'GitHub Community #13836: GITHUB_TOKEN cannot push to branch requiring signed commits'
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
id: permissions-auth-037
|
|
2
|
+
title: 'Environment secrets are only accessible to jobs that declare a matching environment: key — other jobs silently receive empty string'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- environment
|
|
7
|
+
- secrets
|
|
8
|
+
- environment-secrets
|
|
9
|
+
- deployment-environment
|
|
10
|
+
- job-scoped
|
|
11
|
+
- secret-scope
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'environment:\s*[a-z0-9_-]+.*secrets\.|secrets\.[A-Z_]+'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "(No error — secrets.<SECRET_NAME> resolves to '' in jobs that do not declare the matching environment:)"
|
|
17
|
+
root_cause: |
|
|
18
|
+
GitHub Actions supports three secret scopes with different visibility:
|
|
19
|
+
1. Repository secrets — available to all jobs in all workflows in the repository
|
|
20
|
+
2. Organization secrets — available to authorized repositories/workflows
|
|
21
|
+
3. Environment secrets — ONLY available to jobs that declare environment: <env-name>
|
|
22
|
+
|
|
23
|
+
Environment secrets are scoped to a specific deployment environment and are
|
|
24
|
+
intentionally isolated. A job that references secrets.MY_ENV_SECRET without
|
|
25
|
+
declaring environment: production receives '' (empty string) for that secret
|
|
26
|
+
with no error, no warning, and no indication that the secret exists elsewhere.
|
|
27
|
+
|
|
28
|
+
This isolation is a security feature: environment secrets are only released to
|
|
29
|
+
jobs that have satisfied environment protection rules (required reviewers, wait
|
|
30
|
+
timers, deployment branch policies). However it becomes a silent failure when:
|
|
31
|
+
|
|
32
|
+
- A secret is accidentally created in an environment instead of the repository scope
|
|
33
|
+
- A reusable workflow job uses the secret but the caller job did not declare environment:
|
|
34
|
+
- A job is refactored to remove environment: (to skip protection rules in testing) but
|
|
35
|
+
still references the now-inaccessible environment secret
|
|
36
|
+
- A developer expects an environment secret to work like a repository secret
|
|
37
|
+
|
|
38
|
+
The effect is indistinguishable from the secret not existing: the value is '' and the
|
|
39
|
+
job may fail with an auth error, a blank config value, or silently produce wrong output.
|
|
40
|
+
fix: |
|
|
41
|
+
Determine the intended scope:
|
|
42
|
+
- If the secret should be accessible to all jobs: create it as a repository secret
|
|
43
|
+
(Settings > Secrets and variables > Actions > Repository secrets)
|
|
44
|
+
- If the secret must be gated by deployment protection rules: keep it as an environment
|
|
45
|
+
secret AND add environment: <env-name> to every job that needs it
|
|
46
|
+
- If both a repository secret and an environment secret share the same name:
|
|
47
|
+
the environment secret takes precedence in jobs that declare that environment
|
|
48
|
+
|
|
49
|
+
Use the GitHub Settings UI to confirm which scope a secret belongs to before
|
|
50
|
+
debugging unexpected empty values in run steps.
|
|
51
|
+
fix_code:
|
|
52
|
+
- language: yaml
|
|
53
|
+
label: 'Add environment: to the job that needs the environment-scoped secret'
|
|
54
|
+
code: |
|
|
55
|
+
jobs:
|
|
56
|
+
deploy:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
# Without this environment: key, secrets.DEPLOY_API_KEY is ''
|
|
59
|
+
# even though it exists as an environment secret for 'production'
|
|
60
|
+
environment: production
|
|
61
|
+
steps:
|
|
62
|
+
- name: Deploy to production
|
|
63
|
+
env:
|
|
64
|
+
API_KEY: ${{ secrets.DEPLOY_API_KEY }}
|
|
65
|
+
run: echo "Deploying with scoped key"
|
|
66
|
+
|
|
67
|
+
- language: yaml
|
|
68
|
+
label: 'Separate build (no environment) from deploy (with environment) to scope protection rules to deploy only'
|
|
69
|
+
code: |
|
|
70
|
+
jobs:
|
|
71
|
+
build:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
# No environment: here — only repository-scoped secrets are needed for build
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v4
|
|
76
|
+
- run: echo "Building artifacts"
|
|
77
|
+
|
|
78
|
+
deploy:
|
|
79
|
+
needs: build
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
environment: production # Required reviewers or wait timer enforced here
|
|
82
|
+
steps:
|
|
83
|
+
- name: Deploy
|
|
84
|
+
env:
|
|
85
|
+
# DEPLOY_KEY is an environment secret — only available because
|
|
86
|
+
# environment: production is declared on this job
|
|
87
|
+
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
|
|
88
|
+
run: echo "Deploying with environment-scoped secret"
|
|
89
|
+
prevention:
|
|
90
|
+
- 'When creating a secret, confirm its intended scope: environment secrets require the matching environment: on every job that needs them'
|
|
91
|
+
- 'If a secret is needed in a build job (no deployment environment), create it as a repository secret not an environment secret'
|
|
92
|
+
- 'Add environment: to reusable workflow caller jobs when the called workflow references environment-scoped secrets'
|
|
93
|
+
- 'Use the GitHub Settings UI to audit secret scopes when debugging empty secret values'
|
|
94
|
+
docs:
|
|
95
|
+
- url: 'https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment'
|
|
96
|
+
label: 'GitHub Docs: Using environments for deployment'
|
|
97
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment'
|
|
98
|
+
label: 'GitHub Docs: Creating secrets for an environment'
|
|
99
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment'
|
|
100
|
+
label: 'GitHub Docs: jobs.<job_id>.environment syntax'
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
id: runner-environment-103
|
|
2
|
+
title: "actions/setup-go cache skipped with 'no file matched go.sum' when go.sum is absent or not at workspace root"
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- setup-go
|
|
7
|
+
- go
|
|
8
|
+
- cache
|
|
9
|
+
- go-sum
|
|
10
|
+
- monorepo
|
|
11
|
+
- cache-miss
|
|
12
|
+
- cache-dependency-path
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'No file.*matched.*go\.sum|go\.sum.*not.*found.*cache'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'setup-go.*cache.*skip|Unable to find.*go\.sum'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- 'Warning: No file in /home/runner/work/repo/repo matched to [**/go.sum, !**/vendor/**], make sure you have checked out the target repository'
|
|
20
|
+
- 'go.sum not found, module cache will not be saved'
|
|
21
|
+
- 'Cache miss — no go.sum file located at expected path'
|
|
22
|
+
root_cause: |
|
|
23
|
+
actions/setup-go uses go.sum as the cache key hash source. When cache: true (the default
|
|
24
|
+
since setup-go@v4), the action runs hashFiles('**/go.sum') to compute the cache key.
|
|
25
|
+
If no go.sum file is found anywhere in the workspace, the action emits a warning and
|
|
26
|
+
silently skips saving and restoring the module cache entirely — the job continues with
|
|
27
|
+
exit 0 but all Go dependencies are downloaded from scratch on every run.
|
|
28
|
+
|
|
29
|
+
Common root causes:
|
|
30
|
+
|
|
31
|
+
1. New project: go mod tidy has not been run — go.sum does not yet exist in the repo.
|
|
32
|
+
|
|
33
|
+
2. Monorepo layout: Go modules live under subdirectories (e.g., services/api/go.sum).
|
|
34
|
+
The default glob **/go.sum should find nested files, but when combined with the
|
|
35
|
+
!**/vendor/** exclusion or when the repo structure is non-standard, detection can fail.
|
|
36
|
+
|
|
37
|
+
3. Go workspace (go.work): modules are organized under subdirectories managed by a
|
|
38
|
+
go.work file; individual go.sum files may be present but not at the expected root.
|
|
39
|
+
|
|
40
|
+
4. Ordering issue: actions/setup-go runs before actions/checkout, so go.sum is not
|
|
41
|
+
yet in the workspace when the cache key is computed.
|
|
42
|
+
|
|
43
|
+
5. go.sum is listed in .gitignore (non-standard but seen in internal tooling repos
|
|
44
|
+
that regenerate dependencies on every run).
|
|
45
|
+
|
|
46
|
+
The missing-cache warning is only visible in the setup-go step's log output and does
|
|
47
|
+
not fail the workflow. On large Go projects, silent cache bypass wastes 60-120+ seconds
|
|
48
|
+
downloading modules from proxy.golang.org on every CI run.
|
|
49
|
+
fix: |
|
|
50
|
+
For monorepo or nested module layouts, explicitly set cache-dependency-path to the
|
|
51
|
+
location of the go.sum file(s). A glob pattern can match multiple modules if needed.
|
|
52
|
+
|
|
53
|
+
For new projects: run go mod tidy locally and commit go.sum before pushing.
|
|
54
|
+
|
|
55
|
+
For go.work workspaces: list each module's go.sum explicitly in cache-dependency-path
|
|
56
|
+
to ensure all modules participate in the cache key.
|
|
57
|
+
|
|
58
|
+
Always confirm actions/checkout runs before actions/setup-go in the steps list.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: 'Set cache-dependency-path for a nested module in a monorepo'
|
|
62
|
+
code: |
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
|
|
66
|
+
- uses: actions/setup-go@v5
|
|
67
|
+
with:
|
|
68
|
+
go-version: '1.22'
|
|
69
|
+
cache: true
|
|
70
|
+
# Explicit path overrides default glob — required when go.sum is not at repo root
|
|
71
|
+
cache-dependency-path: services/api/go.sum
|
|
72
|
+
|
|
73
|
+
- language: yaml
|
|
74
|
+
label: 'Multiple modules in a go.work workspace — list all go.sum paths'
|
|
75
|
+
code: |
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
|
|
79
|
+
- uses: actions/setup-go@v5
|
|
80
|
+
with:
|
|
81
|
+
go-version: '1.22'
|
|
82
|
+
cache: true
|
|
83
|
+
# Multi-line value: each module go.sum contributes to the aggregate cache key
|
|
84
|
+
cache-dependency-path: |
|
|
85
|
+
services/api/go.sum
|
|
86
|
+
services/worker/go.sum
|
|
87
|
+
pkg/shared/go.sum
|
|
88
|
+
prevention:
|
|
89
|
+
- 'Always run go mod tidy before the first commit to ensure go.sum exists in the repository'
|
|
90
|
+
- 'In monorepos, explicitly set cache-dependency-path rather than relying on the default **/go.sum glob'
|
|
91
|
+
- 'Check the setup-go step output for "No file matched" warnings — caching may be silently disabled without failing the job'
|
|
92
|
+
- 'Ensure actions/checkout runs before actions/setup-go so go.sum is present when the cache key is computed'
|
|
93
|
+
- 'Never add go.sum to .gitignore — it is required for reproducible builds and module cache keying'
|
|
94
|
+
docs:
|
|
95
|
+
- url: 'https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs'
|
|
96
|
+
label: 'actions/setup-go: Caching dependency files and build outputs'
|
|
97
|
+
- url: 'https://github.com/actions/setup-go/issues/289'
|
|
98
|
+
label: 'setup-go #289: Cache fails when go.sum not at repository root'
|
|
99
|
+
- url: 'https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-go'
|
|
100
|
+
label: 'GitHub Docs: Building and testing Go'
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
id: runner-environment-104
|
|
2
|
+
title: 'Private container registry image (ghcr.io, Docker Hub private) requires credentials: in the container: block — a docker login step is too late'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- container
|
|
7
|
+
- ghcr
|
|
8
|
+
- private-registry
|
|
9
|
+
- credentials
|
|
10
|
+
- docker-pull
|
|
11
|
+
- job-container
|
|
12
|
+
- services
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'pull access denied.*repository does not exist or may require.*login'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'unauthorized.*authentication required|denied.*requested access to the resource is denied'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
error_messages:
|
|
19
|
+
- 'Error response from daemon: pull access denied for ghcr.io/org/image, repository does not exist or may require docker login: denied'
|
|
20
|
+
- 'Error: unauthorized: authentication required'
|
|
21
|
+
- 'Error pulling image: denied: requested access to the resource is denied'
|
|
22
|
+
- 'toomanyrequests: You have reached your pull rate limit'
|
|
23
|
+
root_cause: |
|
|
24
|
+
When a job specifies a container image via jobs.<id>.container.image:, GitHub Actions
|
|
25
|
+
pulls the image before the job starts — before any steps execute. There is no
|
|
26
|
+
opportunity to authenticate with a docker login step because steps run inside the
|
|
27
|
+
already-running container.
|
|
28
|
+
|
|
29
|
+
Many workflows attempt to call docker/login-action or similar in a step, but by
|
|
30
|
+
then the container pull has either succeeded or already failed. The login step
|
|
31
|
+
has no effect on the original image pull.
|
|
32
|
+
|
|
33
|
+
The same limitation applies to services containers (jobs.<id>.services.<id>.image:):
|
|
34
|
+
all service containers are also pulled at job startup before any steps run.
|
|
35
|
+
|
|
36
|
+
Common scenarios that hit this error:
|
|
37
|
+
- Private GitHub Container Registry (ghcr.io) images requiring a PAT with
|
|
38
|
+
read:packages scope
|
|
39
|
+
- Docker Hub private repository images (rate-limited or private-tier)
|
|
40
|
+
- Self-hosted or corporate registries requiring Basic auth
|
|
41
|
+
- AWS ECR private images (ECR is not directly supported via credentials: block —
|
|
42
|
+
see fix for the ECR-specific workaround)
|
|
43
|
+
fix: |
|
|
44
|
+
Provide credentials in the container: or services: block using the credentials: key.
|
|
45
|
+
These credentials are used at image pull time, before any steps begin.
|
|
46
|
+
|
|
47
|
+
For ghcr.io: use github.actor as username and a PAT with read:packages scope as
|
|
48
|
+
password. If the package is in the same org and the repository has package access,
|
|
49
|
+
secrets.GITHUB_TOKEN may also work.
|
|
50
|
+
|
|
51
|
+
For Docker Hub: use a Docker Hub username and access token (not password).
|
|
52
|
+
|
|
53
|
+
For AWS ECR private images: use a self-hosted runner with ECR credentials
|
|
54
|
+
pre-configured on the host, or build a public mirror of your private image.
|
|
55
|
+
The credentials: block does not support dynamic ECR token retrieval.
|
|
56
|
+
fix_code:
|
|
57
|
+
- language: yaml
|
|
58
|
+
label: 'Authenticate to a private ghcr.io image using credentials in the container block'
|
|
59
|
+
code: |
|
|
60
|
+
jobs:
|
|
61
|
+
test:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
container:
|
|
64
|
+
image: ghcr.io/myorg/private-runner:latest
|
|
65
|
+
# credentials: evaluated at job startup BEFORE any steps run
|
|
66
|
+
credentials:
|
|
67
|
+
username: ${{ github.actor }}
|
|
68
|
+
password: ${{ secrets.GHCR_PAT }}
|
|
69
|
+
# For same-org packages with package access enabled:
|
|
70
|
+
# password: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
- run: echo "Running inside authenticated private container"
|
|
74
|
+
|
|
75
|
+
- language: yaml
|
|
76
|
+
label: 'Private Docker Hub image in a services block using credentials'
|
|
77
|
+
code: |
|
|
78
|
+
jobs:
|
|
79
|
+
integration-test:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
services:
|
|
82
|
+
database:
|
|
83
|
+
image: myorg/private-db:5.7
|
|
84
|
+
# credentials: here too — service images are pulled before steps
|
|
85
|
+
credentials:
|
|
86
|
+
username: ${{ secrets.DOCKER_USERNAME }}
|
|
87
|
+
password: ${{ secrets.DOCKER_TOKEN }}
|
|
88
|
+
ports:
|
|
89
|
+
- 5432:5432
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v4
|
|
92
|
+
- run: echo "Integration test against private DB image"
|
|
93
|
+
prevention:
|
|
94
|
+
- 'Always use the credentials: block inside container: or services: for private images — a docker login step in run: is too late'
|
|
95
|
+
- 'For same-org ghcr.io images, configure package visibility to allow GITHUB_TOKEN to avoid managing a separate PAT'
|
|
96
|
+
- 'Test authentication separately with docker pull from a local machine using the same credentials before wiring into a workflow'
|
|
97
|
+
docs:
|
|
98
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainer'
|
|
99
|
+
label: 'GitHub Docs: jobs.<job_id>.container.credentials'
|
|
100
|
+
- url: 'https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions'
|
|
101
|
+
label: 'GitHub Docs: Using GITHUB_TOKEN with GitHub Packages (ghcr.io)'
|
|
102
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials'
|
|
103
|
+
label: 'GitHub Docs: jobs.<job_id>.services.<service_id>.credentials'
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
id: silent-failures-051
|
|
2
|
+
title: 'environment.url expression evaluates to empty string when referencing steps.*.outputs — resolved before steps run'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- environment
|
|
7
|
+
- deployment-url
|
|
8
|
+
- step-outputs
|
|
9
|
+
- expression-evaluation
|
|
10
|
+
- dynamic-url
|
|
11
|
+
- deployment-widget
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'environment.*url.*steps\.\w+\.outputs\.\w+'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'deployment.*url.*empty|environment.*url.*not.*set|page_url.*empty'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- 'Deployment environment shows no URL in GitHub UI despite environment.url referencing a step output'
|
|
19
|
+
- 'Deployment URL is empty string — steps.deploy.outputs.url is non-empty in the step logs'
|
|
20
|
+
root_cause: |
|
|
21
|
+
The jobs.<id>.environment.url field is evaluated at job scheduling time — before ANY steps
|
|
22
|
+
in the job have run and before any step outputs exist. Expressions that reference
|
|
23
|
+
steps.*.outputs.* from the same job silently resolve to empty string. GitHub does not
|
|
24
|
+
emit a warning or error when this occurs.
|
|
25
|
+
|
|
26
|
+
This is a fundamental evaluation ordering constraint: the environment block (both name and
|
|
27
|
+
url) is part of the workflow plan GitHub evaluates once when the job is queued. The job
|
|
28
|
+
has not yet started, so step outputs are undefined and resolve to empty.
|
|
29
|
+
|
|
30
|
+
Contexts SAFE to use in environment.url (available at scheduling time):
|
|
31
|
+
- github.* — static for the run
|
|
32
|
+
- vars.* — repository/org/environment variables
|
|
33
|
+
- inputs.* — workflow_dispatch or workflow_call inputs
|
|
34
|
+
- needs.<prior-job>.outputs.* — outputs from already-completed upstream jobs
|
|
35
|
+
- env.* declared at the job level
|
|
36
|
+
|
|
37
|
+
Contexts that silently resolve to empty in environment.url:
|
|
38
|
+
- steps.*.outputs.* — step has not run yet
|
|
39
|
+
- steps.*.conclusion / steps.*.outcome — step has not run yet
|
|
40
|
+
- Any runtime-computed value from within the same job
|
|
41
|
+
|
|
42
|
+
Common scenario: a deploy step creates a preview URL (e.g., Vercel preview, Heroku review
|
|
43
|
+
app, AWS CloudFormation output), writes it to GITHUB_OUTPUT, and the developer tries to
|
|
44
|
+
surface it via environment.url so it appears in the GitHub deployment widget on the PR.
|
|
45
|
+
The widget shows no URL, and no error is ever raised.
|
|
46
|
+
fix: |
|
|
47
|
+
Route the dynamic deployment URL through job outputs of the deploy job, then reference
|
|
48
|
+
needs.deploy.outputs.<name> in a dependent job's environment.url. The needs context IS
|
|
49
|
+
available at scheduling time for downstream jobs because the upstream job has already
|
|
50
|
+
completed.
|
|
51
|
+
|
|
52
|
+
For cases where the URL can be computed entirely from pre-execution contexts (github.ref_name,
|
|
53
|
+
inputs.*, vars.*), use a static URL expression directly in environment.url without
|
|
54
|
+
any step output dependency.
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'Two-job pattern — expose deployment URL as job output, reference via needs in downstream job'
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
deploy:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
outputs:
|
|
63
|
+
# Promote step output to job output — available to downstream jobs via needs
|
|
64
|
+
app_url: ${{ steps.deploy.outputs.url }}
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
- name: Deploy
|
|
68
|
+
id: deploy
|
|
69
|
+
run: |
|
|
70
|
+
DEPLOY_URL="https://preview-${{ github.run_id }}.example.com"
|
|
71
|
+
echo "url=${DEPLOY_URL}" >> $GITHUB_OUTPUT
|
|
72
|
+
|
|
73
|
+
link-deployment:
|
|
74
|
+
needs: deploy
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
environment:
|
|
77
|
+
name: preview
|
|
78
|
+
# needs.deploy.outputs.* resolves correctly — deploy job already completed
|
|
79
|
+
url: ${{ needs.deploy.outputs.app_url }}
|
|
80
|
+
steps:
|
|
81
|
+
- run: echo "Linked deployment to ${{ needs.deploy.outputs.app_url }}"
|
|
82
|
+
|
|
83
|
+
- language: yaml
|
|
84
|
+
label: 'Static URL pattern using pre-execution context (no step output dependency)'
|
|
85
|
+
code: |
|
|
86
|
+
jobs:
|
|
87
|
+
deploy:
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
environment:
|
|
90
|
+
name: preview
|
|
91
|
+
# github.ref_name is available at scheduling time — always resolves correctly
|
|
92
|
+
url: 'https://preview-${{ github.ref_name }}.example.com'
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v4
|
|
95
|
+
- run: echo "deploying..."
|
|
96
|
+
prevention:
|
|
97
|
+
- 'Never reference steps.*.outputs.* directly in environment.url — promote to a job output and reference via needs in a downstream job instead'
|
|
98
|
+
- 'Test environment.url by checking the deployment widget on a pull request — an empty URL indicates the expression evaluated to empty string at scheduling time'
|
|
99
|
+
- 'Document the two-job deploy-then-link pattern in team workflow templates to prevent rediscovery'
|
|
100
|
+
- 'If the URL requires a step output, always add an outputs: block to the job and reference it via needs.<job>.outputs.<name> in a downstream job'
|
|
101
|
+
docs:
|
|
102
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment'
|
|
103
|
+
label: 'GitHub Docs: jobs.<job_id>.environment syntax'
|
|
104
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/passing-information-between-jobs'
|
|
105
|
+
label: 'GitHub Docs: Passing information between jobs'
|
|
106
|
+
- url: 'https://github.com/orgs/community/discussions/9366'
|
|
107
|
+
label: 'GitHub Community #9366: Dynamic environment URL from step output resolves to empty'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
id: silent-failures-052
|
|
2
|
+
title: 'Step outputs referenced in the job-level env: block are always empty string — job env is evaluated before any steps run'
|
|
3
|
+
category: silent-failures
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- env
|
|
7
|
+
- steps-outputs
|
|
8
|
+
- job-level
|
|
9
|
+
- context-evaluation
|
|
10
|
+
- expression
|
|
11
|
+
- environment-variables
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'steps\.[a-z0-9_-]+\.outputs\.[a-z0-9_-]+'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "(No error — steps.<id>.outputs.<name> silently resolves to '' when used in a job-level env: block)"
|
|
17
|
+
root_cause: |
|
|
18
|
+
GitHub Actions evaluates the jobs.<id>.env: block once at job initialization, before
|
|
19
|
+
any steps begin executing. At that point, the steps context exists but all step
|
|
20
|
+
outputs are empty — no step has run yet, so no outputs have been set.
|
|
21
|
+
|
|
22
|
+
This means that ${{ steps.my-step.outputs.value }} in the jobs.<id>.env: block
|
|
23
|
+
always resolves to '' regardless of what the step later produces. The job does not
|
|
24
|
+
fail or warn — the environment variable is simply set to empty string and all steps
|
|
25
|
+
that reference it via $VAR or %VAR% receive nothing.
|
|
26
|
+
|
|
27
|
+
This is a context availability limitation documented by GitHub, but it is easy to
|
|
28
|
+
miss because the expression syntax is valid and the job runs without error.
|
|
29
|
+
|
|
30
|
+
Affected env: scopes (evaluated before steps run):
|
|
31
|
+
- jobs.<id>.env: — job-level env block
|
|
32
|
+
- The workflow-level env: block also cannot access steps.*, for the same reason
|
|
33
|
+
|
|
34
|
+
Env scopes that CAN access step outputs (evaluated per-step):
|
|
35
|
+
- jobs.<id>.steps.<id>.env: — step-level env block, evaluated when that step runs
|
|
36
|
+
|
|
37
|
+
Common mistake: a developer sets a job-level env var to a computed step output
|
|
38
|
+
(e.g., a parsed version string or a generated artifact path) and then uses that
|
|
39
|
+
var in multiple subsequent steps, not realizing it is always empty.
|
|
40
|
+
fix: |
|
|
41
|
+
Move the env: block referencing step outputs from job level down to the individual
|
|
42
|
+
step level. Step-level env: blocks are evaluated when that step runs, so earlier
|
|
43
|
+
step outputs are already populated.
|
|
44
|
+
|
|
45
|
+
If the same value is needed across many steps, pass it via $GITHUB_OUTPUT and
|
|
46
|
+
reference it inline with ${{ steps.step-id.outputs.name }} in each step's run:.
|
|
47
|
+
fix_code:
|
|
48
|
+
- language: yaml
|
|
49
|
+
label: 'Move env that references step outputs from job level to step level'
|
|
50
|
+
code: |
|
|
51
|
+
jobs:
|
|
52
|
+
build:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
|
|
55
|
+
# WRONG: job-level env block — evaluated before any step runs
|
|
56
|
+
# steps.version.outputs.tag is '' here, no matter what the step produces
|
|
57
|
+
# env:
|
|
58
|
+
# RELEASE_TAG: ${{ steps.version.outputs.tag }}
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- name: Compute release tag
|
|
62
|
+
id: version
|
|
63
|
+
run: echo "tag=v1.2.3" >> $GITHUB_OUTPUT
|
|
64
|
+
|
|
65
|
+
- name: Build with release tag
|
|
66
|
+
# CORRECT: step-level env block — evaluated when this step runs
|
|
67
|
+
# steps.version.outputs.tag is populated by the prior step at this point
|
|
68
|
+
env:
|
|
69
|
+
RELEASE_TAG: ${{ steps.version.outputs.tag }}
|
|
70
|
+
run: echo "Building $RELEASE_TAG"
|
|
71
|
+
|
|
72
|
+
- name: Publish with release tag
|
|
73
|
+
env:
|
|
74
|
+
RELEASE_TAG: ${{ steps.version.outputs.tag }}
|
|
75
|
+
run: echo "Publishing $RELEASE_TAG"
|
|
76
|
+
|
|
77
|
+
- language: yaml
|
|
78
|
+
label: 'Use inline expression in run steps to avoid repeating step-level env blocks'
|
|
79
|
+
code: |
|
|
80
|
+
jobs:
|
|
81
|
+
release:
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- name: Resolve version
|
|
85
|
+
id: version
|
|
86
|
+
run: echo "tag=v2.0.0" >> $GITHUB_OUTPUT
|
|
87
|
+
|
|
88
|
+
- name: Tag Docker image
|
|
89
|
+
# Reference step output directly in run — no env: block needed
|
|
90
|
+
run: echo "Tagging image as ${{ steps.version.outputs.tag }}"
|
|
91
|
+
|
|
92
|
+
- name: Push Docker image
|
|
93
|
+
run: echo "Pushing tag ${{ steps.version.outputs.tag }}"
|
|
94
|
+
prevention:
|
|
95
|
+
- 'Never reference steps.<id>.outputs.* in the jobs.<id>.env: block — use step-level env: or inline expressions in run: instead'
|
|
96
|
+
- 'Enable actionlint locally to catch step output references in job-level env blocks before they reach CI'
|
|
97
|
+
- 'When the same step output is needed in many steps, document it with a comment on the generating step so readers know where to look'
|
|
98
|
+
docs:
|
|
99
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/contexts#context-availability'
|
|
100
|
+
label: 'GitHub Docs: Context availability — which contexts are accessible at each location'
|
|
101
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables'
|
|
102
|
+
label: 'GitHub Docs: Store information in variables'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
id: triggers-036
|
|
2
|
+
title: 'branches-ignore filter does not suppress tag pushes — tags always fire unless tags-ignore is also specified'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- push
|
|
7
|
+
- branches-ignore
|
|
8
|
+
- tags
|
|
9
|
+
- tag-push
|
|
10
|
+
- filter
|
|
11
|
+
- trigger-bypass
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on.*push.*branches-ignore|branches-ignore.*\*\*'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
- regex: 'tag.*push.*trigger.*unexpected|branches-ignore.*tags.*still.*fire'
|
|
16
|
+
flags: 'i'
|
|
17
|
+
error_messages:
|
|
18
|
+
- "Workflow triggered by tag push despite branches-ignore: ['**'] — expected no branch pushes to trigger"
|
|
19
|
+
- 'v1.2.3 tag push fires a workflow that should only run on branch pushes'
|
|
20
|
+
root_cause: |
|
|
21
|
+
GitHub Actions push event filters (branches:, branches-ignore:, paths:, paths-ignore:) apply
|
|
22
|
+
only to branch-ref pushes. Tag pushes target a refs/tags/* ref, not a refs/heads/* ref, and
|
|
23
|
+
are entirely unaffected by branch filters.
|
|
24
|
+
|
|
25
|
+
When a workflow declares on: push with branches-ignore: but no tags: or tags-ignore: filter,
|
|
26
|
+
all tag pushes trigger the workflow unconditionally regardless of the branch filter.
|
|
27
|
+
|
|
28
|
+
A critical misunderstanding: adding branches-ignore: ['**'] looks like it "disables all push
|
|
29
|
+
triggers" but it only disables branch pushes. Tag pushes still fire unconditionally.
|
|
30
|
+
|
|
31
|
+
GitHub documentation states that push event filters are independent per ref type: branch
|
|
32
|
+
filters only affect refs/heads/* pushes, and tag filters only affect refs/tags/* pushes.
|
|
33
|
+
They do not compose across ref types — setting one does not implicitly filter the other.
|
|
34
|
+
|
|
35
|
+
Consequence: release tag pushes (v1.2.3) trigger CI and CD workflows not designed for them,
|
|
36
|
+
potentially running expensive test suites or deploying artifacts on every version tag.
|
|
37
|
+
This is among the top-voted GitHub Community questions about push event triggers and
|
|
38
|
+
appears repeatedly in Stack Overflow answers on the [github-actions] tag.
|
|
39
|
+
fix: |
|
|
40
|
+
Add an explicit tags-ignore: ['**'] filter to prevent all tag pushes from triggering the
|
|
41
|
+
workflow. Alternatively, use an explicit branches: allowlist — but note that branch
|
|
42
|
+
allowlists also do not suppress tag pushes; tags-ignore: is always required separately.
|
|
43
|
+
|
|
44
|
+
For workflows intended ONLY for tags (release workflows), remove branches filters entirely
|
|
45
|
+
and add tags: with the desired semver glob pattern.
|
|
46
|
+
fix_code:
|
|
47
|
+
- language: yaml
|
|
48
|
+
label: 'Add tags-ignore to explicitly suppress all tag pushes alongside branches-ignore'
|
|
49
|
+
code: |
|
|
50
|
+
on:
|
|
51
|
+
push:
|
|
52
|
+
branches-ignore:
|
|
53
|
+
- 'dependabot/**'
|
|
54
|
+
tags-ignore:
|
|
55
|
+
- '**' # Required — branches-ignore does NOT suppress tag pushes
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: 'Release workflow — run only on semver tags, no branch triggers'
|
|
58
|
+
code: |
|
|
59
|
+
on:
|
|
60
|
+
push:
|
|
61
|
+
tags:
|
|
62
|
+
- 'v[0-9]+.[0-9]+.[0-9]+' # Only fires on semver tags like v1.2.3
|
|
63
|
+
# No branches filter — exclusive tag-only trigger
|
|
64
|
+
prevention:
|
|
65
|
+
- "When adding branches or branches-ignore filters, always decide explicitly whether tag pushes should be included or excluded — then set tags: or tags-ignore: accordingly"
|
|
66
|
+
- 'Use actionlint to validate push trigger filter combinations before committing'
|
|
67
|
+
- 'Document the intent in workflow comments: "This workflow runs on branch pushes only. Tag pushes excluded via tags-ignore."'
|
|
68
|
+
- 'Audit release tag workflows to confirm they use tags: filters rather than relying on implicit gaps in branch filters'
|
|
69
|
+
docs:
|
|
70
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#push'
|
|
71
|
+
label: 'GitHub Docs: push event — filter pattern cheat sheet'
|
|
72
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushbranchestagsbranches-ignoretags-ignore'
|
|
73
|
+
label: 'GitHub Docs: on.push branches/tags filter syntax'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
id: triggers-037
|
|
2
|
+
title: 'on.release types: [published] does not fire for pre-releases — prereleased is a separate event type'
|
|
3
|
+
category: triggers
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- release
|
|
7
|
+
- prereleased
|
|
8
|
+
- published
|
|
9
|
+
- event-types
|
|
10
|
+
- pre-release
|
|
11
|
+
- trigger-filter
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: 'on:\s*\n\s+release:\s*\n\s+types:\s*\[.*published(?!.*prereleased)'
|
|
14
|
+
flags: 'i'
|
|
15
|
+
error_messages:
|
|
16
|
+
- "(No error — workflow simply does not trigger when a pre-release is published via the GitHub UI or API)"
|
|
17
|
+
root_cause: |
|
|
18
|
+
GitHub's release event distinguishes two separate activity types for publishing:
|
|
19
|
+
- published: fires when a full (non-pre) release is published (is_prerelease: false)
|
|
20
|
+
- prereleased: fires when a pre-release is published (is_prerelease: true)
|
|
21
|
+
|
|
22
|
+
The published type does NOT include pre-releases. A workflow with
|
|
23
|
+
types: [published] will never fire when "Set as a pre-release" is checked in
|
|
24
|
+
the GitHub UI, or when a release is created with prerelease: true via the Releases API.
|
|
25
|
+
|
|
26
|
+
This silently skips release automation (npm publish, Docker image push, deployment
|
|
27
|
+
pipelines) for pre-release versions (e.g., v2.0.0-rc.1, v1.5.0-beta.3).
|
|
28
|
+
No warning, no skipped-run entry in the Actions tab — the event simply never arrives.
|
|
29
|
+
|
|
30
|
+
Note: the created type fires for both releases AND pre-releases when they are first
|
|
31
|
+
created as drafts, not when published. The released type fires for both
|
|
32
|
+
published and prereleased events and is the simplest way to catch both.
|
|
33
|
+
fix: |
|
|
34
|
+
To fire on BOTH full releases and pre-releases: add prereleased to the types list,
|
|
35
|
+
or switch to types: [released] which fires for both without listing each type.
|
|
36
|
+
|
|
37
|
+
To fire ONLY on pre-releases: use types: [prereleased].
|
|
38
|
+
|
|
39
|
+
To distinguish inside the workflow: check github.event.release.prerelease (boolean).
|
|
40
|
+
fix_code:
|
|
41
|
+
- language: yaml
|
|
42
|
+
label: 'Include prereleased to fire for both full releases and pre-releases'
|
|
43
|
+
code: |
|
|
44
|
+
on:
|
|
45
|
+
release:
|
|
46
|
+
# published fires only for full releases
|
|
47
|
+
# prereleased fires only for pre-releases
|
|
48
|
+
# List both to catch all published releases
|
|
49
|
+
types: [published, prereleased]
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
publish:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- name: Set dist-tag based on release type
|
|
56
|
+
id: meta
|
|
57
|
+
run: |
|
|
58
|
+
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
|
|
59
|
+
echo "dist_tag=next" >> $GITHUB_OUTPUT
|
|
60
|
+
else
|
|
61
|
+
echo "dist_tag=latest" >> $GITHUB_OUTPUT
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
- language: yaml
|
|
65
|
+
label: 'Use released to always fire for both release types without listing each'
|
|
66
|
+
code: |
|
|
67
|
+
on:
|
|
68
|
+
release:
|
|
69
|
+
# released is equivalent to [published, prereleased] — fires for both
|
|
70
|
+
types: [released]
|
|
71
|
+
prevention:
|
|
72
|
+
- 'Always include prereleased in types: if pre-release automation is expected (npm publish, Docker push, deploy)'
|
|
73
|
+
- 'Test release workflows by creating a GitHub pre-release — verify the workflow appears in the Actions tab'
|
|
74
|
+
- 'Use github.event.release.prerelease (boolean) to branch behavior inside the workflow rather than relying on separate trigger types'
|
|
75
|
+
docs:
|
|
76
|
+
- url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#release'
|
|
77
|
+
label: 'GitHub Docs: release event and activity types'
|
|
78
|
+
- url: 'https://docs.github.com/en/rest/releases/releases#create-a-release'
|
|
79
|
+
label: 'GitHub REST API: Create a release (prerelease field)'
|
package/package.json
CHANGED