Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions .github/workflows/build-release-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,10 @@ jobs:
if: ${{ inputs.enable_nuget }}
needs: [get-version, validate-release]
runs-on: ubuntu-latest
# OIDC trusted publishing through NuGet/login needs id-token: write, granted by the caller on its call to this task.
# It is declared alongside contents: read, since a job-level permissions: block zeroes every unlisted scope, and this job's checkout steps need it.
permissions:
contents: read
id-token: write
# OIDC trusted publishing through NuGet/login needs id-token: write, and the caller grants it on its call to this task when it sets nuget: true, a real push.
# A build-only run (enable_nuget without nuget, or any smoke build) never reaches NuGet/login and needs no such grant.
# No job-level permissions: block here, because a called job's block is validated against the caller's grant before its if: runs.
# A block naming id-token would fail every caller that does not grant it at startup, including a smoke build under a read-only pull request token.
steps:

- name: Checkout code step
Expand Down Expand Up @@ -534,10 +533,9 @@ jobs:
if: ${{ inputs.github && !inputs.smoke && !failure() && !cancelled() }}
runs-on: ubuntu-latest
needs: [get-version, validate-release, build-executable, build-nuget, build-pypi, build-docker]
# The release upload and the artifact-delete cleanup both write with GITHUB_TOKEN, and the caller grants these.
permissions:
contents: write
actions: write
# The release upload and the artifact-delete cleanup both write with GITHUB_TOKEN, and the caller grants contents: write and actions: write when it sets github: true on a non-smoke run.
# A caller publishing only to a registry, or smoke building, leaves this job disabled and grants neither.
# No job-level permissions: block here, for the reason build-nuget gives: a block is validated against the caller's grant before if: runs, and a smoke caller holds a read-only token.

steps:

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/validate-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
# This is #729, decided here, the one place the fleet's uvx tools are pinned or floated.
- name: Setup uv step
if: hashFiles('pyproject.toml') != ''
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.13"

Expand Down Expand Up @@ -230,7 +230,7 @@ jobs:

- name: Setup uv step
if: hashFiles('pyproject.toml') != '' && hashFiles('tests/**') != '' && hashFiles('uv.lock') != ''
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
with:
python-version: "3.13"

Expand Down
2 changes: 1 addition & 1 deletion docs/reusable-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ A repo whose publisher needs the release-gate decision reaches `publish-plan-tas

## Adopting the Release Chain

A downstream repo replaces the whole of its `.github/workflows/build-release-task.yml`, and every per-target leaf task it carries (`build-executable-task.yml`, `build-nugetlibrary-task.yml`, `build-pypilibrary-task.yml`, `build-docker-task.yml`), with a caller stub in its own `publish-release.yml` reaching the hub tasks by pin. `test-pull-request.yml`'s smoke job calls `build-release-task.yml` the same way, with `smoke: true` and the paths-filter's `enable_*` outputs. Nothing here lands as a catalog snippet in this pull request, since a caller stub's pin can only name a released hub commit and no release yet carries `build-release-task.yml` ([Pinning][pinning]). The snippet follows the release that first ships it, tracked in the [Rollout][rollout] section below.
A downstream repo replaces the whole of its `.github/workflows/build-release-task.yml`, and every per-target leaf task it carries (`build-executable-task.yml`, `build-nugetlibrary-task.yml`, `build-pypilibrary-task.yml`, `build-docker-task.yml`), with a caller stub in its own `publish-release.yml` reaching the hub tasks by pin. `test-pull-request.yml`'s smoke job calls `build-release-task.yml` the same way, with `smoke: true` and the paths-filter's `enable_*` outputs. Nothing here lands as a catalog snippet in this pull request, since a caller stub's pin can only name a released hub commit and no release yet carries `build-release-task.yml` ([Pinning][pinning]). The snippet follows the release that first ships it, tracked in the [Rollout][rollout] section below. The task declares no job-level `permissions:` of its own, because a called job's block is validated against the caller's grant before its `if:` runs and would fail a caller that does not grant it at startup. The caller therefore grants only what its enabled paths write with: `contents: write` and `actions: write` when it sets `github: true` on a non-smoke run (the release upload and the artifact cleanup), `id-token: write` when it sets `nuget: true` (a real push through `NuGet/login`), and nothing beyond `contents: read` on a build-only or smoke run, where a Dependabot pull request holds a read-only token.

The stub keeps its own trigger policy exactly as today: `workflow_dispatch` plus a main-only weekly `schedule` for a Docker repo, or `workflow_dispatch` plus a paths-filtered `push` to `main` for a NuGet or PyPI repo whose merges should auto-publish. What moves to the hub is the release-gate decision, the build/version/publish job graph, and the Docker core, never the trigger. This is the full shape, a NuGet-library repo whose merges publish:

Expand Down