@htekdev/actions-debugger 1.0.111 → 1.0.113

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.
@@ -0,0 +1,123 @@
1
+ id: 'caching-artifacts-065'
2
+ title: 'Docker BuildKit type=gha cache fails on self-hosted runners with BuildKit < 0.21.0 after legacy cache service v1 shutdown'
3
+ category: 'caching-artifacts'
4
+ severity: 'error'
5
+ tags:
6
+ - docker
7
+ - buildkit
8
+ - buildx
9
+ - gha-cache
10
+ - self-hosted
11
+ - cache-api-v2
12
+ - legacy-service
13
+ patterns:
14
+ - regex: 'This legacy service is shutting down'
15
+ flags: 'i'
16
+ - regex: 'failed to solve.*legacy.*service'
17
+ flags: 'i'
18
+ - regex: 'gha-cache-sunset'
19
+ flags: 'i'
20
+ - regex: 'cache.*type=gha.*legacy'
21
+ flags: 'i'
22
+ error_messages:
23
+ - "ERROR: failed to solve: This legacy service is shutting down, effective April 15, 2025. Migrate to the new service ASAP. For more information: https://gh.io/gha-cache-sunset"
24
+ - "This legacy service is shutting down, effective April 15, 2025."
25
+ - "importing cache manifest from gha"
26
+ - "exporting to GitHub Actions Cache"
27
+ root_cause: |
28
+ GitHub decommissioned the legacy GitHub Actions Cache service v1 API on
29
+ April 15, 2025. All cache operations now require the new v2 API.
30
+
31
+ Docker BuildKit uses a separate Go-based GitHub Actions cache client
32
+ (`tonistiigi/go-actions-cache`) — distinct from the `@actions/cache` npm
33
+ package used by `actions/cache`. The `go-actions-cache` library added support
34
+ for cache API v2 in BuildKit 0.20.0 and Buildx 0.21.0.
35
+
36
+ Self-hosted runners that have not been updated since before the migration
37
+ still ship Docker Engine with an older BuildKit version (< 0.20.0) or an
38
+ older Docker Buildx binary (< 0.21.0). When these workflows use
39
+ `cache-from: type=gha` or `cache-to: type=gha`, the old BuildKit communicates
40
+ with the now-decommissioned v1 API endpoint and receives the shutdown error:
41
+
42
+ ERROR: failed to solve: This legacy service is shutting down, effective
43
+ April 15, 2025. Migrate to the new service ASAP.
44
+
45
+ GitHub-hosted runners (ubuntu-latest, etc.) are automatically up to date and
46
+ are not affected. Only self-hosted runners or custom BuildKit setups that
47
+ pin an old Buildx/BuildKit version are impacted.
48
+
49
+ Note: this is distinct from `actions/cache` action failing due to stale
50
+ ACTIONS_CACHE_URL overrides (caching-artifacts-031), which is a different
51
+ failure mode involving the npm-based cache client.
52
+ fix: |
53
+ Option 1 (recommended): Pin docker/setup-buildx-action to install the latest
54
+ Buildx on the self-hosted runner before each build. This ensures Buildx >=
55
+ 0.21.0 is always used regardless of the runner's system Buildx version.
56
+
57
+ Option 2: Update the self-hosted runner's Docker Engine to >= 28.0.0 and
58
+ Buildx to >= 0.21.0 at the system level.
59
+
60
+ Option 3: Switch to registry-based cache (type=registry) if upgrading Buildx
61
+ is not feasible. GHCR registry cache is not subject to the API version
62
+ requirement and is free for public repositories.
63
+
64
+ Minimum versions required for cache API v2:
65
+ - Docker Buildx >= 0.21.0
66
+ - BuildKit >= 0.20.0
67
+ - Docker Compose >= 2.33.1
68
+ - Docker Engine >= 28.0.0 (when using containerd image store)
69
+ fix_code:
70
+ - language: yaml
71
+ label: 'Pin setup-buildx-action to install latest Buildx on self-hosted runner'
72
+ code: |
73
+ jobs:
74
+ build:
75
+ runs-on: [self-hosted, linux, x64]
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+
79
+ # Always install the latest Buildx — ensures >= 0.21.0 for cache API v2
80
+ - name: Set up Docker Buildx
81
+ uses: docker/setup-buildx-action@v3
82
+ with:
83
+ version: latest # Overrides the runner's system Buildx version
84
+
85
+ - name: Build and push
86
+ uses: docker/build-push-action@v7
87
+ with:
88
+ push: ${{ github.ref == 'refs/heads/main' }}
89
+ tags: ghcr.io/${{ github.repository }}:latest
90
+ cache-from: type=gha
91
+ cache-to: type=gha,mode=max
92
+ - language: yaml
93
+ label: 'Fallback: use GHCR registry cache instead of type=gha'
94
+ code: |
95
+ - name: Log in to GHCR
96
+ uses: docker/login-action@v3
97
+ with:
98
+ registry: ghcr.io
99
+ username: ${{ github.actor }}
100
+ password: ${{ secrets.GITHUB_TOKEN }}
101
+
102
+ - name: Build and push (registry cache — no API version dependency)
103
+ uses: docker/build-push-action@v7
104
+ with:
105
+ push: ${{ github.ref == 'refs/heads/main' }}
106
+ tags: ghcr.io/${{ github.repository }}:latest
107
+ cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
108
+ cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
109
+ prevention:
110
+ - "Always add docker/setup-buildx-action with 'version: latest' before any Docker build step on self-hosted runners."
111
+ - "Periodically update self-hosted runner Docker Engine and Buildx to latest stable versions."
112
+ - "Subscribe to the GitHub Changelog (github.blog/changelog) for advance notice of cache service API changes."
113
+ - "Verify Buildx version in CI: add 'docker buildx version' as a diagnostic step to catch version drift early."
114
+ - "Consider using registry cache (type=registry) as a more portable alternative to type=gha on self-hosted runners."
115
+ docs:
116
+ - url: 'https://github.com/docker/build-push-action/issues/1345'
117
+ label: 'docker/build-push-action#1345 — type=gha cache failure after legacy service shutdown (Apr 2025)'
118
+ - url: 'https://docs.docker.com/build/ci/github-actions/cache/#github-cache'
119
+ label: 'Docker Docs — GitHub Actions cache backend (note: legacy v1 API shut down April 15, 2025)'
120
+ - url: 'https://github.com/docker/setup-buildx-action'
121
+ label: 'docker/setup-buildx-action — Install latest Buildx on any runner'
122
+ - url: 'https://gh.io/gha-cache-sunset'
123
+ label: 'GitHub — GHA cache service v1 sunset announcement'
@@ -0,0 +1,85 @@
1
+ id: concurrency-timing-051
2
+ title: 'merge_group event has no pull_request.number — PR-number-keyed concurrency group collapses all merge queue runs to the same slot'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - concurrency
7
+ - merge-queue
8
+ - merge_group
9
+ - pull_request
10
+ - cancel-in-progress
11
+ - null-key
12
+ patterns:
13
+ - regex: 'merge_group'
14
+ flags: 'i'
15
+ - regex: 'pull_request\.number'
16
+ flags: 'i'
17
+ - regex: 'Canceling since a higher priority waiting run was found'
18
+ flags: 'i'
19
+ error_messages:
20
+ - 'This run was automatically cancelled'
21
+ - 'Canceling since a higher priority waiting run was found'
22
+ - 'Required status checks did not pass'
23
+ root_cause: |
24
+ The merge_group event payload does not include a pull_request object. When a workflow
25
+ uses ${{ github.event.pull_request.number }} in a concurrency group key — a common
26
+ pattern to scope CI runs per open PR — the expression evaluates to an empty string
27
+ for all merge_group-triggered runs.
28
+
29
+ All merge queue validation runs therefore share an identical concurrency group key
30
+ (e.g., ci- instead of ci-42). With cancel-in-progress: true, each new merge queue
31
+ batch immediately cancels the currently running validation for all other batches in
32
+ the queue. With cancel-in-progress: false, only the most-recently-queued batch runs;
33
+ earlier batches are ejected from the merge queue with 'Required status checks did
34
+ not pass'.
35
+
36
+ This silently serializes or destroys merge queue throughput and commonly occurs when
37
+ developers add merge_group to an existing pull_request workflow that already uses
38
+ PR-number-based concurrency without updating the group expression.
39
+ fix: |
40
+ Use a conditional expression that resolves to a unique key for both event types.
41
+ For merge_group events, github.event.merge_group.head_sha provides a value that is
42
+ unique per merge queue batch:
43
+
44
+ group: ci-${{ github.event.pull_request.number || github.event.merge_group.head_sha }}
45
+ fix_code:
46
+ - language: yaml
47
+ label: 'Broken: merge_group runs collapse to ci- (empty PR number)'
48
+ code: |
49
+ on:
50
+ pull_request:
51
+ merge_group:
52
+
53
+ concurrency:
54
+ group: ci-${{ github.event.pull_request.number }}
55
+ cancel-in-progress: true
56
+ - language: yaml
57
+ label: 'Fixed: fallback to merge_group.head_sha for merge queue runs'
58
+ code: |
59
+ on:
60
+ pull_request:
61
+ merge_group:
62
+
63
+ concurrency:
64
+ # pull_request: keyed by PR number (e.g., ci-42)
65
+ # merge_group: keyed by batch SHA (unique per merge queue batch)
66
+ group: ci-${{ github.event.pull_request.number || github.event.merge_group.head_sha }}
67
+ cancel-in-progress: true
68
+
69
+ jobs:
70
+ test:
71
+ runs-on: ubuntu-latest
72
+ steps:
73
+ - uses: actions/checkout@v4
74
+ - run: make test
75
+ prevention:
76
+ - 'When adding merge_group to an existing pull_request workflow, audit every concurrency group expression for github.event.pull_request.* references — all are null on merge_group events'
77
+ - 'Use github.event.merge_group.head_sha as the fallback identifier for merge queue runs; it is unique per batch'
78
+ - 'Verify the resolved concurrency key by adding a debug step that prints the evaluated expression for each event type'
79
+ docs:
80
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#merge_group'
81
+ label: 'GitHub Docs: merge_group event'
82
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-concurrency'
83
+ label: 'GitHub Docs: Using concurrency'
84
+ - url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue'
85
+ label: 'GitHub Docs: Managing a merge queue'
@@ -0,0 +1,84 @@
1
+ id: concurrency-timing-050
2
+ title: 'push and schedule sharing a concurrency group silently cancel each other on the default branch'
3
+ category: concurrency-timing
4
+ severity: silent-failure
5
+ tags:
6
+ - concurrency
7
+ - push
8
+ - schedule
9
+ - cancel-in-progress
10
+ - nightly-build
11
+ - silent-failure
12
+ patterns:
13
+ - regex: 'Canceling since a higher priority waiting run was found'
14
+ flags: 'i'
15
+ - regex: 'This run was automatically cancelled'
16
+ flags: 'i'
17
+ error_messages:
18
+ - 'This run was automatically cancelled'
19
+ - 'Canceling since a higher priority waiting run was found'
20
+ root_cause: |
21
+ When a workflow responds to both on.push and on.schedule, and uses a concurrency
22
+ group key that does not include github.event_name, all push and schedule runs share
23
+ the same concurrency slot.
24
+
25
+ Both a push to the default branch and a scheduled run targeting the default branch
26
+ evaluate github.ref to refs/heads/main (or the configured default branch name).
27
+ A concurrency group of ${{ github.workflow }}-${{ github.ref }} therefore produces
28
+ an identical key for both event types.
29
+
30
+ With cancel-in-progress: true, any push commit that lands while a scheduled run
31
+ (nightly build, report, data sync) is executing immediately cancels that run with
32
+ no error notification. With cancel-in-progress: false, the one pending-run slot is
33
+ taken by the push run, silently displacing any queued scheduled run.
34
+
35
+ This is distinct from the workflow_dispatch + schedule pattern (concurrency-timing-047)
36
+ because push events are emitted automatically on every commit and are far more
37
+ frequent than manual dispatches, making accidental cancellation of scheduled runs a
38
+ routine occurrence rather than an occasional manual event.
39
+ fix: |
40
+ Include github.event_name in the concurrency group key so that push runs and
41
+ schedule runs each occupy a separate, independent slot:
42
+
43
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
44
+ fix_code:
45
+ - language: yaml
46
+ label: 'Broken: push and schedule share one slot'
47
+ code: |
48
+ on:
49
+ push:
50
+ branches: [main]
51
+ schedule:
52
+ - cron: '0 2 * * *'
53
+
54
+ concurrency:
55
+ group: ${{ github.workflow }}-${{ github.ref }}
56
+ cancel-in-progress: true
57
+ - language: yaml
58
+ label: 'Fixed: include github.event_name to isolate push and schedule concurrency slots'
59
+ code: |
60
+ on:
61
+ push:
62
+ branches: [main]
63
+ schedule:
64
+ - cron: '0 2 * * *'
65
+
66
+ concurrency:
67
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
68
+ cancel-in-progress: true
69
+
70
+ jobs:
71
+ ci:
72
+ runs-on: ubuntu-latest
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+ - run: make test
76
+ prevention:
77
+ - 'Always include ${{ github.event_name }} in the concurrency group key for workflows triggered by both automated (push/pull_request) and scheduled events'
78
+ - 'After adding on.schedule to an existing push-triggered workflow, audit the concurrency group key to confirm scheduled runs receive their own independent slot'
79
+ - 'Monitor the Actions tab for unexpected gaps in scheduled run history — silent cancellations leave no failed run, only a missing run for that time slot'
80
+ docs:
81
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-concurrency'
82
+ label: 'GitHub Docs: Using concurrency'
83
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule'
84
+ label: 'GitHub Docs: schedule event'
@@ -0,0 +1,91 @@
1
+ id: known-unsolved-059
2
+ title: 'vars context in reusable workflows reflects the CALLER repository variables — the called workflow repository-level vars are inaccessible'
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - vars
7
+ - reusable-workflow
8
+ - workflow_call
9
+ - variables
10
+ - configuration
11
+ - known-limitation
12
+ patterns:
13
+ - regex: 'vars\.[A-Z_]+'
14
+ flags: 'i'
15
+ - regex: 'workflow_call'
16
+ flags: 'i'
17
+ error_messages:
18
+ - '(no runtime error — vars context silently returns caller repository values instead of called workflow repository values)'
19
+ root_cause: |
20
+ GitHub Actions populates the vars context in a reusable workflow called via
21
+ workflow_call using the CALLER repository organization, repository, and environment
22
+ variables — not those defined in the repository that hosts the reusable workflow.
23
+
24
+ This means repository-level variables set in the reusable workflow repository are
25
+ not accessible when the workflow executes in another repository context. The vars
26
+ context in the called workflow reflects only:
27
+ - Organization-level variables visible to the caller
28
+ - The CALLER repository-level variables
29
+ - Environment-scoped variables from environments the caller deploys to
30
+
31
+ Unlike secrets (which has secrets: inherit), there is no vars: inherit mechanism.
32
+ A shared reusable workflow library that stores its own configuration in
33
+ repository-level variables cannot access those values at runtime when called
34
+ from another repository.
35
+
36
+ The workflow runs without error but references wrong or empty variable values
37
+ silently, producing incorrect behavior that is difficult to diagnose because
38
+ the runner logs show no permission error.
39
+ fix: |
40
+ There is no built-in mechanism to make the called workflow repository-level
41
+ variables available at runtime.
42
+
43
+ Available workarounds:
44
+ 1. Promote shared configuration to organization-level variables — visible to
45
+ all member repositories and available in both caller and called contexts.
46
+ 2. Pass required values as explicit workflow_call inputs from the caller:
47
+ with:
48
+ registry_url: ${{ vars.REGISTRY_URL }}
49
+ 3. Store shared configuration in a committed file in the called workflow
50
+ repository and read it via a step after checkout.
51
+ fix_code:
52
+ - language: yaml
53
+ label: 'Workaround: pass configuration values as explicit workflow_call inputs'
54
+ code: |
55
+ # Caller repository workflow
56
+ jobs:
57
+ deploy:
58
+ uses: org/shared-workflows/.github/workflows/deploy.yml@main
59
+ with:
60
+ deploy_env: ${{ vars.DEPLOY_ENV }}
61
+ registry_url: ${{ vars.REGISTRY_URL }}
62
+ secrets: inherit
63
+
64
+ ---
65
+ # Called reusable workflow (in org/shared-workflows repo)
66
+ on:
67
+ workflow_call:
68
+ inputs:
69
+ deploy_env:
70
+ type: string
71
+ required: true
72
+ registry_url:
73
+ type: string
74
+ required: true
75
+ jobs:
76
+ deploy:
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - run: echo "Deploying to ${{ inputs.deploy_env }}"
80
+ prevention:
81
+ - 'Do not rely on repository-level vars in a shared reusable workflow library repo — they will not be available when the workflow runs in another repository context'
82
+ - 'Use organization-level variables for shared configuration that must be accessible from reusable workflows across all member repositories'
83
+ - 'Document all required configuration values as explicit workflow_call inputs with type and description so callers know exactly what to pass'
84
+ - 'Audit shared workflow libraries for vars. references that assume the called repo context — they will silently read caller repo values or return empty strings'
85
+ docs:
86
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#vars-context'
87
+ label: 'GitHub Docs: vars context'
88
+ - url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows#passing-inputs-and-secrets-to-a-reusable-workflow'
89
+ label: 'GitHub Docs: Passing inputs and secrets to a reusable workflow'
90
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables'
91
+ label: 'GitHub Docs: Store information in variables'
@@ -0,0 +1,104 @@
1
+ id: permissions-auth-063
2
+ title: 'security-events: write required for SARIF upload — missing permission causes Resource not accessible by integration'
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - security-events
7
+ - sarif
8
+ - code-scanning
9
+ - permissions
10
+ - upload-sarif
11
+ - github-token
12
+ patterns:
13
+ - regex: 'Resource not accessible by integration'
14
+ flags: 'i'
15
+ - regex: 'HttpError: Resource not accessible by integration'
16
+ flags: 'i'
17
+ - regex: 'Code scanning is not enabled for this repository'
18
+ flags: 'i'
19
+ - regex: 'Advanced Security must be enabled for this repository to use code scanning'
20
+ flags: 'i'
21
+ error_messages:
22
+ - 'Resource not accessible by integration'
23
+ - 'HttpError: Resource not accessible by integration'
24
+ - 'Code scanning is not enabled for this repository'
25
+ - 'Advanced Security must be enabled for this repository to use code scanning'
26
+ root_cause: |
27
+ The github/codeql-action/upload-sarif action and third-party security scanning
28
+ actions (Trivy, Snyk, Semgrep, osv-scanner, etc.) submit SARIF results to the
29
+ GitHub code scanning API endpoint POST /repos/{owner}/{repo}/code-scanning/sarifs.
30
+ This endpoint requires the security-events: write permission on the GITHUB_TOKEN.
31
+
32
+ Since February 2023, newly created repositories default GITHUB_TOKEN to read-only
33
+ permissions. A workflow that calls upload-sarif without an explicit
34
+ security-events: write permission block will receive 403 Resource not accessible by
35
+ integration from the API and the scan results will not appear in the Security tab.
36
+
37
+ This error is commonly overlooked because:
38
+ 1. The scanning step (CodeQL analyze, Trivy scan, Semgrep scan) often completes
39
+ successfully — the red X appears only on the upload step.
40
+ 2. Example workflows published before the read-only-default policy may omit the
41
+ security-events: write permission line.
42
+ 3. For fork pull requests, the pull_request trigger cannot write security-events
43
+ even with the permission declared — a workflow_run split pattern is required.
44
+ fix: |
45
+ Add security-events: write to the job permissions block. Include contents: read
46
+ for checkout and actions: read if the workflow uses CodeQL (which needs to read
47
+ workflow definitions).
48
+ fix_code:
49
+ - language: yaml
50
+ label: 'Add security-events: write for SARIF upload'
51
+ code: |
52
+ jobs:
53
+ scan:
54
+ runs-on: ubuntu-latest
55
+ permissions:
56
+ security-events: write # required for upload-sarif
57
+ contents: read # required for checkout
58
+ actions: read # required by CodeQL to read workflow info
59
+ steps:
60
+ - uses: actions/checkout@v4
61
+ - name: Run Trivy vulnerability scanner
62
+ uses: aquasecurity/trivy-action@master
63
+ with:
64
+ scan-type: 'fs'
65
+ format: 'sarif'
66
+ output: 'trivy-results.sarif'
67
+ - name: Upload Trivy results to GitHub Security tab
68
+ uses: github/codeql-action/upload-sarif@v3
69
+ with:
70
+ sarif_file: 'trivy-results.sarif'
71
+ - language: yaml
72
+ label: 'Fork PRs: upload SARIF in a workflow_run triggered workflow (has write access)'
73
+ code: |
74
+ # Scanning step runs in pull_request context (no write access from fork)
75
+ # Upload step runs in workflow_run context (has security-events: write)
76
+ on:
77
+ workflow_run:
78
+ workflows: ['Security Scan']
79
+ types: [completed]
80
+ jobs:
81
+ upload-results:
82
+ runs-on: ubuntu-latest
83
+ permissions:
84
+ security-events: write
85
+ steps:
86
+ - name: Download SARIF artifact
87
+ uses: actions/download-artifact@v4
88
+ with:
89
+ run-id: ${{ github.event.workflow_run.id }}
90
+ name: sarif-results
91
+ - uses: github/codeql-action/upload-sarif@v3
92
+ with:
93
+ sarif_file: results.sarif
94
+ prevention:
95
+ - 'Add security-events: write to every job that calls github/codeql-action/upload-sarif or any third-party SARIF upload action'
96
+ - 'For fork pull request scanning, use the workflow_run + artifact pattern — the pull_request trigger cannot write security-events from fork contexts'
97
+ - 'Check the Security tab after a scan workflow succeeds — absence of scan results despite a green workflow is the primary indicator of missing security-events permission'
98
+ docs:
99
+ - url: 'https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github'
100
+ label: 'GitHub Docs: Uploading a SARIF file to GitHub'
101
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs'
102
+ label: 'GitHub Docs: Assigning permissions to jobs'
103
+ - url: 'https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/results-are-different-than-expected'
104
+ label: 'GitHub Docs: Code scanning troubleshooting — results are different than expected'
@@ -0,0 +1,132 @@
1
+ id: runner-environment-180
2
+ title: 'ubuntu-24.04 removes Mono/MSBuild/NuGet — .NET Framework net48/net40 builds fail with "command not found"'
3
+ category: runner-environment
4
+ severity: error
5
+ tags:
6
+ - ubuntu-24
7
+ - ubuntu-latest
8
+ - mono
9
+ - msbuild
10
+ - nuget
11
+ - dotnet-framework
12
+ - migration
13
+ - tool-removed
14
+ patterns:
15
+ - regex: 'mono: command not found'
16
+ flags: 'i'
17
+ - regex: 'msbuild: command not found'
18
+ flags: 'i'
19
+ - regex: '/usr/bin/msbuild.*No such file or directory'
20
+ flags: 'i'
21
+ - regex: 'nuget: command not found'
22
+ flags: 'i'
23
+ - regex: 'Could not find file.*msbuild\.exe'
24
+ flags: 'i'
25
+ error_messages:
26
+ - "mono: command not found"
27
+ - "msbuild: command not found"
28
+ - "nuget: command not found"
29
+ - "/usr/bin/msbuild: No such file or directory"
30
+ - "bash: line 1: mono: command not found"
31
+ root_cause: |
32
+ When GitHub Actions migrated `ubuntu-latest` to Ubuntu 24.04 (rollout Dec 2024 –
33
+ Jan 2025, runner-images#10636), the Mono framework, mono-based MSBuild, and mono-based
34
+ NuGet were intentionally removed from the Ubuntu 24.04 runner image. These packages
35
+ are not available in Ubuntu 24.04 (Noble Numbat) apt repositories because Mono is not
36
+ maintained for Noble.
37
+
38
+ Affected workflows:
39
+ - .NET Framework projects targeting net40, net45, net46, net47, net48 built on Linux
40
+ using Mono (common for Unity CI, Windows-targeted libraries, legacy C# projects)
41
+ - Any workflow running `mono`, `msbuild`, or `nuget` shell commands on ubuntu-latest
42
+ - Third-party actions that internally call mono (e.g., Unity build actions, some .NET
43
+ test runners)
44
+
45
+ The failure is immediate and obvious ("command not found") but the root cause is often
46
+ unclear because the workflow worked fine on ubuntu-22.04. The ubuntu-latest label
47
+ silently changed the underlying image, not the workflow YAML.
48
+
49
+ Source: actions/runner-images#10636 (384 reactions, closed Jan 2025),
50
+ actions/runner-images#11675 (mono specifically, closed "not planned" — Mono will NOT
51
+ be added back to ubuntu-24.04).
52
+ fix: |
53
+ Option 1 (recommended for most cases): Switch to windows-latest
54
+ .NET Framework targets are first-class on Windows runners which still include MSBuild,
55
+ NuGet, and full .NET Framework support. Most .csproj net48 builds run correctly on
56
+ windows-latest.
57
+
58
+ Option 2: Install Mono at runtime on Ubuntu 22.04 (not 24.04)
59
+ Pin to ubuntu-22.04 (supported until April 2027) and install Mono via apt at runtime:
60
+ sudo apt-get install -y mono-complete msbuild
61
+
62
+ Option 3: Migrate to .NET Core / .NET 6+ with multi-targeting
63
+ Add a `net8.0` target to your csproj alongside `net48` for CI builds on Linux, and
64
+ build the Linux target on ubuntu-latest. The Windows-only target can be built on
65
+ windows-latest in a separate matrix job.
66
+
67
+ Option 4: Use a Docker container with Mono
68
+ Run the build step in a `mono:latest` container:
69
+ container: mono:latest
70
+ fix_code:
71
+ - language: yaml
72
+ label: 'Switch net48 / Mono builds to windows-latest'
73
+ code: |
74
+ jobs:
75
+ build:
76
+ runs-on: windows-latest # .NET Framework is fully supported on Windows runners
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+ - name: Setup MSBuild
80
+ uses: microsoft/setup-msbuild@v2
81
+ - name: Restore NuGet packages
82
+ run: nuget restore MySolution.sln
83
+ - name: Build
84
+ run: msbuild MySolution.sln /p:Configuration=Release
85
+ - language: yaml
86
+ label: 'Pin to ubuntu-22.04 and install Mono at runtime'
87
+ code: |
88
+ jobs:
89
+ build:
90
+ runs-on: ubuntu-22.04 # ubuntu-24.04 has no Mono; 22.04 supported until April 2027
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ - name: Install Mono
94
+ run: |
95
+ sudo apt-get update -q
96
+ sudo apt-get install -y mono-complete msbuild
97
+ - name: Build
98
+ run: msbuild MySolution.sln /p:Configuration=Release
99
+ - language: yaml
100
+ label: 'Matrix build separating Linux .NET Core from Windows .NET Framework'
101
+ code: |
102
+ jobs:
103
+ build:
104
+ strategy:
105
+ matrix:
106
+ include:
107
+ - os: ubuntu-latest
108
+ target: net8.0
109
+ - os: windows-latest
110
+ target: net48
111
+ runs-on: ${{ matrix.os }}
112
+ steps:
113
+ - uses: actions/checkout@v4
114
+ - uses: microsoft/setup-msbuild@v2
115
+ if: runner.os == 'Windows'
116
+ - name: Build
117
+ run: dotnet build -f ${{ matrix.target }} -c Release
118
+ prevention:
119
+ - 'Pin `runs-on: ubuntu-22.04` for any workflow requiring Mono, msbuild, or nuget CLI on Linux'
120
+ - 'Use `windows-latest` for all .NET Framework (net40/45/46/47/48) builds — MSBuild is native there'
121
+ - 'Audit workflows for bare `mono`, `msbuild`, or `nuget` commands before migrating ubuntu-latest'
122
+ - 'Consider migrating legacy .NET Framework projects to .NET 8+ for platform-agnostic CI'
123
+ - 'Check runner software lists at https://github.com/actions/runner-images for tool availability per OS'
124
+ docs:
125
+ - url: 'https://github.com/actions/runner-images/issues/10636'
126
+ label: 'actions/runner-images#10636: Ubuntu-latest migrates to Ubuntu-24.04 (384 reactions, Jan 2025)'
127
+ - url: 'https://github.com/actions/runner-images/issues/11675'
128
+ label: 'actions/runner-images#11675: Mono missing on Ubuntu 24.04 (closed not_planned — will not be added back)'
129
+ - url: 'https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md'
130
+ label: 'Ubuntu 24.04 runner image software list'
131
+ - url: 'https://github.com/microsoft/setup-msbuild'
132
+ label: 'microsoft/setup-msbuild action for Windows runners'