@htekdev/actions-debugger 1.0.23 → 1.0.24
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/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/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/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/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,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"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
id: known-unsolved-028
|
|
2
|
+
title: "merge_group Event Does Not Support paths, branches, or tags Filters"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- merge_group
|
|
7
|
+
- merge-queue
|
|
8
|
+
- paths
|
|
9
|
+
- branches
|
|
10
|
+
- filters
|
|
11
|
+
- monorepo
|
|
12
|
+
- known-limitation
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: "merge_group:\\s*\\n\\s+paths:"
|
|
15
|
+
flags: "ms"
|
|
16
|
+
- regex: "merge_group:\\s*\\n\\s+branches:"
|
|
17
|
+
flags: "ms"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "on:\n merge_group:\n paths:\n - 'src/**'"
|
|
20
|
+
- "workflow triggered for merge_group despite paths filter"
|
|
21
|
+
root_cause: |
|
|
22
|
+
The merge_group event (used with GitHub's merge queue feature) does not support
|
|
23
|
+
the standard event activity filters that other events support. Specifically,
|
|
24
|
+
the following filters are silently IGNORED when listed under merge_group:
|
|
25
|
+
|
|
26
|
+
- paths and paths-ignore
|
|
27
|
+
- branches and branches-ignore
|
|
28
|
+
- tags
|
|
29
|
+
|
|
30
|
+
When a workflow defines:
|
|
31
|
+
on:
|
|
32
|
+
pull_request:
|
|
33
|
+
paths: ['src/**']
|
|
34
|
+
merge_group:
|
|
35
|
+
paths: ['src/**'] # silently ignored
|
|
36
|
+
|
|
37
|
+
The paths filter applies to pull_request but NOT to merge_group. The workflow
|
|
38
|
+
always triggers for every merge_group event regardless of which files changed,
|
|
39
|
+
even if no files in src/** were modified.
|
|
40
|
+
|
|
41
|
+
This is particularly painful for monorepos that use paths filters to scope CI
|
|
42
|
+
workflows to service boundaries — the merge queue bypasses those boundaries
|
|
43
|
+
and runs all required checks for every PR regardless of which service changed.
|
|
44
|
+
|
|
45
|
+
The merge_group event only supports filtering by:
|
|
46
|
+
types: [checks_requested] (default and only supported activity type)
|
|
47
|
+
|
|
48
|
+
GitHub has acknowledged this is a known limitation with no native fix planned.
|
|
49
|
+
fix: |
|
|
50
|
+
There is no native path filtering for merge_group. Workarounds:
|
|
51
|
+
|
|
52
|
+
1. Step-level change detection: Use a job step with dorny/paths-filter or
|
|
53
|
+
tj-actions/changed-files to detect whether relevant files changed, then
|
|
54
|
+
gate subsequent steps on that output. This allows individual steps to
|
|
55
|
+
short-circuit while the job still runs (satisfying required check status).
|
|
56
|
+
|
|
57
|
+
2. Gate job pattern: Add a detect-changes job that runs paths detection and
|
|
58
|
+
exposes outputs. Downstream jobs use needs: + if: to conditionally run
|
|
59
|
+
based on the detected changes.
|
|
60
|
+
|
|
61
|
+
3. Accept always-run: For merge queue, it is generally safer to run all
|
|
62
|
+
required checks regardless of file changes. This ensures complete
|
|
63
|
+
validation before merging and avoids stale-cache scenarios.
|
|
64
|
+
fix_code:
|
|
65
|
+
- language: yaml
|
|
66
|
+
label: "Workaround — step-level paths filter inside merge_group triggered job"
|
|
67
|
+
code: |
|
|
68
|
+
on:
|
|
69
|
+
pull_request:
|
|
70
|
+
paths: ['src/**'] # pull_request respects this filter
|
|
71
|
+
merge_group: # merge_group ignores all path/branch filters
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
build:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
fetch-depth: 0
|
|
80
|
+
|
|
81
|
+
- uses: dorny/paths-filter@v3
|
|
82
|
+
id: changes
|
|
83
|
+
with:
|
|
84
|
+
filters: |
|
|
85
|
+
src:
|
|
86
|
+
- 'src/**'
|
|
87
|
+
|
|
88
|
+
- name: Build (skip if no src changes on pull_request)
|
|
89
|
+
if: steps.changes.outputs.src == 'true' || github.event_name == 'merge_group'
|
|
90
|
+
run: npm run build
|
|
91
|
+
shell: bash
|
|
92
|
+
|
|
93
|
+
- language: yaml
|
|
94
|
+
label: "Workaround — detect-changes gate job for monorepo required checks"
|
|
95
|
+
code: |
|
|
96
|
+
on:
|
|
97
|
+
pull_request:
|
|
98
|
+
merge_group:
|
|
99
|
+
|
|
100
|
+
jobs:
|
|
101
|
+
detect-changes:
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
outputs:
|
|
104
|
+
src-changed: ${{ steps.filter.outputs.src }}
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v4
|
|
107
|
+
with:
|
|
108
|
+
fetch-depth: 0
|
|
109
|
+
- uses: dorny/paths-filter@v3
|
|
110
|
+
id: filter
|
|
111
|
+
with:
|
|
112
|
+
filters: |
|
|
113
|
+
src:
|
|
114
|
+
- 'src/**'
|
|
115
|
+
|
|
116
|
+
build:
|
|
117
|
+
needs: detect-changes
|
|
118
|
+
if: needs.detect-changes.outputs.src-changed == 'true' || github.event_name == 'merge_group'
|
|
119
|
+
runs-on: ubuntu-latest
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v4
|
|
122
|
+
- run: npm run build
|
|
123
|
+
shell: bash
|
|
124
|
+
prevention:
|
|
125
|
+
- "Do not add paths: or branches: under merge_group: — they are silently ignored."
|
|
126
|
+
- "For monorepo required checks, plan for merge_group always running all required checks regardless of file changes."
|
|
127
|
+
- "Use step-level change detection (dorny/paths-filter, tj-actions/changed-files) to short-circuit expensive work inside jobs."
|
|
128
|
+
- "Document that merge queue bypasses path filters in your monorepo CI design documentation."
|
|
129
|
+
docs:
|
|
130
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#merge_group"
|
|
131
|
+
label: "GitHub Docs: merge_group event — supported filter types"
|
|
132
|
+
- url: "https://stackoverflow.com/questions/76655935/when-does-a-github-workflow-trigger-for-merge-group-and-is-it-restricted-by-branch"
|
|
133
|
+
label: "Stack Overflow: merge_group trigger and branch restrictions (Score: 7, 9K views)"
|
|
134
|
+
- url: "https://github.com/orgs/community/discussions/90533"
|
|
135
|
+
label: "GitHub Community: merge_group does not support path filters"
|
|
136
|
+
- url: "https://github.com/dorny/paths-filter"
|
|
137
|
+
label: "dorny/paths-filter: step-level changed-files detection for monorepos"
|