From aef2f982ca51e8a9739ca77881b3378ff8fd0ead Mon Sep 17 00:00:00 2001 From: monsieurleberre Date: Thu, 8 Oct 2026 08:25:57 -0300 Subject: [PATCH 1/3] fix(csharp-ci): restore with github.token instead of the bot PAT Restore and pack authenticate to GitHub Packages with the job's github.token. BOT_GITHUB_TOKEN stays declared as optional and deprecated. Claude-Session: https://claude.ai/code/session_01C8uJmZ2aaTMnWXHQKWJezu --- .github/workflows/csharp-ci.yaml | 13 +++++++------ CHANGELOG.md | 4 ++++ README.md | 14 ++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/csharp-ci.yaml b/.github/workflows/csharp-ci.yaml index 5ae1034..3764ad3 100644 --- a/.github/workflows/csharp-ci.yaml +++ b/.github/workflows/csharp-ci.yaml @@ -23,10 +23,11 @@ on: secrets: BOT_GITHUB_TOKEN: description: >- - PAT or GitHub App token with `read:packages` for `dotnet restore` - against private NuGet feeds (e.g. GitHub Packages). Optional; - omit for public-only restores. Pass via `secrets: inherit` or an - explicit `secrets:` block on the caller. + Deprecated and unused. `dotnet restore` and `dotnet pack` authenticate + to GitHub Packages with the job's `github.token` (the jobs declare + `packages: read`). Kept declared and optional so callers that name it + in an explicit `secrets:` block keep working; an inherited or passed + value is never exposed to the steps. required: false inputs: concurrency-group: @@ -312,7 +313,7 @@ jobs: - name: Restore env: GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail dotnet restore @@ -590,7 +591,7 @@ jobs: - name: Restore env: GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail dotnet restore diff --git a/CHANGELOG.md b/CHANGELOG.md index f6cfda5..d053ad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- `csharp-ci.yaml` authenticates `dotnet restore` and `dotnet pack` against GitHub Packages with the job's `github.token` (the `build-and-test` and `pack` jobs already declare `packages: read`) instead of `secrets.BOT_GITHUB_TOKEN`, so a long-lived PAT is no longer in the environment of steps that execute pull-request-controlled MSBuild files (`Directory.Build.props`, `nuget.config`, project targets). The `BOT_GITHUB_TOKEN` secret stays declared as optional and deprecated, so callers that name it in an explicit `secrets:` block do not break; its value is ignored. A private package owned by another repository is readable with `github.token` only when the consumer repository is listed under the package's "Manage Actions access" settings with the Read role; grant that before moving `v2`, or restores of such packages fail with 401/403. + ## [2.6.0] - 2026-09-29 ### Added diff --git a/README.md b/README.md index 9ea3cdc..93a530d 100644 --- a/README.md +++ b/README.md @@ -151,12 +151,14 @@ choosing which shard carries coverage — use `build-matrix` instead (see | `pack` | `false` | When true, also runs `dotnet pack` and uploads `.nupkg` artifacts. | | `pack-project` | *(empty)* | Project to pack. Required when `pack: true`. | -**Optional secrets**: - -- `BOT_GITHUB_TOKEN` — PAT or GitHub App token with `read:packages` for - `dotnet restore` against private NuGet feeds (e.g. GitHub Packages). - Omit for public-only restores. Pass via `secrets: inherit` or an - explicit `secrets:` block on the caller. +**Private NuGet feeds**: `dotnet restore` and `dotnet pack` authenticate to +GitHub Packages with the job's `github.token` (`packages: read`); no secret is +needed. For a private package owned by another repository, add the consumer +repository under the package's "Manage Actions access" settings with the Read +role. + +**Deprecated secret**: `BOT_GITHUB_TOKEN` is still accepted but unused, so +callers that name it in an explicit `secrets:` block keep working. Consumer `.github/workflows/csharp-ci.yaml`: From 5a7bf4f8f7d99822ef86a9feafff67998ee9767b Mon Sep 17 00:00:00 2001 From: monsieurleberre Date: Fri, 9 Oct 2026 07:50:35 -0300 Subject: [PATCH 2/3] fix(csharp-ci): withhold the restore token from fork pull requests Restore sets GITHUB_TOKEN only when the run is not a fork pull request, and the build-and-test and pack checkouts use persist-credentials: false, so fork-controlled MSBuild never sees a packages: read token. README and CHANGELOG say to grant package Actions access only to private consumers; public repos resolve from nuget.org. Claude-Session: https://claude.ai/code/session_01C8uJmZ2aaTMnWXHQKWJezu --- .github/workflows/csharp-ci.yaml | 9 +++++++-- CHANGELOG.md | 3 ++- README.md | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/csharp-ci.yaml b/.github/workflows/csharp-ci.yaml index 3764ad3..97061ba 100644 --- a/.github/workflows/csharp-ci.yaml +++ b/.github/workflows/csharp-ci.yaml @@ -278,6 +278,8 @@ jobs: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Setup .NET uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 @@ -293,6 +295,7 @@ jobs: repository: peacefulstudio/github-actions ref: ${{ job.workflow_sha }} path: .github-actions-helpers + persist-credentials: false - name: Resolve dotnet-coverage version if: ${{ matrix.coverage }} @@ -313,7 +316,7 @@ jobs: - name: Restore env: GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ (!github.event.pull_request || github.event.pull_request.head.repo.full_name == github.repository) && github.token || '' }} run: | set -euo pipefail dotnet restore @@ -580,6 +583,8 @@ jobs: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Setup .NET uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 @@ -591,7 +596,7 @@ jobs: - name: Restore env: GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ (!github.event.pull_request || github.event.pull_request.head.repo.full_name == github.repository) && github.token || '' }} run: | set -euo pipefail dotnet restore diff --git a/CHANGELOG.md b/CHANGELOG.md index d053ad1..8e21ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- `csharp-ci.yaml` authenticates `dotnet restore` and `dotnet pack` against GitHub Packages with the job's `github.token` (the `build-and-test` and `pack` jobs already declare `packages: read`) instead of `secrets.BOT_GITHUB_TOKEN`, so a long-lived PAT is no longer in the environment of steps that execute pull-request-controlled MSBuild files (`Directory.Build.props`, `nuget.config`, project targets). The `BOT_GITHUB_TOKEN` secret stays declared as optional and deprecated, so callers that name it in an explicit `secrets:` block do not break; its value is ignored. A private package owned by another repository is readable with `github.token` only when the consumer repository is listed under the package's "Manage Actions access" settings with the Read role; grant that before moving `v2`, or restores of such packages fail with 401/403. +- `csharp-ci.yaml` authenticates `dotnet restore` and `dotnet pack` against GitHub Packages with the job's `github.token` (the `build-and-test` and `pack` jobs already declare `packages: read`) instead of `secrets.BOT_GITHUB_TOKEN`, so a long-lived PAT is no longer in the environment of steps that execute pull-request-controlled MSBuild files (`Directory.Build.props`, `nuget.config`, project targets). The `BOT_GITHUB_TOKEN` secret stays declared as optional and deprecated, so callers that name it in an explicit `secrets:` block do not break; its value is ignored. A private package owned by another repository is readable with `github.token` only when the consumer repository is listed under the package's "Manage Actions access" settings with the Read role; grant that before moving `v2`, or restores of such packages fail with 401/403. Grant it only to private consumer repositories: a public repository must resolve every package from nuget.org. +- `csharp-ci.yaml` withholds the restore credential on pull requests from forks: `Restore` sets `GITHUB_TOKEN` to `github.token` only when the run is not a pull request, or the pull request's head repository is the calling repository, and to an empty string otherwise, so fork-controlled MSBuild never sees a `packages: read` token. The `actions/checkout` steps in `build-and-test` and `pack` set `persist-credentials: false`, so the token is not left in the checkout's git credentials either. Fork pull requests restore from public sources (nuget.org) only. ## [2.6.0] - 2026-09-29 diff --git a/README.md b/README.md index 93a530d..178680b 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,9 @@ choosing which shard carries coverage — use `build-matrix` instead (see GitHub Packages with the job's `github.token` (`packages: read`); no secret is needed. For a private package owned by another repository, add the consumer repository under the package's "Manage Actions access" settings with the Read -role. +role. Grant that only to private consumer repositories: a public repository +must resolve every package from nuget.org. On a pull request from a fork, +`Restore` runs with an empty `GITHUB_TOKEN`, so only public sources resolve. **Deprecated secret**: `BOT_GITHUB_TOKEN` is still accepted but unused, so callers that name it in an explicit `secrets:` block keep working. From e96a002b0f064040fba23a994542769b2da8c4c4 Mon Sep 17 00:00:00 2001 From: monsieurleberre Date: Fri, 9 Oct 2026 08:00:05 -0300 Subject: [PATCH 3/3] fix(csharp-ci): restore without any credential The private GitHub Packages feed is retired and every package resolves from nuget.org, so the Restore steps carry no token and the jobs no longer need packages: read. Claude-Session: https://claude.ai/code/session_01C8uJmZ2aaTMnWXHQKWJezu --- .github/workflows/csharp-ci.yaml | 15 +++------------ CHANGELOG.md | 3 +-- README.md | 10 +++------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/.github/workflows/csharp-ci.yaml b/.github/workflows/csharp-ci.yaml index 97061ba..2601100 100644 --- a/.github/workflows/csharp-ci.yaml +++ b/.github/workflows/csharp-ci.yaml @@ -23,10 +23,9 @@ on: secrets: BOT_GITHUB_TOKEN: description: >- - Deprecated and unused. `dotnet restore` and `dotnet pack` authenticate - to GitHub Packages with the job's `github.token` (the jobs declare - `packages: read`). Kept declared and optional so callers that name it - in an explicit `secrets:` block keep working; an inherited or passed + Deprecated and unused. `dotnet restore` runs without any credential and + resolves from nuget.org. Kept declared and optional so callers that name + it in an explicit `secrets:` block keep working; an inherited or passed value is never exposed to the steps. required: false inputs: @@ -263,7 +262,6 @@ jobs: include: ${{ fromJSON(needs.normalize.outputs.matrix) }} permissions: contents: read - packages: read pull-requests: write defaults: run: @@ -314,9 +312,6 @@ jobs: "$GITHUB_WORKSPACE/.github-actions-helpers/scripts/install-dotnet-coverage.sh" "$DOTNET_COVERAGE_VERSION" - name: Restore - env: - GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ (!github.event.pull_request || github.event.pull_request.head.repo.full_name == github.repository) && github.token || '' }} run: | set -euo pipefail dotnet restore @@ -557,7 +552,6 @@ jobs: runs-on: ${{ inputs.plumbing-runner != '' && (startsWith(inputs.plumbing-runner, '[') && fromJSON(inputs.plumbing-runner) || inputs.plumbing-runner) || ((inputs.matrix-mode != '' && inputs.matrix-mode || vars.CI_MATRIX_MODE) == 'cheap' && github.event.repository.private == true && fromJSON('["self-hosted","hetzner"]') || 'ubuntu-latest') }} permissions: contents: read - packages: read defaults: run: shell: bash @@ -594,9 +588,6 @@ jobs: global-json-file: ${{ inputs.dotnet-version == '' && ((inputs.working-directory == '.' || inputs.working-directory == '') && 'global.json' || format('{0}/global.json', inputs.working-directory)) || '' }} - name: Restore - env: - GITHUB_USERNAME: ${{ github.actor }} - GITHUB_TOKEN: ${{ (!github.event.pull_request || github.event.pull_request.head.repo.full_name == github.repository) && github.token || '' }} run: | set -euo pipefail dotnet restore diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e21ff5..02ea1a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- `csharp-ci.yaml` authenticates `dotnet restore` and `dotnet pack` against GitHub Packages with the job's `github.token` (the `build-and-test` and `pack` jobs already declare `packages: read`) instead of `secrets.BOT_GITHUB_TOKEN`, so a long-lived PAT is no longer in the environment of steps that execute pull-request-controlled MSBuild files (`Directory.Build.props`, `nuget.config`, project targets). The `BOT_GITHUB_TOKEN` secret stays declared as optional and deprecated, so callers that name it in an explicit `secrets:` block do not break; its value is ignored. A private package owned by another repository is readable with `github.token` only when the consumer repository is listed under the package's "Manage Actions access" settings with the Read role; grant that before moving `v2`, or restores of such packages fail with 401/403. Grant it only to private consumer repositories: a public repository must resolve every package from nuget.org. -- `csharp-ci.yaml` withholds the restore credential on pull requests from forks: `Restore` sets `GITHUB_TOKEN` to `github.token` only when the run is not a pull request, or the pull request's head repository is the calling repository, and to an empty string otherwise, so fork-controlled MSBuild never sees a `packages: read` token. The `actions/checkout` steps in `build-and-test` and `pack` set `persist-credentials: false`, so the token is not left in the checkout's git credentials either. Fork pull requests restore from public sources (nuget.org) only. +- `csharp-ci.yaml` runs `dotnet restore` and `dotnet pack` without any credential: the `Restore` steps no longer receive `secrets.BOT_GITHUB_TOKEN` or `github.token`, so no token is in the environment of steps that execute pull-request-controlled MSBuild files (`Directory.Build.props`, `nuget.config`, project targets), and the `actions/checkout` steps set `persist-credentials: false`. Every package resolves from nuget.org; the private GitHub Packages feed is retired, so no package "Manage Actions access" grants are needed. The `BOT_GITHUB_TOKEN` secret stays declared as optional and deprecated, so callers that name it in an explicit `secrets:` block do not break; its value is ignored. ## [2.6.0] - 2026-09-29 diff --git a/README.md b/README.md index 178680b..9d2b107 100644 --- a/README.md +++ b/README.md @@ -151,13 +151,9 @@ choosing which shard carries coverage — use `build-matrix` instead (see | `pack` | `false` | When true, also runs `dotnet pack` and uploads `.nupkg` artifacts. | | `pack-project` | *(empty)* | Project to pack. Required when `pack: true`. | -**Private NuGet feeds**: `dotnet restore` and `dotnet pack` authenticate to -GitHub Packages with the job's `github.token` (`packages: read`); no secret is -needed. For a private package owned by another repository, add the consumer -repository under the package's "Manage Actions access" settings with the Read -role. Grant that only to private consumer repositories: a public repository -must resolve every package from nuget.org. On a pull request from a fork, -`Restore` runs with an empty `GITHUB_TOKEN`, so only public sources resolve. +**Restore is credential-free**: `dotnet restore` and `dotnet pack` run with no +token and resolve every package from nuget.org. The private GitHub Packages feed +is retired, so no package grants are needed. **Deprecated secret**: `BOT_GITHUB_TOKEN` is still accepted but unused, so callers that name it in an explicit `secrets:` block keep working.