@htekdev/actions-debugger 1.0.56 → 1.0.58

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.
Files changed (25) hide show
  1. package/errors/caching-artifacts/caching-artifacts-038.yml +95 -0
  2. package/errors/caching-artifacts/caching-artifacts-039.yml +110 -0
  3. package/errors/caching-artifacts/caching-artifacts-040.yml +112 -0
  4. package/errors/concurrency-timing/concurrency-timing-033.yml +104 -0
  5. package/errors/concurrency-timing/concurrency-timing-034.yml +123 -0
  6. package/errors/known-unsolved/known-unsolved-038.yml +124 -0
  7. package/errors/known-unsolved/known-unsolved-039.yml +102 -0
  8. package/errors/permissions-auth/permissions-auth-041.yml +110 -0
  9. package/errors/permissions-auth/permissions-auth-042.yml +125 -0
  10. package/errors/runner-environment/runner-environment-112.yml +98 -0
  11. package/errors/runner-environment/runner-environment-113.yml +118 -0
  12. package/errors/runner-environment/runner-environment-114.yml +130 -0
  13. package/errors/runner-environment/runner-environment-115.yml +120 -0
  14. package/errors/runner-environment/runner-environment-116.yml +106 -0
  15. package/errors/runner-environment/runner-environment-117.yml +109 -0
  16. package/errors/runner-environment/runner-environment-118.yml +102 -0
  17. package/errors/silent-failures/silent-failures-057.yml +120 -0
  18. package/errors/silent-failures/silent-failures-058.yml +126 -0
  19. package/errors/silent-failures/silent-failures-059.yml +104 -0
  20. package/errors/triggers/triggers-041.yml +105 -0
  21. package/errors/triggers/triggers-042.yml +110 -0
  22. package/errors/triggers/triggers-043.yml +125 -0
  23. package/errors/yaml-syntax/yaml-syntax-040.yml +135 -0
  24. package/errors/yaml-syntax/yaml-syntax-041.yml +147 -0
  25. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ id: known-unsolved-039
