diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 44f22a1..a09236d 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -7,7 +7,7 @@ on: workflow_call: inputs: runs-on: - description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Public/open-source repos opt out by passing '[\"ubuntu-24.04\"]'." + description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Ignored for public repos, which always run on '[\"ubuntu-24.04\"]'." type: string # Self-hosted by default since 2026-09-20, matching claude.yml. # @@ -34,11 +34,14 @@ on: # advisory regardless: the claude-code-action step below is # continue-on-error, so it cannot block a PR on either runner. # - # PUBLIC REPOS SHOULD OPT OUT, and not only for the free minutes: - # running fork PRs on a self-hosted pool is the hazard GitHub warns - # about. No public repo consumes this reusable today (checked across - # the three orgs and nkg/*), so the default is safe as it stands -- - # but a new public consumer must pass '["ubuntu-24.04"]'. + # PUBLIC REPOS never use this input. The job's runs-on forces + # ["ubuntu-24.04"] whenever github.event.repository.visibility is + # "public": fork PRs on a self-hosted pool are the hazard GitHub + # warns about, hosted minutes are free there, and a public caller + # that omits `runs-on` would otherwise queue for 24h on a pool with + # no runner registered. An event whose payload carries no + # repository object falls back to this input, so public callers + # should still pass a hosted `runs-on` as well. # # Revisit when credits reset; hosted is the right default the moment # it can actually start a job. @@ -87,7 +90,7 @@ jobs: contains(inputs.filter-authors, github.event.pull_request.user.login) || (inputs.filter-first-time-only && (github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' || github.event.pull_request.author_association == 'FIRST_TIMER')) ) - runs-on: ${{ fromJSON(inputs.runs-on) }} + runs-on: ${{ fromJSON(github.event.repository.visibility == 'public' && '["ubuntu-24.04"]' || inputs.runs-on) }} timeout-minutes: ${{ inputs.timeout-minutes }} # `claude-code-action` posts review comments back on the PR, so it # needs write on pull-requests and issues. The `claude.yml` (bot) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index b20d7db..085716f 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -29,7 +29,7 @@ on: workflow_call: inputs: runs-on: - description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Public/open-source repos opt out by passing '[\"ubuntu-latest\"]'." + description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Ignored for public repos, which always run on '[\"ubuntu-latest\"]'." type: string # Self-hosted by default since 2026-09-20. Actions credits are # exhausted across HordiaLabs, Sproncy and Regularmusic, so a hosted @@ -39,9 +39,16 @@ on: # running nothing at all: 4 calls (Sproncy: sproncy-schemas, sproncy-secrets-dashboard, sproncy-transcription, sproncy-website-scrapers). # # Every other consumer already passes `runs-on` explicitly, so this - # changes behaviour only for those that did not. Public repos should - # pass '["ubuntu-latest"]' -- fork PRs on a self-hosted pool are the - # hazard GitHub warns about; no public repo consumes this today. + # changes behaviour only for those that did not. + # + # Public repos never use this input. The job's runs-on forces + # ["ubuntu-latest"] whenever github.event.repository.visibility is + # "public": fork PRs on a self-hosted pool are the hazard GitHub + # warns about, hosted minutes are free there, and a public caller + # that omits `runs-on` would otherwise queue for 24h on a pool with + # no runner registered. An event whose payload carries no + # repository object falls back to this input, so public callers + # should still pass a hosted `runs-on` as well. default: '["self-hosted", "linux", "x64"]' merge-strategy: description: "squash | merge | rebase" @@ -73,7 +80,7 @@ jobs: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.actor.login == 'dependabot[bot]') - runs-on: ${{ fromJSON(inputs.runs-on) }} + runs-on: ${{ fromJSON(github.event.repository.visibility == 'public' && '["ubuntu-latest"]' || inputs.runs-on) }} timeout-minutes: 10 permissions: contents: write diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml index 5a50bd4..2f7cdb0 100644 --- a/.github/workflows/lint-workflows.yml +++ b/.github/workflows/lint-workflows.yml @@ -7,7 +7,7 @@ on: workflow_call: inputs: runs-on: - description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Public/open-source repos opt out by passing '[\"ubuntu-latest\"]'." + description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Ignored for public repos, which always run on '[\"ubuntu-latest\"]'." type: string # Self-hosted by default since 2026-09-20. Actions credits are # exhausted across HordiaLabs, Sproncy and Regularmusic, so a hosted @@ -17,9 +17,16 @@ on: # running nothing at all: 1 call (Sproncy/agent-skills). # # Every other consumer already passes `runs-on` explicitly, so this - # changes behaviour only for those that did not. Public repos should - # pass '["ubuntu-latest"]' -- fork PRs on a self-hosted pool are the - # hazard GitHub warns about; no public repo consumes this today. + # changes behaviour only for those that did not. + # + # Public repos never use this input. The job's runs-on forces + # ["ubuntu-latest"] whenever github.event.repository.visibility is + # "public": fork PRs on a self-hosted pool are the hazard GitHub + # warns about, hosted minutes are free there, and a public caller + # that omits `runs-on` would otherwise queue for 24h on a pool with + # no runner registered. An event whose payload carries no + # repository object falls back to this input, so public callers + # should still pass a hosted `runs-on` as well. default: '["self-hosted", "linux", "x64"]' actionlint-version: type: string @@ -39,7 +46,7 @@ on: jobs: actionlint: - runs-on: ${{ fromJSON(inputs.runs-on) }} + runs-on: ${{ fromJSON(github.event.repository.visibility == 'public' && '["ubuntu-latest"]' || inputs.runs-on) }} timeout-minutes: 10 permissions: contents: read @@ -68,7 +75,7 @@ jobs: yamllint: if: inputs.run-yamllint - runs-on: ${{ fromJSON(inputs.runs-on) }} + runs-on: ${{ fromJSON(github.event.repository.visibility == 'public' && '["ubuntu-latest"]' || inputs.runs-on) }} timeout-minutes: 10 permissions: contents: read diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 0cb8532..ebdebff 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -13,7 +13,7 @@ on: workflow_call: inputs: runs-on: - description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Public/open-source repos opt out by passing '[\"ubuntu-latest\"]'." + description: "Runner label(s) as a JSON array string (parsed with fromJSON). Defaults to the self-hosted pool. Ignored for public repos, which always run on '[\"ubuntu-latest\"]'." type: string # Self-hosted by default since 2026-09-20. Actions credits are # exhausted across HordiaLabs, Sproncy and Regularmusic, so a hosted @@ -23,9 +23,16 @@ on: # running nothing at all: 6 calls (Sproncy: GitHub-runners, agent-skills, sproncy-schemas, sproncy-secrets-dashboard, sproncy-transcription, sproncy-website-scrapers). # # Every other consumer already passes `runs-on` explicitly, so this - # changes behaviour only for those that did not. Public repos should - # pass '["ubuntu-latest"]' -- fork PRs on a self-hosted pool are the - # hazard GitHub warns about; no public repo consumes this today. + # changes behaviour only for those that did not. + # + # Public repos never use this input. The job's runs-on forces + # ["ubuntu-latest"] whenever github.event.repository.visibility is + # "public": fork PRs on a self-hosted pool are the hazard GitHub + # warns about, hosted minutes are free there, and a public caller + # that omits `runs-on` would otherwise queue for 24h on a pool with + # no runner registered. An event whose payload carries no + # repository object falls back to this input, so public callers + # should still pass a hosted `runs-on` as well. default: '["self-hosted", "linux", "x64"]' betterleaks-version: type: string @@ -66,7 +73,7 @@ on: jobs: scan: - runs-on: ${{ fromJSON(inputs.runs-on) }} + runs-on: ${{ fromJSON(github.event.repository.visibility == 'public' && '["ubuntu-latest"]' || inputs.runs-on) }} timeout-minutes: 15 permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c4677..161cc92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ project uses [SemVer](https://semver.org/) for the `vMAJOR.MINOR.PATCH` tags. ## [Unreleased] +## [3.7.0] - 2026-10-19 + +### Changed + +- **Public repos always get GitHub-hosted runners from `secret-scan.yml`, + `lint-workflows.yml`, `dependabot-auto-merge.yml` and + `claude-code-review.yml`.** Each job's `runs-on` now overrides the input + with `["ubuntu-latest"]` (`["ubuntu-24.04"]` for the review) whenever + `github.event.repository.visibility` is `public`. The comments added in + 3.6.0 said no public repo consumes these, which was wrong: six `nkg/*` + public repos call `secret-scan` or `lint-workflows`. The ones that left + `runs-on` unset (`lint-workflows` in `nkg/gha-token-server` and + `nkg/distrobuilder-proxmox-lxc-images`), or had no self-hosted runner + registered (`nkg/gha-nomad-dispatcher`), queued for 24h and were cancelled + on every run. Self-hosted would also have run fork PRs on our own hosts. + Private callers are unchanged. An event with no `repository` in its payload + falls back to the input, so public callers should keep passing a hosted + `runs-on`. (#93) + ### Fixed - **`docker-build.yml` no longer tags an image before Trivy has scanned it.** @@ -1319,7 +1338,8 @@ README; everything below is the actual content of this repo. - Bumped `actions/checkout` from `@v4` to `@v6` across all existing workflows for consistency with new files. -[Unreleased]: https://github.com/nkg/github-actions/compare/v3.6.0...HEAD +[Unreleased]: https://github.com/nkg/github-actions/compare/v3.7.0...HEAD +[3.7.0]: https://github.com/nkg/github-actions/compare/v3.6.0...v3.7.0 [3.6.0]: https://github.com/nkg/github-actions/compare/v3.5.0...v3.6.0 [3.5.0]: https://github.com/nkg/github-actions/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/nkg/github-actions/compare/v3.3.0...v3.4.0