@htekdev/actions-debugger 1.0.55 → 1.0.57

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/concurrency-timing/concurrency-timing-033.yml +104 -0
  4. package/errors/concurrency-timing/concurrency-timing-034.yml +123 -0
  5. package/errors/known-unsolved/known-unsolved-037.yml +124 -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-040.yml +142 -0
  9. package/errors/permissions-auth/permissions-auth-041.yml +110 -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/silent-failures/silent-failures-056.yml +105 -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/triggers/triggers-040.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,142 @@
1
+ id: permissions-auth-040
2
+ title: "GITHUB_TOKEN is read-only by default in new repositories — write operations fail with 403"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - GITHUB_TOKEN
7
+ - read-only
8
+ - permissions
9
+ - 403
10
+ - new-repo
11
+ - workflow-permissions
12
+ patterns:
13
+ - regex: 'remote:\s*Permission to .* denied to github-actions\[bot\]'
14
+ flags: i
15
+ - regex: 'Error:\s*Resource not accessible by integration'
16
+ flags: i
17
+ - regex: 'HttpError:\s*Resource not accessible by integration'
18
+ flags: i
19
+ - regex: '403.*github-actions\[bot\]'
20
+ flags: i
21
+ error_messages:
22
+ - "remote: Permission to org/repo.git denied to github-actions[bot]"
23
+ - "Error: Resource not accessible by integration"
24
+ - "HttpError: Resource not accessible by integration"
25
+ - "fatal: unable to access 'https://github.com/org/repo/': The requested URL returned error: 403"
26
+ - "GitHub Actions is not permitted to create or approve pull requests"
27
+ root_cause: |
28
+ In February 2023 (GitHub Changelog), GitHub changed the default GITHUB_TOKEN
29
+ permissions for all new repositories and new organizations from read-write to
30
+ read-only. Repositories created before this change retain their original default
31
+ (read-write) unless an administrator explicitly changes the org-level setting.
32
+
33
+ This creates two failure scenarios:
34
+
35
+ 1. COPIED WORKFLOWS: A developer copies a workflow from an older repo or a public
36
+ template that relies on the default read-write GITHUB_TOKEN (for example, to push
37
+ a build artifact, create a release, comment on a PR, or open a pull request). In a
38
+ new repo, that same workflow fails with 403 or "Resource not accessible by
39
+ integration" because the token only has read permissions.
40
+
41
+ 2. ORG POLICY CHANGE: An organization administrator enables the read-only default
42
+ at the organization level (Settings > Actions > General > Workflow permissions).
43
+ All repos in the org immediately start failing any workflow that performs write
44
+ operations without an explicit permissions block.
45
+
46
+ The failure message is typically cryptic and does not mention that the root cause is
47
+ the workflow permissions default — it only says the token was denied.
48
+ fix: |
49
+ Add explicit permissions blocks to workflows or jobs that perform write operations.
50
+ GitHub recommends the principle of least privilege: grant only the specific write
51
+ permission needed, not blanket read-write access.
52
+
53
+ At the workflow level, a top-level permissions: block sets defaults for all jobs.
54
+ At the job level, a permissions: block overrides the workflow-level default for that
55
+ job only. Job-level is preferred — it minimises the blast radius if a job is
56
+ compromised.
57
+
58
+ Common permissions needed:
59
+ - contents: write — push commits, create releases, upload release assets
60
+ - pull-requests: write — create/comment on pull requests
61
+ - issues: write — create/comment on issues
62
+ - packages: write — publish to GitHub Packages
63
+ - pages: write — deploy to GitHub Pages (also needs id-token: write for OIDC)
64
+
65
+ Alternatively, change the default at repo level: Settings > Actions > General >
66
+ Workflow permissions > Read and write permissions. Not recommended for security-
67
+ conscious teams.
68
+ fix_code:
69
+ - language: yaml
70
+ label: "Problem: workflow relies on default write token, fails in new repos"
71
+ code: |
72
+ # No permissions block — relies on default, which is READ-ONLY in new repos
73
+ on: push
74
+
75
+ jobs:
76
+ release:
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@v4
80
+ - name: Build
81
+ run: npm run build
82
+ - name: Create GitHub Release
83
+ uses: softprops/action-gh-release@v2
84
+ with:
85
+ files: dist/**
86
+ # Fails with 403 in new repos — needs contents:write
87
+ - language: yaml
88
+ label: "Fix: explicit job-level permissions (least privilege)"
89
+ code: |
90
+ on: push
91
+
92
+ jobs:
93
+ release:
94
+ runs-on: ubuntu-latest
95
+ permissions:
96
+ contents: write # Required to create releases and upload assets
97
+ steps:
98
+ - uses: actions/checkout@v4
99
+ - name: Build
100
+ run: npm run build
101
+ - name: Create GitHub Release
102
+ uses: softprops/action-gh-release@v2
103
+ with:
104
+ files: dist/**
105
+ - language: yaml
106
+ label: "Workflow-level permissions (when multiple jobs all need write access)"
107
+ code: |
108
+ on: push
109
+
110
+ # Set workflow-level default — overridden per job as needed
111
+ permissions:
112
+ contents: write
113
+ pull-requests: write
114
+
115
+ jobs:
116
+ build-and-release:
117
+ runs-on: ubuntu-latest
118
+ # Inherits workflow-level permissions
119
+ steps:
120
+ - uses: actions/checkout@v4
121
+ - run: npm run build
122
+
123
+ comment-on-pr:
124
+ runs-on: ubuntu-latest
125
+ permissions:
126
+ pull-requests: write # Job-level — more restrictive than workflow default
127
+ contents: read
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+ prevention:
131
+ - "Always declare explicit permissions blocks in workflows that push, create releases, comment, or deploy — never rely on defaults"
132
+ - "Run 'actionlint' locally — it warns when a workflow uses write-requiring actions without corresponding permissions"
133
+ - "Use the minimum permissions required: grant 'contents: write' only on jobs that need it, not at workflow level"
134
+ - "When copying a workflow from another repo, check whether it has a permissions block — add one if missing"
135
+ - "Review the GitHub Actions audit log when a workflow fails with 403 to confirm the token permissions in use"
136
+ docs:
137
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token"
138
+ label: "GitHub Docs: GITHUB_TOKEN permissions"
139
+ - url: "https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#setting-the-permissions-of-the-github_token-for-your-repository"
140
+ label: "GitHub Docs: Setting default workflow permissions"
141
+ - url: "https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/"
142
+ label: "GitHub Changelog: Default GITHUB_TOKEN permissions changed to read-only (Feb 2023)"
@@ -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,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"