2
+ title: "on.schedule minimum interval is 5 minutes — sub-5-minute cron patterns silently run every 5 minutes"
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - schedule
7
+ - cron
8
+ - on.schedule
9
+ - rate-limit
10
+ - interval
11
+ - minimum-frequency
12
+ patterns:
13
+ - regex: '^\s*-\s*cron:\s*[''"]?\*\/[1-4]\s'
14
+ flags: 'm'
15
+ - regex: 'schedule.*cron.*every\s+(?:1|2|3|4)\s+minute'
16
+ flags: 'i'
17
+ error_messages:
18
+ - "*/1 * * * *"
19
+ - "*/2 * * * *"
20
+ - "*/3 * * * *"
21
+ - "*/4 * * * *"
22
+ root_cause: |
23
+ GitHub Actions enforces a minimum cron schedule interval of 5 minutes. Cron
24
+ expressions that would trigger more frequently — such as */1, */2, */3, or */4
25
+ in the minutes field — are silently capped and run every 5 minutes instead of
26
+ the configured interval.
27
+
28
+ There is no validation error, no warning in workflow run logs, and no annotation
29
+ on the workflow YAML file. The workflow appears correctly configured in the GitHub
30
+ UI, but runs execute every 5 minutes regardless of the specified granularity.
31
+
32
+ This limitation is enforced server-side by GitHub's scheduler and cannot be
33
+ overridden via any workflow configuration. It applies to all hosted runners and
34
+ self-hosted runners triggered via GitHub's scheduler.
35
+
36
+ Additionally, even at the 5-minute minimum, scheduled workflows on busy
37
+ repositories may be delayed further during periods of high GitHub Actions load.
38
+ Under heavy load, schedules are queued and run as capacity allows — not
39
+ necessarily at the exact scheduled time. The schedule event documentation notes
40
+ that schedules may be delayed "up to hours" when load is high.
41
+
42
+ Common developer mental model mismatch: developers migrating from cron daemons
43
+ (systemd, cron, AWS EventBridge) where sub-minute schedules are routine expect
44
+ GitHub Actions to support the same granularity.
45
+ fix: |
46
+ Use a minimum interval of 5 minutes in your cron expression — replace */1,
47
+ */2, */3, or */4 in the minutes field with */5.
48
+
49
+ If sub-5-minute polling or execution is required, use an external trigger
50
+ mechanism instead of on.schedule:
51
+
52
+ - External scheduler (AWS EventBridge, Azure Logic Apps, GCP Cloud Scheduler)
53
+ that calls the workflow_dispatch REST API
54
+ - A self-hosted runner with a local cron daemon (systemd timer, Linux cron)
55
+ that invokes the GitHub API to dispatch the workflow
56
+ - Move the time-sensitive logic out of GitHub Actions entirely and run it
57
+ in a dedicated service that can be triggered at any frequency
58
+ fix_code:
59
+ - language: yaml
60
+ label: "Minimum supported interval — every 5 minutes"
61
+ code: |
62
+ on:
63
+ schedule:
64
+ # Minimum supported interval is every 5 minutes
65
+ - cron: '*/5 * * * *'
66
+ # The following are silently capped to every 5 minutes:
67
+ # - cron: '*/1 * * * *'
68
+ # - cron: '*/2 * * * *'
69
+ # - cron: '*/3 * * * *'
70
+ # - cron: '*/4 * * * *'
71
+ - language: yaml
72
+ label: "workflow_dispatch trigger for external scheduler integration"
73
+ code: |
74
+ # Trigger via REST API from an external scheduler at any frequency:
75
+ # POST https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW.yml/dispatches
76
+ # Body: {"ref": "main"}
77
+ on:
78
+ workflow_dispatch:
79
+ inputs:
80
+ triggered_by:
81
+ description: 'Trigger source identifier'
82
+ type: string
83
+ default: 'external-scheduler'
84
+
85
+ jobs:
86
+ scheduled-task:
87
+ runs-on: ubuntu-latest
88
+ steps:
89
+ - name: Run task
90
+ run: echo "Triggered by ${{ inputs.triggered_by }}"
91
+ prevention:
92
+ - "Always use */5 or higher in the cron minutes field for on.schedule expressions"
93
+ - "Add a comment next to every cron expression documenting the intended frequency and the 5-minute minimum"
94
+ - "Test schedule behavior by temporarily adding an on.push trigger during development rather than waiting for scheduled runs"
95
+ - "For sub-5-minute needs, design the solution with an external scheduler calling workflow_dispatch from the start — do not plan to reduce the interval later"
96
+ docs:
97
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule"
98
+ label: "GitHub Docs — Schedule event (on.schedule)"
99
+ - url: "https://stackoverflow.com/questions/63612471/run-github-actions-every-x-minutes-cron-job"
100
+ label: "Stack Overflow — Run GitHub Actions every X minutes (highly voted)"
101
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule"
102
+ label: "GitHub Docs — Note on schedule delays under high load"
@@ -0,0 +1,110 @@
1
+ id: permissions-auth-041
2
+ title: "actions/checkout submodules fail with auth error when private submodule repos are outside the workflow repository"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - checkout
7
+ - submodules
8
+ - github-token
9
+ - private-repo
10
+ - authentication
11
+ - pat
12
+ - deploy-key
13
+ patterns:
14
+ - regex: 'fatal: could not read Username for.*terminal prompts disabled'
15
+ flags: 'i'
16
+ - regex: 'fatal: repository ''.*github\.com.*'' not found'
17
+ flags: 'i'
18
+ - regex: 'remote: Repository not found\.'
19
+ flags: 'i'
20
+ - regex: 'Error: The process.*git.*failed with exit code 128'
21
+ flags: 'i'
22
+ error_messages:
23
+ - "fatal: could not read Username for 'https://github.com': terminal prompts disabled"
24
+ - "fatal: repository 'https://github.com/org/private-submodule/' not found"
25
+ - "remote: Repository not found."
26
+ - "Error: The process '/usr/bin/git' failed with exit code 128"
27
+ root_cause: |
28
+ `actions/checkout` uses the `GITHUB_TOKEN` (or the `token` input) to authenticate
29
+ git operations during checkout, including recursive submodule initialization.
30
+ `GITHUB_TOKEN` is automatically scoped to the repository that owns the running
31
+ workflow — it cannot access private repositories in other organizations, or other
32
+ private repositories within the same organization.
33
+
34
+ When a submodule `.gitmodules` entry points to a private repository that is different
35
+ from the workflow repository, git authentication fails during `submodule update`.
36
+ The resulting error (`fatal: could not read Username` or `repository not found`)
37
+ is confusing because the submodule URL is correct — the problem is authentication
38
+ scope, not the URL itself.
39
+
40
+ Developers upgrading from self-hosted runners (where global git credentials were
41
+ configured) or from `actions/checkout@v1-v2` (which had different authentication
42
+ behavior) frequently encounter this when moving to `actions/checkout@v4` on
43
+ GitHub-hosted runners.
44
+
45
+ This is a deliberate security boundary: `GITHUB_TOKEN` is intentionally limited to
46
+ the current repository to prevent workflows from silently reading other private repos.
47
+ fix: |
48
+ Use one of two approaches:
49
+
50
+ 1. **PAT approach**: Create a Personal Access Token (classic PAT with `repo` scope,
51
+ or a fine-grained PAT with read access to all submodule repos). Store it as a
52
+ repository secret and pass it via the `token` input to `actions/checkout`.
53
+
54
+ 2. **Deploy key approach**: Add a read-only deploy key to each private submodule
55
+ repository, then use `webfactory/ssh-agent` to load the key before checkout.
56
+ This avoids PATs entirely and follows least-privilege principles.
57
+
58
+ For organizations, a machine account (a GitHub account created for automation) with
59
+ `repo` read access to all submodule repos is often easier to manage than fine-grained
60
+ PATs.
61
+ fix_code:
62
+ - language: yaml
63
+ label: "PAT with repo scope passed via token input"
64
+ code: |
65
+ jobs:
66
+ build:
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ with:
71
+ submodules: recursive
72
+ token: ${{ secrets.SUBMODULE_PAT }}
73
+ # SUBMODULE_PAT: classic PAT with repo scope, or fine-grained PAT
74
+ # with Contents: read on all private submodule repos
75
+
76
+ - language: yaml
77
+ label: "SSH deploy key approach using webfactory/ssh-agent"
78
+ code: |
79
+ jobs:
80
+ build:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - name: Configure SSH agent with submodule deploy key
84
+ uses: webfactory/ssh-agent@v0.9.0
85
+ with:
86
+ ssh-private-key: ${{ secrets.SUBMODULE_DEPLOY_KEY }}
87
+ # Add multiple keys for multiple private submodule repos:
88
+ # ssh-private-key: |
89
+ # ${{ secrets.SUBMODULE_REPO_A_KEY }}
90
+ # ${{ secrets.SUBMODULE_REPO_B_KEY }}
91
+
92
+ - uses: actions/checkout@v4
93
+ with:
94
+ submodules: recursive
95
+ # No token needed — SSH agent handles git authentication
96
+ prevention:
97
+ - "Prefer deploy keys over PATs for submodule access — deploy keys are repo-scoped and easier to rotate"
98
+ - "Use fine-grained PATs scoped to specific repositories rather than classic PATs with broad repo scope"
99
+ - "Document which submodule repos require special secrets so new contributors can configure their forks"
100
+ - "Consider converting private submodule dependencies to private packages in a registry to avoid cross-repo auth complexity"
101
+ - "Test submodule checkout in a fresh fork or on a runner without existing git credentials to catch auth issues early"
102
+ docs:
103
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token"
104
+ label: "GitHub Docs — GITHUB_TOKEN permissions and scope"
105
+ - url: "https://github.com/actions/checkout#usage"
106
+ label: "actions/checkout — token and submodules inputs"
107
+ - url: "https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys"
108
+ label: "GitHub Docs — Managing deploy keys"
109
+ - url: "https://github.com/webfactory/ssh-agent"
110
+ label: "webfactory/ssh-agent — Load SSH keys for Actions"
@@ -0,0 +1,125 @@
1
+ id: permissions-auth-042
2
+ title: "actions/attest-build-provenance requires both id-token: write AND attestations: write — missing attestations permission causes 403"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - attestations
7
+ - attest-build-provenance
8
+ - id-token
9
+ - permissions
10
+ - supply-chain
11
+ - sigstore
12
+ - 403
13
+ patterns:
14
+ - regex: 'attestations.*write.*required|write.*attestations.*required'
15
+ flags: 'i'
16
+ - regex: 'Resource not accessible by integration.*attest'
17
+ flags: 'i'
18
+ - regex: 'Error: Failed to create attestation'
19
+ flags: 'i'
20
+ - regex: 'permissions.*attestations.*write'
21
+ flags: 'i'
22
+ error_messages:
23
+ - "Error: Failed to create attestation: Resource not accessible by integration"
24
+ - "Error: Failed to create attestation: 403 Forbidden"
25
+ - "RequestError [HttpError]: Resource not accessible by integration"
26
+ - "Error: Attestation creation failed: must have `attestations: write` permission"
27
+ root_cause: |
28
+ GitHub's artifact attestation feature (GA May 2024, https://github.blog/changelog/2024-05-02-github-artifact-attestations-is-generally-available/)
29
+ allows workflows to generate Sigstore-compatible provenance attestations for build
30
+ artifacts using `actions/attest-build-provenance` or `actions/attest`.
31
+
32
+ These actions require TWO permissions on the GITHUB_TOKEN:
33
+ 1. `id-token: write` — to fetch an OIDC token from GitHub's identity provider
34
+ (used to sign the attestation with Sigstore)
35
+ 2. `attestations: write` — to store the attestation in GitHub's attestation store
36
+ (new permission introduced with the attestation feature)
37
+
38
+ Most early documentation examples, blog posts, and quickstarts only show
39
+ `id-token: write`, omitting `attestations: write`. Developers following these
40
+ examples get a cryptic 403 "Resource not accessible by integration" error when
41
+ the attest step runs, with no clear indication that a second permission is missing.
42
+
43
+ The error is consistent and not environment-specific — it will fail on every run
44
+ until the `attestations: write` permission is added.
45
+
46
+ Additionally, organization-level policies may restrict the use of attestations.
47
+ If the organization has disabled artifact attestations, the 403 will occur even
48
+ with both permissions present — the error message does not distinguish between
49
+ "missing permission" and "feature disabled by org policy".
50
+ fix: |
51
+ Add BOTH `id-token: write` AND `attestations: write` to the permissions block
52
+ of the job that runs the attest-build-provenance action.
53
+
54
+ If your workflow or job already declares a `permissions` block, add both entries
55
+ to it. If your workflow has no permissions block, add one with both required
56
+ permissions plus any other permissions your workflow needs (e.g., `contents: read`
57
+ for checkout).
58
+
59
+ Note: `attestations: write` is only available on github.com. GitHub Enterprise
60
+ Server (GHES) requires GHES 3.12+ for attestation support.
61
+ fix_code:
62
+ - language: yaml
63
+ label: "Correct permissions for actions/attest-build-provenance"
64
+ code: |
65
+ jobs:
66
+ build-and-attest:
67
+ runs-on: ubuntu-latest
68
+ permissions:
69
+ id-token: write # Required: OIDC token for Sigstore signing
70
+ attestations: write # Required: write attestation to GitHub store
71
+ contents: read # Required: checkout
72
+
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+
76
+ - name: Build artifact
77
+ run: |
78
+ npm ci
79
+ npm run build
80
+ tar -czf dist.tar.gz dist/
81
+
82
+ - name: Generate build provenance attestation
83
+ uses: actions/attest-build-provenance@v2
84
+ with:
85
+ subject-path: dist.tar.gz
86
+ - language: yaml
87
+ label: "Attest multiple subjects (container image + artifact)"
88
+ code: |
89
+ jobs:
90
+ release:
91
+ runs-on: ubuntu-latest
92
+ permissions:
93
+ id-token: write
94
+ attestations: write
95
+ contents: read
96
+ packages: write # If pushing to GHCR
97
+
98
+ steps:
99
+ - uses: actions/checkout@v4
100
+
101
+ - name: Attest binary
102
+ uses: actions/attest-build-provenance@v2
103
+ with:
104
+ subject-path: bin/myapp
105
+
106
+ - name: Attest container image
107
+ uses: actions/attest-build-provenance@v2
108
+ with:
109
+ subject-name: ghcr.io/${{ github.repository }}/myapp
110
+ subject-digest: ${{ steps.push.outputs.digest }}
111
+ push-to-registry: true
112
+ prevention:
113
+ - "Always include BOTH `id-token: write` and `attestations: write` in the permissions block — neither alone is sufficient"
114
+ - "Copy the permissions block from the official actions/attest-build-provenance README examples, not from third-party blog posts"
115
+ - "Use `gh attestation verify <artifact>` locally to test that attestations were created successfully after the workflow runs"
116
+ - "For GHES deployments, verify the GHES version is 3.12+ before adding attestation steps to workflows"
117
+ docs:
118
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds"
119
+ label: "GitHub Docs — Using artifact attestations to establish provenance"
120
+ - url: "https://github.blog/changelog/2024-05-02-github-artifact-attestations-is-generally-available/"
121
+ label: "GitHub Changelog — Artifact attestations GA (May 2024)"
122
+ - url: "https://github.com/actions/attest-build-provenance"
123
+ label: "actions/attest-build-provenance — README and required permissions"
124
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token"
125
+ label: "GitHub Docs — GITHUB_TOKEN permission scopes (attestations: write)"
@@ -0,0 +1,98 @@
1
+ id: runner-environment-112
2
+ title: "ubuntu-24.04 nftables replaces iptables — Docker bridge networking and kind/minikube cluster setup fails"
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - ubuntu-24.04
7
+ - docker
8
+ - networking
9
+ - iptables
10
+ - nftables
11
+ - kind
12
+ - minikube
13
+ - runner-image-update
14
+ patterns:
15
+ - regex: 'No chain/target/match by that name'
16
+ flags: 'i'
17
+ - regex: 'iptables.*failed.*nat.*DOCKER'
18
+ flags: 'i'
19
+ - regex: 'Failed to set up iptables.*\(iptables-nft\)'
20
+ flags: 'i'
21
+ - regex: 'network.*setup.*failed.*iptables'
22
+ flags: 'i'
23
+ - regex: 'Error response from daemon.*iptables failed'
24
+ flags: 'i'
25
+ error_messages:
26
+ - "iptables: No chain/target/match by that name."
27
+ - "Error response from daemon: driver failed programming external connectivity on endpoint: iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 5432 -j DNAT --to-destination 172.17.0.2:5432 ! -i docker0: iptables: No chain/target/match by that name."
28
+ - "Failed to set up iptables rules: iptables-nft is not supported"
29
+ - "Error: failed to create cluster: failed to ensure docker network: failed to set up kind CNI"
30
+ root_cause: |
31
+ Ubuntu 24.04 ships with nftables as the primary packet-filtering framework. The `iptables`
32
+ command is now a thin wrapper around nftables (`iptables-nft`), replacing the legacy
33
+ `iptables-legacy` backend that was default on Ubuntu 20.04/22.04.
34
+
35
+ Docker's bridge networking writes iptables rules using legacy format assumptions. CNI plugins
36
+ used by Kubernetes tools (kind, minikube, k3s) also expect the legacy iptables backend to be
37
+ active. When these tools run on ubuntu-24.04, they encounter incompatible chain formats and
38
+ rule semantics that iptables-nft does not honour, causing network setup to fail with "No
39
+ chain/target/match by that name" errors.
40
+
41
+ The runner-images migration from ubuntu-22.04 to ubuntu-24.04 (and from ubuntu-latest pointing
42
+ to ubuntu-22.04 to ubuntu-24.04) silently exposes this issue for any workflow running
43
+ Docker-in-Docker, Kubernetes clusters, or service containers with custom networking.
44
+ fix: |
45
+ Switch the `iptables` alternative back to the legacy backend before starting Docker or
46
+ any CNI-based tooling. Add this step at the top of your job, before any Docker or cluster
47
+ setup steps:
48
+
49
+ - name: Switch to legacy iptables
50
+ run: |
51
+ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
52
+ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
53
+
54
+ For kind clusters, the above fix is the most reliable approach.
55
+
56
+ If you cannot switch the iptables backend (e.g., you require nftables for other reasons),
57
+ use Docker with `--iptables=false` and manage forwarding rules manually, or use network
58
+ mode `host` where possible.
59
+
60
+ Long-term: upgrade to versions of kind (≥ v0.24), minikube (≥ v1.33), and Docker Engine
61
+ (≥ 27) that include nftables-native support.
62
+ fix_code:
63
+ - language: yaml
64
+ label: "Switch to iptables-legacy before Docker or cluster setup (ubuntu-24.04)"
65
+ code: |
66
+ jobs:
67
+ test:
68
+ runs-on: ubuntu-24.04
69
+ steps:
70
+ - name: Switch to legacy iptables
71
+ run: |
72
+ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
73
+ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
74
+
75
+ - name: Start kind cluster
76
+ uses: helm/kind-action@v1
77
+ with:
78
+ cluster_name: ci
79
+ - language: yaml
80
+ label: "Pin runs-on to ubuntu-22.04 as a temporary workaround"
81
+ code: |
82
+ jobs:
83
+ test:
84
+ # Temporary: ubuntu-22.04 uses iptables-legacy by default
85
+ # TODO: migrate to ubuntu-24.04 after updating kind/Docker versions
86
+ runs-on: ubuntu-22.04
87
+ prevention:
88
+ - "Test workflows on ubuntu-24.04 explicitly before relying on ubuntu-latest switching to it"
89
+ - "Keep kind, minikube, and Docker Engine versions current — nftables-native support arrived in each"
90
+ - "Add an iptables-legacy switch step as a defensive measure in any job using Docker networking or CNI plugins"
91
+ - "Watch runner-images release notes (github.com/actions/runner-images) for ubuntu-latest label changes"
92
+ docs:
93
+ - url: "https://docs.docker.com/network/iptables/"
94
+ label: "Docker — iptables and nftables networking"
95
+ - url: "https://github.com/actions/runner-images/issues/10636"
96
+ label: "runner-images #10636 — ubuntu-24.04 iptables-nft Docker incompatibility"
97
+ - url: "https://kind.sigs.k8s.io/docs/user/known-issues/"
98
+ label: "kind — Known Issues including iptables compatibility"
@@ -0,0 +1,118 @@
1
+ id: runner-environment-113
2
+ title: "ubuntu-24.04 removed python3-distutils — ModuleNotFoundError: No module named 'distutils' on pip install"
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - ubuntu-24.04
7
+ - python
8
+ - distutils
9
+ - pip
10
+ - setup-py
11
+ - runner-image-update
12
+ - python-3-12
13
+ patterns:
14
+ - regex: "ModuleNotFoundError: No module named 'distutils'"
15
+ flags: 'i'
16
+ - regex: 'No module named ''distutils\.core'''
17
+ flags: 'i'
18
+ - regex: 'error in setup command.*distutils'
19
+ flags: 'i'
20
+ - regex: 'distutils.*not found.*pip install'
21
+ flags: 'i'
22
+ error_messages:
23
+ - "ModuleNotFoundError: No module named 'distutils'"
24
+ - "ModuleNotFoundError: No module named 'distutils.core'"
25
+ - "error in setup command: Error parsing /path/to/setup.cfg: No module named 'distutils.core'"
26
+ - "note: This error originates from a subprocess, and is likely not a problem with pip."
27
+ - "error: legacy-install-failure"
28
+ root_cause: |
29
+ Ubuntu 24.04 ships Python 3.12 as the system Python. Python 3.12 fully removed the
30
+ `distutils` module from its standard library (it was deprecated in 3.10 and removed in 3.12
31
+ per PEP 632). Ubuntu Noble (24.04) also removed the `python3-distutils` apt backport package
32
+ that had been available on 20.04 and 22.04.
33
+
34
+ Many older Python packages still use `distutils` directly in their `setup.py` or `setup.cfg`
35
+ files. When `pip install` processes these packages, the build backend invokes `distutils` which
36
+ is no longer available. The error appears to be a build backend failure rather than a pip
37
+ failure, so the root cause is easy to miss.
38
+
39
+ This affects any workflow that:
40
+ - Uses the system Python (no `actions/setup-python` step)
41
+ - Installs packages with legacy `setup.py` that import `distutils`
42
+ - Creates virtualenvs from the system Python without pre-installing setuptools
43
+
44
+ Distinct from `setup-python-version-range-resolves-to-313-distutils-removed` which covers
45
+ setup-python action resolving to Python 3.12/3.13 and distutils in user packages. This entry
46
+ covers the apt-level removal affecting system Python on the ubuntu-24.04 runner image itself.
47
+ fix: |
48
+ Option 1 (recommended): Use actions/setup-python to manage the Python version explicitly.
49
+ setup-python bundles setuptools which provides a distutils compatibility shim.
50
+
51
+ Option 2: Install setuptools as the first pip action before installing other packages.
52
+ setuptools >= 58.0.0 ships a `distutils` compatibility shim that satisfies `import distutils`.
53
+
54
+ - name: Install setuptools (distutils shim)
55
+ run: pip install setuptools
56
+
57
+ Option 3: Install the apt package `python3-setuptools` which restores distutils compatibility
58
+ at the system level for Python 3.12.
59
+
60
+ Option 4 (long-term): Migrate the affected package to PEP 517/518 — replace setup.py with
61
+ pyproject.toml and a modern build backend (setuptools>=61, flit, hatch, etc.).
62
+ fix_code:
63
+ - language: yaml
64
+ label: "Use actions/setup-python so setuptools is available (recommended)"
65
+ code: |
66
+ jobs:
67
+ build:
68
+ runs-on: ubuntu-24.04
69
+ steps:
70
+ - uses: actions/checkout@v4
71
+
72
+ - uses: actions/setup-python@v5
73
+ with:
74
+ python-version: '3.12'
75
+
76
+ - name: Install dependencies
77
+ run: pip install -r requirements.txt
78
+ - language: yaml
79
+ label: "Pre-install setuptools to restore distutils shim (quick fix for system Python)"
80
+ code: |
81
+ jobs:
82
+ build:
83
+ runs-on: ubuntu-24.04
84
+ steps:
85
+ - uses: actions/checkout@v4
86
+
87
+ - name: Install setuptools (distutils compatibility shim for Python 3.12)
88
+ run: pip install --upgrade setuptools
89
+
90
+ - name: Install dependencies
91
+ run: pip install -r requirements.txt
92
+ - language: yaml
93
+ label: "Install python3-setuptools via apt (system-level fix)"
94
+ code: |
95
+ jobs:
96
+ build:
97
+ runs-on: ubuntu-24.04
98
+ steps:
99
+ - name: Install python3-setuptools
100
+ run: sudo apt-get install -y python3-setuptools
101
+
102
+ - name: Install dependencies
103
+ run: pip install -r requirements.txt
104
+ prevention:
105
+ - "Always use actions/setup-python rather than relying on system Python — it ships with setuptools"
106
+ - "Pin a Python version explicitly; avoid implicit system Python which changes per runner image"
107
+ - "Run pip install --dry-run in CI to detect distutils/setuptools issues before they block builds"
108
+ - "Audit third-party dependencies for setup.py usage with: pip install pipdeptree; pipdeptree | grep setup.py"
109
+ - "Migrate legacy setup.py packages to pyproject.toml for long-term Python 3.12+ compatibility"
110
+ docs:
111
+ - url: "https://docs.python.org/3/whatsnew/3.12.html#removed"
112
+ label: "Python 3.12 — Removed modules (distutils)"
113
+ - url: "https://peps.python.org/pep-0632/"
114
+ label: "PEP 632 — Deprecate distutils"
115
+ - url: "https://github.com/actions/runner-images/issues/9674"
116
+ label: "runner-images #9674 — python3-distutils unavailable on ubuntu-24.04"
117
+ - url: "https://github.com/actions/setup-python"
118
+ label: "actions/setup-python — bundles setuptools"