@htekdev/actions-debugger 1.0.23 → 1.0.25
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/artifact-minimum-retention-one-day.yml +153 -0
- package/errors/caching-artifacts/cache-api-propagation-delay-post-save.yml +128 -0
- package/errors/caching-artifacts/cache-backend-internal-error-skipped.yml +75 -0
- package/errors/caching-artifacts/cache-hit-step-id-case-sensitive-mismatch.yml +95 -0
- package/errors/caching-artifacts/cache-save-post-step-skipped-on-failure.yml +114 -0
- package/errors/concurrency-timing/deploy-pages-in-progress-deployment-wedged.yml +70 -0
- package/errors/concurrency-timing/deployment-review-timeout-expired.yml +88 -0
- package/errors/concurrency-timing/job-concurrency-scope-per-run-not-global.yml +81 -0
- package/errors/concurrency-timing/merge-queue-concurrency-cancel-blocks-all.yml +86 -0
- package/errors/concurrency-timing/reusable-workflow-github-workflow-context-cancel.yml +124 -0
- package/errors/concurrency-timing/runner-scale-set-jobs-never-start.yml +123 -0
- package/errors/concurrency-timing/runner-temp-dir-race-concurrent-workers.yml +90 -0
- package/errors/known-unsolved/artifact-download-url-unauthenticated-404.yml +98 -0
- package/errors/known-unsolved/checkout-v6-credentials-docker-run-manual.yml +105 -0
- package/errors/known-unsolved/concurrency-groups-repo-scoped-only.yml +138 -0
- package/errors/known-unsolved/environment-deployment-false-custom-protection.yml +93 -0
- package/errors/known-unsolved/matrix-256-job-limit.yml +142 -0
- package/errors/known-unsolved/merge-group-paths-filter-not-supported.yml +137 -0
- package/errors/known-unsolved/no-job-allow-failure.yml +73 -0
- package/errors/known-unsolved/schedule-cron-hours-long-queue-drift.yml +101 -0
- package/errors/permissions-auth/checkout-persist-credentials-token-write.yml +90 -0
- package/errors/permissions-auth/checkout-v6-cross-repo-token-override.yml +103 -0
- package/errors/permissions-auth/create-github-app-token-cross-job-token-revoked.yml +95 -0
- package/errors/permissions-auth/github-token-contents-write-missing-git-push.yml +117 -0
- package/errors/permissions-auth/org-actions-policy-blocks-unapproved-action.yml +106 -0
- package/errors/runner-environment/codeql-action-v2-deprecated.yml +110 -0
- package/errors/runner-environment/macos-26-openssl-3-system-library-breaking.yml +114 -0
- package/errors/runner-environment/macos-26-ruby-34-default-upgrade.yml +114 -0
- package/errors/runner-environment/macos-26-xcode-default-265-pin-required.yml +99 -0
- package/errors/runner-environment/macos-latest-label-switches-to-macos26.yml +127 -0
- package/errors/runner-environment/maven-gradle-403-cache-backend-outage.yml +116 -0
- package/errors/runner-environment/node20-removed-toolcache-default-node22.yml +104 -0
- package/errors/runner-environment/powershell-74-76-threadjob-module-rename.yml +124 -0
- package/errors/runner-environment/self-hosted-runner-not-found.yml +134 -0
- package/errors/runner-environment/self-hosted-runner-selinux-service-exec-failure.yml +116 -0
- package/errors/runner-environment/service-container-no-healthcheck.yml +158 -0
- package/errors/runner-environment/setup-node-v5-corepack-pnpm-not-found.yml +101 -0
- package/errors/runner-environment/setup-node-yarn-not-installed-self-hosted.yml +76 -0
- package/errors/runner-environment/setup-python-externally-managed-env-error.yml +95 -0
- package/errors/runner-environment/windows-2019-runner-retired-june2025.yml +118 -0
- package/errors/runner-environment/windows-2022-docker-daemon-not-started.yml +108 -0
- package/errors/silent-failures/cache-hit-output-string-not-boolean.yml +96 -0
- package/errors/silent-failures/checkout-lfs-pointer-not-content.yml +105 -0
- package/errors/silent-failures/reusable-workflow-output-skipped-contains-secret.yml +115 -0
- package/errors/silent-failures/setup-node-silent-download-exit-zero.yml +105 -0
- package/errors/silent-failures/setup-python-truncated-manifest-silent-exit.yml +111 -0
- package/errors/silent-failures/undefined-env-expression-empty-string-silent.yml +115 -0
- package/errors/silent-failures/windows-powershell-github-output-bash-syntax.yml +118 -0
- package/errors/triggers/fork-pr-first-time-contributor-approval-required.yml +142 -0
- package/errors/triggers/on-push-branches-glob-star-no-slash-match.yml +78 -0
- package/errors/triggers/pull-request-target-env-protection-default-branch-eval.yml +117 -0
- package/errors/triggers/required-status-check-renamed-never-passes.yml +87 -0
- package/errors/triggers/schedule-cron-self-hosted-runner-not-triggered.yml +107 -0
- package/errors/yaml-syntax/case-function-runner-version-too-old.yml +100 -0
- package/errors/yaml-syntax/composite-action-run-shell-missing.yml +90 -0
- package/errors/yaml-syntax/composite-action-secrets-context-unavailable.yml +99 -0
- package/errors/yaml-syntax/github-script-octokit-renamed-to-github.yml +130 -0
- package/errors/yaml-syntax/labeler-v5-config-format-breaking.yml +67 -0
- package/errors/yaml-syntax/runs-on-expression-array-syntax-error.yml +121 -0
- package/errors/yaml-syntax/setup-go-matrix-version-float-coercion.yml +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
id: known-unsolved-027
|
|
2
|
+
title: "Artifact Download URLs Require GitHub Authentication (404 for Unauthenticated Users)"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- upload-artifact
|
|
7
|
+
- download
|
|
8
|
+
- authentication
|
|
9
|
+
- public-access
|
|
10
|
+
- known-limitation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "artifact.*download.*404|artifact.*not found.*auth"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "artifact.*only.*registered|artifact.*guest.*not found"
|
|
15
|
+
flags: "i"
|
|
16
|
+
error_messages:
|
|
17
|
+
- "404 Not Found when downloading artifact URL without a GitHub session"
|
|
18
|
+
- "Resource not accessible by integration"
|
|
19
|
+
- "Must have push access to repository to list workflow run artifacts"
|
|
20
|
+
root_cause: |
|
|
21
|
+
All GitHub Actions artifact download URLs — both the UI links on the Actions tab and the archive
|
|
22
|
+
URLs returned by the REST API (GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/zip)
|
|
23
|
+
— require GitHub authentication. Unauthenticated visitors to public repositories receive a
|
|
24
|
+
404 Not Found response regardless of the artifact's age or the repository's visibility.
|
|
25
|
+
|
|
26
|
+
This was not always the case. Prior to a GitHub platform change (around late 2020), direct
|
|
27
|
+
artifact archive URLs were publicly accessible without a GitHub account. The change was made
|
|
28
|
+
without a formal deprecation announcement, breaking projects that distributed nightly builds
|
|
29
|
+
or pre-release binaries via artifact URLs linked from documentation or release pages.
|
|
30
|
+
|
|
31
|
+
The REST API endpoint for listing artifacts (GET /repos/{owner}/{repo}/actions/artifacts)
|
|
32
|
+
also requires authentication, preventing anonymous services from discovering or linking
|
|
33
|
+
current artifact URLs.
|
|
34
|
+
|
|
35
|
+
185 reactions on actions/upload-artifact#51 (open since Feb 2020).
|
|
36
|
+
fix: |
|
|
37
|
+
There is no way to make GitHub Actions artifacts publicly accessible without authentication.
|
|
38
|
+
|
|
39
|
+
Alternatives for distributing build outputs to unauthenticated users:
|
|
40
|
+
1. GitHub Releases — release assets attached via `softprops/action-gh-release` are publicly
|
|
41
|
+
downloadable without a GitHub account.
|
|
42
|
+
2. GitHub Pages — deploy build outputs as static files; pages are publicly accessible.
|
|
43
|
+
3. External storage (S3, GCS, Azure Blob Storage) — upload artifacts to a public bucket
|
|
44
|
+
and provide a stable URL.
|
|
45
|
+
4. nightly.link proxy — a free service that fetches artifacts via GitHub App auth and
|
|
46
|
+
provides a public redirect URL. URL format:
|
|
47
|
+
https://nightly.link/{owner}/{repo}/workflows/{workflow}/{branch}/{artifact-name}.zip
|
|
48
|
+
fix_code:
|
|
49
|
+
- language: yaml
|
|
50
|
+
label: "Alternative: publish as GitHub Release asset (publicly accessible)"
|
|
51
|
+
code: |
|
|
52
|
+
jobs:
|
|
53
|
+
build-and-release:
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
permissions:
|
|
56
|
+
contents: write
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- run: ./build.sh -o dist/output.zip
|
|
60
|
+
|
|
61
|
+
# Release assets are publicly downloadable without GitHub login
|
|
62
|
+
- uses: softprops/action-gh-release@v2
|
|
63
|
+
with:
|
|
64
|
+
tag_name: nightly-${{ github.run_number }}
|
|
65
|
+
files: dist/output.zip
|
|
66
|
+
prerelease: true
|
|
67
|
+
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "Alternative: deploy to GitHub Pages (publicly accessible static files)"
|
|
70
|
+
code: |
|
|
71
|
+
jobs:
|
|
72
|
+
deploy-pages:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
permissions:
|
|
75
|
+
pages: write
|
|
76
|
+
id-token: write
|
|
77
|
+
environment:
|
|
78
|
+
name: github-pages
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
- run: ./build.sh -o ./public
|
|
82
|
+
|
|
83
|
+
- uses: actions/upload-pages-artifact@v3
|
|
84
|
+
with:
|
|
85
|
+
path: ./public
|
|
86
|
+
- uses: actions/deploy-pages@v4
|
|
87
|
+
prevention:
|
|
88
|
+
- "Never publish artifact download URLs as public links — they are always authentication-gated."
|
|
89
|
+
- "For public distribution of build outputs, use GitHub Releases, GitHub Pages, or an external CDN."
|
|
90
|
+
- "Use nightly.link for zero-config public access to the latest artifact in open-source projects."
|
|
91
|
+
- "Document the authentication requirement in README when directing contributors to artifacts."
|
|
92
|
+
docs:
|
|
93
|
+
- url: "https://github.com/actions/upload-artifact/issues/51"
|
|
94
|
+
label: "actions/upload-artifact #51 — Artifact URLs require GitHub account (185 reactions)"
|
|
95
|
+
- url: "https://nightly.link"
|
|
96
|
+
label: "nightly.link — Public proxy for GitHub Actions artifacts"
|
|
97
|
+
- url: "https://docs.github.com/en/rest/actions/artifacts"
|
|
98
|
+
label: "GitHub REST API — Actions Artifacts (requires auth)"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
id: known-unsolved-024
|
|
2
|
+
title: "checkout@v6 Git Credentials Inaccessible Inside Manually Invoked Docker Containers"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- docker
|
|
8
|
+
- credentials
|
|
9
|
+
- git-auth
|
|
10
|
+
- includeif
|
|
11
|
+
- v6
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "fatal: could not read Username for.*No such device or address"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "fatal: Authentication failed for.*github\\.com"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "includeIf\\.gitdir.*git-credentials.*config"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "fatal: could not read Username for 'https://github.com': No such device or address"
|
|
21
|
+
- "fatal: Authentication failed for 'https://github.com/org/repo.git'"
|
|
22
|
+
- "remote: Repository not found."
|
|
23
|
+
root_cause: |
|
|
24
|
+
actions/checkout@v6 changed the credential persistence mechanism from HTTP Authorization
|
|
25
|
+
headers written directly into .git/config (the v5 approach) to path-based includeIf.gitdir
|
|
26
|
+
directives that reference a credentials file stored in $RUNNER_TEMP.
|
|
27
|
+
|
|
28
|
+
This mechanism works for GitHub-managed Docker container actions (using: docker), where
|
|
29
|
+
the runner automatically mounts $RUNNER_TEMP into the container as /github/runner_temp
|
|
30
|
+
and configures paths to match. However, it breaks for any workflow step that runs its own
|
|
31
|
+
docker run commands or scripts that spin up Docker containers manually, because:
|
|
32
|
+
|
|
33
|
+
1. The $RUNNER_TEMP directory is not mounted into the custom container.
|
|
34
|
+
2. Even if mounted, the includeIf.gitdir path condition requires the workspace to be
|
|
35
|
+
mounted at /github/workspace for the condition to evaluate as true.
|
|
36
|
+
3. Any docker run at a non-standard path will not satisfy the gitdir condition even if
|
|
37
|
+
the credentials file is accessible.
|
|
38
|
+
|
|
39
|
+
Any workflow step that runs docker run outside of a formal Docker action and then performs
|
|
40
|
+
authenticated repository operations (fetch, clone, publish) inside that container will
|
|
41
|
+
fail to authenticate, even with persist-credentials: true.
|
|
42
|
+
|
|
43
|
+
As of June 2026, a fix is in progress upstream (switching from includeIf.gitdir to
|
|
44
|
+
include.path would eliminate the path-matching requirement) but not yet released.
|
|
45
|
+
No fully clean universal workaround exists.
|
|
46
|
+
Reported in actions/checkout#2359 (12 reactions, open Jan 2026).
|
|
47
|
+
fix: |
|
|
48
|
+
No complete fix available until checkout releases an updated credential mechanism.
|
|
49
|
+
|
|
50
|
+
Option 1 (recommended until fix released): Pin to actions/checkout@v5.
|
|
51
|
+
v5 uses HTTP Authorization headers written directly into .git/config, visible inside any
|
|
52
|
+
Docker container that mounts the workspace. No path-based includeIf is involved.
|
|
53
|
+
|
|
54
|
+
Option 2: Mount RUNNER_TEMP at the expected path inside docker run.
|
|
55
|
+
Add -v "$RUNNER_TEMP:/github/runner_temp" to the docker run command AND mount the
|
|
56
|
+
workspace at /github/workspace. Both mounts are required simultaneously.
|
|
57
|
+
|
|
58
|
+
Option 3: Pass the token explicitly to the container via environment variable.
|
|
59
|
+
Set the GitHub token as an env variable and configure the credential helper inside
|
|
60
|
+
the container entrypoint to use it via the token-embedded remote URL.
|
|
61
|
+
fix_code:
|
|
62
|
+
- language: yaml
|
|
63
|
+
label: "Option 1: Pin to checkout@v5 (recommended until upstream fix)"
|
|
64
|
+
code: |
|
|
65
|
+
- name: Checkout repository
|
|
66
|
+
uses: actions/checkout@v5
|
|
67
|
+
with:
|
|
68
|
+
fetch-depth: 0
|
|
69
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "Option 2: Mount RUNNER_TEMP and workspace at expected paths"
|
|
72
|
+
code: |
|
|
73
|
+
- name: Checkout repository
|
|
74
|
+
uses: actions/checkout@v6
|
|
75
|
+
with:
|
|
76
|
+
persist-credentials: true
|
|
77
|
+
- name: Run docker with credentials accessible
|
|
78
|
+
run: |
|
|
79
|
+
docker run \
|
|
80
|
+
-v "$GITHUB_WORKSPACE:/github/workspace" \
|
|
81
|
+
-v "$RUNNER_TEMP:/github/runner_temp" \
|
|
82
|
+
-w /github/workspace \
|
|
83
|
+
my-image:latest ./scripts/build.sh
|
|
84
|
+
- language: yaml
|
|
85
|
+
label: "Option 3: Pass token to container entrypoint"
|
|
86
|
+
code: |
|
|
87
|
+
- name: Run docker with explicit token
|
|
88
|
+
env:
|
|
89
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
90
|
+
run: |
|
|
91
|
+
docker run -e GH_TOKEN="${GH_TOKEN}" my-image:latest ./scripts/release.sh
|
|
92
|
+
prevention:
|
|
93
|
+
- "Do not upgrade to checkout@v6 if your workflow contains manual docker run steps that perform authenticated repository operations inside the container."
|
|
94
|
+
- "Use formal Docker container actions (using: docker in action.yml) instead of manual docker run steps when repository operations inside containers are needed."
|
|
95
|
+
- "Pin checkout to an explicit version and use Dependabot/Renovate to control upgrade timing."
|
|
96
|
+
- "Track actions/checkout#2359 for the upstream fix when released."
|
|
97
|
+
docs:
|
|
98
|
+
- url: "https://github.com/actions/checkout/issues/2359"
|
|
99
|
+
label: "actions/checkout#2359 — checkout@v6 credentials broken with Docker container actions (Jan 2026)"
|
|
100
|
+
- url: "https://github.com/actions/checkout/issues/2386"
|
|
101
|
+
label: "actions/checkout#2386 — related Docker credential issue"
|
|
102
|
+
- url: "https://github.com/actions/checkout/releases/tag/v6.0.0"
|
|
103
|
+
label: "checkout v6.0.0 release notes — credential mechanism change"
|
|
104
|
+
- url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-docker-container-action"
|
|
105
|
+
label: "Creating a Docker container action"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
id: known-unsolved-023
|
|
2
|
+
title: "Concurrency Groups Are Repository-Scoped — Cannot Synchronize Workflows Across Repositories"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- concurrency
|
|
7
|
+
- cross-repository
|
|
8
|
+
- scoping
|
|
9
|
+
- deployment
|
|
10
|
+
- mutex
|
|
11
|
+
- known-limit
|
|
12
|
+
- organization
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: "concurrency.*cross.?repo|cross.?repo.*concurrency"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "concurrency.*organization|global.*concurrency|multi.?repo.*concurrency"
|
|
17
|
+
flags: "i"
|
|
18
|
+
- regex: "concurrency only works within a single repository"
|
|
19
|
+
flags: "i"
|
|
20
|
+
error_messages:
|
|
21
|
+
- "GitHub Actions concurrency only works within a single repository"
|
|
22
|
+
- "Concurrency groups cannot be shared across repositories — identical group names in different repos run in parallel"
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub Actions `concurrency:` groups are evaluated strictly within a single repository.
|
|
25
|
+
Two workflows in different repositories that define identical `concurrency.group` strings
|
|
26
|
+
(e.g., `group: deploy-production`) run in PARALLEL with no coordination — the group
|
|
27
|
+
name has no cross-repository meaning.
|
|
28
|
+
|
|
29
|
+
This is a hard platform-level scope boundary. GitHub's concurrency implementation stores
|
|
30
|
+
group membership state per-repository, so there is no shared namespace between repos.
|
|
31
|
+
|
|
32
|
+
Common scenarios where this surprises teams:
|
|
33
|
+
- Multiple application repos deploying to a shared staging/production environment
|
|
34
|
+
- A monorepo split into microservice repos that must not deploy simultaneously
|
|
35
|
+
- Platform teams providing reusable deploy workflows called from many repos
|
|
36
|
+
- Organizations trying to limit total simultaneous cloud deployments across a fleet
|
|
37
|
+
- Trying to prevent two repos from running the same database migration at once
|
|
38
|
+
|
|
39
|
+
The behavior is counterintuitive because the same concurrency group string CAN
|
|
40
|
+
coordinate same-repo workflows — so teams naturally assume it works cross-repo too.
|
|
41
|
+
|
|
42
|
+
Source: Stack Overflow #79690697 (Make workflow run sequentially for reusable workflow
|
|
43
|
+
— accepted answer explicitly states: "GitHub Actions' concurrency only works within
|
|
44
|
+
a single repository")
|
|
45
|
+
Source: ben-z/gh-action-mutex (community workaround for cross-repo mutex, 500+ stars)
|
|
46
|
+
Source: GitHub Actions docs (concurrency group scope is per-repository)
|
|
47
|
+
fix: |
|
|
48
|
+
There is no native GitHub Actions concurrency primitive that works cross-repository.
|
|
49
|
+
Use one of these architectural workarounds:
|
|
50
|
+
|
|
51
|
+
Workaround 1 — Orchestrator repository with repository_dispatch (recommended):
|
|
52
|
+
Create a central orchestrator repo. Each application repo sends a
|
|
53
|
+
`repository_dispatch` event to the orchestrator. The orchestrator runs the shared
|
|
54
|
+
workflow sequentially using its own concurrency group (single-repo scope is fine
|
|
55
|
+
here because all dispatch events arrive at one repo).
|
|
56
|
+
|
|
57
|
+
Workaround 2 — External mutex via ben-z/gh-action-mutex:
|
|
58
|
+
Uses a dedicated lock repository's branch as a distributed mutex. Works across
|
|
59
|
+
repos because the lock state lives in one shared repository.
|
|
60
|
+
|
|
61
|
+
Workaround 3 — Environment deployment protection with single-slot reviewers:
|
|
62
|
+
A deployment environment with required reviewers can throttle approval-gated
|
|
63
|
+
deployments. Multiple jobs can still enter "waiting" simultaneously, but the
|
|
64
|
+
gate prevents concurrent execution if reviewers approve only one at a time.
|
|
65
|
+
|
|
66
|
+
Workaround 4 — External coordination service (Redis, DynamoDB, Azure Service Bus):
|
|
67
|
+
For production systems at scale, use a lock/queue service all workflows can reach.
|
|
68
|
+
Requires infrastructure management but provides the most reliable behavior.
|
|
69
|
+
fix_code:
|
|
70
|
+
- language: yaml
|
|
71
|
+
label: "Orchestrator pattern: all repos dispatch to a central repo that serializes deploys"
|
|
72
|
+
code: |
|
|
73
|
+
# In each application repo — sends dispatch to orchestrator
|
|
74
|
+
name: Trigger Orchestrated Deploy
|
|
75
|
+
on:
|
|
76
|
+
push:
|
|
77
|
+
branches: [main]
|
|
78
|
+
jobs:
|
|
79
|
+
dispatch:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- name: Dispatch to orchestrator
|
|
83
|
+
run: |
|
|
84
|
+
gh api /repos/myorg/deploy-orchestrator/dispatches \
|
|
85
|
+
--method POST \
|
|
86
|
+
-F event_type=deploy \
|
|
87
|
+
-F client_payload[repo]="${{ github.repository }}" \
|
|
88
|
+
-F client_payload[sha]="${{ github.sha }}"
|
|
89
|
+
env:
|
|
90
|
+
GH_TOKEN: ${{ secrets.ORCHESTRATOR_DISPATCH_PAT }}
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
# In deploy-orchestrator repo — single-repo concurrency works correctly here
|
|
94
|
+
name: Orchestrated Deploy
|
|
95
|
+
on:
|
|
96
|
+
repository_dispatch:
|
|
97
|
+
types: [deploy]
|
|
98
|
+
concurrency:
|
|
99
|
+
group: deploy-production # Scoped to orchestrator repo — works as intended
|
|
100
|
+
cancel-in-progress: false # Queue all deployments; never skip one
|
|
101
|
+
jobs:
|
|
102
|
+
deploy:
|
|
103
|
+
runs-on: ubuntu-latest
|
|
104
|
+
steps:
|
|
105
|
+
- run: |
|
|
106
|
+
echo "Deploying ${{ github.event.client_payload.repo }} @ ${{ github.event.client_payload.sha }}"
|
|
107
|
+
# ... deploy logic ...
|
|
108
|
+
|
|
109
|
+
- language: yaml
|
|
110
|
+
label: "External mutex using ben-z/gh-action-mutex for cross-repo locking"
|
|
111
|
+
code: |
|
|
112
|
+
jobs:
|
|
113
|
+
deploy:
|
|
114
|
+
runs-on: ubuntu-latest
|
|
115
|
+
steps:
|
|
116
|
+
- name: Acquire cross-repo deployment lock
|
|
117
|
+
uses: ben-z/gh-action-mutex@v1.0-alpha-7
|
|
118
|
+
with:
|
|
119
|
+
branch: mutex-deploy-production # Branch in the shared lock repo
|
|
120
|
+
repo-token: ${{ secrets.MUTEX_REPO_PAT }}
|
|
121
|
+
repository: myorg/locks # Dedicated lock repository
|
|
122
|
+
|
|
123
|
+
- name: Deploy
|
|
124
|
+
run: ./deploy.sh
|
|
125
|
+
|
|
126
|
+
# Lock is automatically released when the job completes (success or failure)
|
|
127
|
+
prevention:
|
|
128
|
+
- "Do not assume identical concurrency group names coordinate across repositories — they have no cross-repo effect."
|
|
129
|
+
- "Design cross-repo deployment sequencing with an explicit architecture before scaling to multiple repos."
|
|
130
|
+
- "Document the serialization mechanism in your runbooks — this is a common architecture surprise for new team members."
|
|
131
|
+
- "Prefer a single orchestrator repo pattern from the start when cross-repo ordering is a requirement."
|
|
132
|
+
docs:
|
|
133
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/using-concurrency"
|
|
134
|
+
label: "GitHub Docs: Using concurrency in GitHub Actions (repo-scoped)"
|
|
135
|
+
- url: "https://stackoverflow.com/questions/79690697/make-workflow-run-sequentially-allowing-one-job-run-at-a-time-for-github-action-reusable-workflow"
|
|
136
|
+
label: "Stack Overflow #79690697: Cross-repo sequential GitHub Actions execution"
|
|
137
|
+
- url: "https://github.com/ben-z/gh-action-mutex"
|
|
138
|
+
label: "ben-z/gh-action-mutex — cross-repo mutex via shared lock repository"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: known-unsolved-029
|
|
2
|
+
title: "deployment:false environment key incompatible with custom deployment protection rules"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- environment
|
|
7
|
+
- deployment
|
|
8
|
+
- protection-rules
|
|
9
|
+
- custom-deployment-protection
|
|
10
|
+
- no-fix
|
|
11
|
+
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "You cannot (use|disable) deployment.*custom deployment protection"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "deployment: false.*is not supported.*protection rule"
|
|
16
|
+
flags: "i"
|
|
17
|
+
|
|
18
|
+
error_messages:
|
|
19
|
+
- "You cannot use deployment: false when a custom deployment protection rule is configured for this environment."
|
|
20
|
+
- "Environments with custom deployment protection rules require automatic deployment tracking. Remove deployment: false to proceed."
|
|
21
|
+
|
|
22
|
+
root_cause: |
|
|
23
|
+
GitHub Actions added deployment: false as a new key for environments in March 2026
|
|
24
|
+
(changelog 2026-03-19). This key lets workflows use environment secrets and variables
|
|
25
|
+
without creating a GitHub Deployment record, which was a long-requested feature for
|
|
26
|
+
teams who want secrets management without polluting the Deployments tab.
|
|
27
|
+
|
|
28
|
+
However, the deployment: false key is fundamentally incompatible with custom deployment
|
|
29
|
+
protection rules. Custom deployment protection rules (third-party apps registered via
|
|
30
|
+
the GitHub API to gate deployments) rely on GitHub Deployment events being created —
|
|
31
|
+
the protection rule callback fires when a deployment is created, approves or rejects it,
|
|
32
|
+
and the workflow proceeds based on the response.
|
|
33
|
+
|
|
34
|
+
When deployment: false is set, no Deployment record is created, so there is no event
|
|
35
|
+
to trigger the custom protection rule callback. GitHub therefore blocks the combination
|
|
36
|
+
entirely rather than silently skipping the protection rule check, which could create
|
|
37
|
+
a security bypass.
|
|
38
|
+
|
|
39
|
+
This is a platform-level design constraint with no workaround. Teams that need custom
|
|
40
|
+
deployment protection rules cannot use deployment: false and must keep auto-deployment
|
|
41
|
+
enabled.
|
|
42
|
+
|
|
43
|
+
fix: |
|
|
44
|
+
There is no workaround. If your environment uses a custom deployment protection rule
|
|
45
|
+
(a third-party app that approves or rejects deployments), you cannot use deployment: false.
|
|
46
|
+
|
|
47
|
+
Options:
|
|
48
|
+
1. Remove the custom deployment protection rule if it is no longer needed, then use
|
|
49
|
+
deployment: false to stop creating Deployment records.
|
|
50
|
+
2. Keep auto-deployment enabled (omit deployment: false) and accept that deployments
|
|
51
|
+
will appear in the Deployments tab.
|
|
52
|
+
3. Use a different secrets management strategy (repository secrets, organization secrets,
|
|
53
|
+
or HashiCorp Vault) if you want to avoid environment-scoped secrets entirely.
|
|
54
|
+
|
|
55
|
+
fix_code:
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Valid: deployment:false without custom protection rules"
|
|
58
|
+
code: |
|
|
59
|
+
# Works only when the environment has NO custom deployment protection rules
|
|
60
|
+
jobs:
|
|
61
|
+
deploy:
|
|
62
|
+
environment:
|
|
63
|
+
name: staging
|
|
64
|
+
deployment: false # Suppresses Deployment record creation
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- run: echo "Using ${{ secrets.STAGING_API_KEY }} without creating a deployment"
|
|
68
|
+
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: "Required: keep deployment enabled when custom protection rules are configured"
|
|
71
|
+
code: |
|
|
72
|
+
# When a custom deployment protection rule (third-party app) is registered
|
|
73
|
+
# on the environment, omit deployment:false entirely
|
|
74
|
+
jobs:
|
|
75
|
+
deploy:
|
|
76
|
+
environment:
|
|
77
|
+
name: production # Has custom protection rule — must create deployment
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
steps:
|
|
80
|
+
- run: echo "Deployment record created; custom protection rule fires and must approve"
|
|
81
|
+
|
|
82
|
+
prevention:
|
|
83
|
+
- "Check environment settings in Settings > Environments before adding deployment: false to confirm no custom protection rules are registered."
|
|
84
|
+
- "Document which environments use custom protection rules so team members know deployment: false is unavailable for those environments."
|
|
85
|
+
- "If you need both secrets management and no deployment records, consider migrating secrets to organization-level secrets with repository access restrictions."
|
|
86
|
+
|
|
87
|
+
docs:
|
|
88
|
+
- url: "https://github.blog/changelog/2026-03-19-github-actions-late-march-2026-updates/"
|
|
89
|
+
label: "GitHub Changelog 2026-03-19: Environments without auto-deployment (deployment: false)"
|
|
90
|
+
- url: "https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#using-an-environment-without-auto-deployment"
|
|
91
|
+
label: "GitHub Docs: Using an environment without auto-deployment"
|
|
92
|
+
- url: "https://docs.github.com/en/actions/deployment/protecting-deployments/creating-custom-deployment-protection-rules"
|
|
93
|
+
label: "GitHub Docs: Creating custom deployment protection rules"
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
id: known-unsolved-022
|
|
2
|
+
title: "Job Matrix Hard Limit: Maximum 256 Jobs Per Workflow Run"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- matrix
|
|
7
|
+
- strategy
|
|
8
|
+
- job-limit
|
|
9
|
+
- scale
|
|
10
|
+
- known-limit
|
|
11
|
+
- monorepo
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "256 jobs|matrix.*256|256.*matrix"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "Job matrix generate.*more than|maximum.*jobs.*workflow.*run"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "matrix.*maximum.*jobs|generates more than 256"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "Job matrix generates more than 256 jobs"
|
|
21
|
+
- "A job matrix can generate a maximum of 256 jobs per workflow run"
|
|
22
|
+
- "The job was not run because it would exceed the maximum number of jobs in a workflow run"
|
|
23
|
+
root_cause: |
|
|
24
|
+
GitHub Actions enforces a hard limit of 256 jobs per workflow run for job matrix strategies.
|
|
25
|
+
This limit applies equally to GitHub-hosted and self-hosted runners and cannot be raised
|
|
26
|
+
via repository or organization settings.
|
|
27
|
+
|
|
28
|
+
The limit is evaluated at workflow run creation time against the total number of matrix
|
|
29
|
+
job combinations. If the computed combination count exceeds 256, the run is blocked.
|
|
30
|
+
|
|
31
|
+
Common matrix combinations that silently approach or exceed the limit:
|
|
32
|
+
- 3 OS × 4 Node.js versions × 22 packages = 264 jobs ❌
|
|
33
|
+
- 4 cloud regions × 4 environments × 17 services = 272 jobs ❌
|
|
34
|
+
- 5 browsers × 4 OS × 13 test shards = 260 jobs ❌
|
|
35
|
+
|
|
36
|
+
This is a long-standing documented constraint. As of the GitHub Actions limits page,
|
|
37
|
+
the 256-job cap has not been increased and no per-organization override is available.
|
|
38
|
+
|
|
39
|
+
Practical impact grows over time: monorepos and test suites that start within the limit
|
|
40
|
+
gradually approach it as services and configuration dimensions are added, with no
|
|
41
|
+
warning until a push suddenly fails workflow creation.
|
|
42
|
+
|
|
43
|
+
Source: GitHub Docs — Usage limits (256 jobs / workflow run)
|
|
44
|
+
Source: cloudposse/github-action-matrix-extended (widely-used community workaround)
|
|
45
|
+
Source: huggingface/transformers PR#28773 (real-world 2-level split at scale)
|
|
46
|
+
fix: |
|
|
47
|
+
There is no way to raise the 256-job limit via settings. Use one of these workarounds:
|
|
48
|
+
|
|
49
|
+
Workaround 1 — Nested matrix via reusable workflows (extends limit to 256² = 65,536):
|
|
50
|
+
Split work into "slices" at the top-level matrix (up to 256 slices), then call a
|
|
51
|
+
reusable workflow that runs its own matrix (up to 256 jobs per slice). Each slice
|
|
52
|
+
× jobs combination stays within the per-run limit.
|
|
53
|
+
|
|
54
|
+
Workaround 2 — `github-action-matrix-extended` by cloudposse:
|
|
55
|
+
Automates the nested slice pattern and supports up to 3 levels (256³ theoretical max).
|
|
56
|
+
Outputs JSON structure consumed by nested matrix strategy calls.
|
|
57
|
+
|
|
58
|
+
Workaround 3 — Path-change filtering via `dorny/paths-filter`:
|
|
59
|
+
Only generate matrix entries for packages/services that changed in the PR. Keeps
|
|
60
|
+
CI fast AND well under the job limit for most runs.
|
|
61
|
+
|
|
62
|
+
Workaround 4 — Reduce matrix dimensions with `include`/`exclude`:
|
|
63
|
+
Test all OS on the latest version; test all versions on one OS. Use `exclude:` to
|
|
64
|
+
prune uncommon OS × version combinations that rarely catch real issues.
|
|
65
|
+
fix_code:
|
|
66
|
+
- language: yaml
|
|
67
|
+
label: "Nested reusable workflow split: top-level generates slices, called workflow runs sub-matrix"
|
|
68
|
+
code: |
|
|
69
|
+
# outer.yml — up to 256 slices × called workflow's matrix (256 each)
|
|
70
|
+
name: Test Suite
|
|
71
|
+
on: [push]
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
generate-slices:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
outputs:
|
|
77
|
+
slices: ${{ steps.gen.outputs.slices }}
|
|
78
|
+
steps:
|
|
79
|
+
- id: gen
|
|
80
|
+
run: |
|
|
81
|
+
# Generate slice IDs from your service list (split into groups)
|
|
82
|
+
python3 -c "
|
|
83
|
+
import json, math
|
|
84
|
+
services = ['svc-' + str(i) for i in range(300)]
|
|
85
|
+
slice_size = 10
|
|
86
|
+
slices = [services[i:i+slice_size] for i in range(0, len(services), slice_size)]
|
|
87
|
+
print('slices=' + json.dumps([json.dumps(s) for s in slices]))
|
|
88
|
+
" >> $GITHUB_OUTPUT
|
|
89
|
+
|
|
90
|
+
run-slice:
|
|
91
|
+
needs: generate-slices
|
|
92
|
+
strategy:
|
|
93
|
+
matrix:
|
|
94
|
+
slice: ${{ fromJSON(needs.generate-slices.outputs.slices) }}
|
|
95
|
+
fail-fast: false
|
|
96
|
+
uses: ./.github/workflows/test-slice.yml
|
|
97
|
+
with:
|
|
98
|
+
services: ${{ matrix.slice }} # JSON string of services for this slice
|
|
99
|
+
|
|
100
|
+
- language: yaml
|
|
101
|
+
label: "Path filtering: only run matrix jobs for changed packages"
|
|
102
|
+
code: |
|
|
103
|
+
jobs:
|
|
104
|
+
detect-changes:
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
outputs:
|
|
107
|
+
matrix: ${{ steps.filter.outputs.changes }}
|
|
108
|
+
steps:
|
|
109
|
+
- uses: actions/checkout@v4
|
|
110
|
+
- id: filter
|
|
111
|
+
uses: dorny/paths-filter@v3
|
|
112
|
+
with:
|
|
113
|
+
filters: |
|
|
114
|
+
service-a: ['services/a/**']
|
|
115
|
+
service-b: ['services/b/**']
|
|
116
|
+
service-c: ['services/c/**']
|
|
117
|
+
# ... define one filter per service
|
|
118
|
+
|
|
119
|
+
test:
|
|
120
|
+
needs: detect-changes
|
|
121
|
+
if: needs.detect-changes.outputs.matrix != '[]'
|
|
122
|
+
strategy:
|
|
123
|
+
matrix:
|
|
124
|
+
service: ${{ fromJSON(needs.detect-changes.outputs.matrix) }}
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@v4
|
|
128
|
+
- run: npm test --workspace=services/${{ matrix.service }}
|
|
129
|
+
prevention:
|
|
130
|
+
- "Track total matrix job count — when exceeding 200, plan a nested split before hitting the 256 wall."
|
|
131
|
+
- "Implement path-change filtering early in the project lifecycle to prevent combinatorial explosion."
|
|
132
|
+
- "Prefer `include`/`exclude` pruning over full combinatorial matrices for OS × version coverage."
|
|
133
|
+
- "Design your CI architecture with the 256-job ceiling in mind, especially for monorepos."
|
|
134
|
+
docs:
|
|
135
|
+
- url: "https://docs.github.com/en/actions/administering-github-actions/usage-limits-billing-and-administration#usage-limits"
|
|
136
|
+
label: "GitHub Docs: Usage limits — Job Matrix (256 jobs per workflow run)"
|
|
137
|
+
- url: "https://github.com/cloudposse/github-action-matrix-extended"
|
|
138
|
+
label: "cloudposse/github-action-matrix-extended — nested matrix workaround"
|
|
139
|
+
- url: "https://github.com/dorny/paths-filter"
|
|
140
|
+
label: "dorny/paths-filter — change-based matrix filtering"
|
|
141
|
+
- url: "https://github.com/huggingface/transformers/pull/28773"
|
|
142
|
+
label: "huggingface/transformers PR#28773: Real-world 2-level matrix split"
|