From 7ecff6abb33138048b90541e2183438a615b705f Mon Sep 17 00:00:00 2001 From: erwan-joly Date: Sun, 30 Aug 2026 20:39:31 +1200 Subject: [PATCH 1/4] ci: trigger releases on tag push, drop deprecated release actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The create trigger fired on every branch creation, checkout@v2 and setup-dotnet@v3 run on the retired node16 runtime, and the release upload used jossef/action-latest-release-info + the archived actions/upload-release-asset@v1 with a REPO_TOKEN PAT — attaching the package to the previous release when the new tag has none. Aligned with the NosCore.Packets workflow. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dotnet.yml | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f619f3e..dcff035 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -3,7 +3,6 @@ name: .NET on: push: branches: [ master ] - create: tags: - '*.*.*' pull_request: @@ -11,17 +10,19 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + contents: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 10.0.x + dotnet-version: '10.0.x' - name: Check Tag id: check-tag run: | - if [[ v${{ github.event.ref }} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ ${{ github.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "match=true" >> $GITHUB_OUTPUT fi @@ -35,27 +36,17 @@ jobs: if: steps.check-tag.outputs.match == 'true' id: build_artifact run: | + TAG_NAME=${GITHUB_REF#refs/tags/} dotnet restore dotnet build -c Release dotnet pack -c Release -o /tmp/nupkgs -v m -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg - dotnet nuget push /tmp/nupkgs/NosCore.PathFinder.${{github.event.ref}}.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} - echo "ARTIFACT_PATH=/tmp/nupkgs/NosCore.PathFinder.${{github.event.ref}}.nupkg" >> $GITHUB_OUTPUT - echo "ARTIFACT_NAME=NosCore.PathFinder.${{github.event.ref}}.nupkg" >> $GITHUB_OUTPUT - - - name: Gets Latest Release - if: steps.check-tag.outputs.match == 'true' - id: latest_release_info - uses: jossef/action-latest-release-info@v1.1.0 - env: - GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + dotnet nuget push /tmp/nupkgs/NosCore.PathFinder.${TAG_NAME}.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} + echo "ARTIFACT_PATH=/tmp/nupkgs/NosCore.PathFinder.${TAG_NAME}.nupkg" >> $GITHUB_OUTPUT - name: Upload Release Asset if: steps.check-tag.outputs.match == 'true' - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + uses: softprops/action-gh-release@v2 with: - upload_url: ${{ steps.latest_release_info.outputs.upload_url }} - asset_path: ${{ steps.build_artifact.outputs.ARTIFACT_PATH }} - asset_name: ${{ steps.build_artifact.outputs.ARTIFACT_NAME }} - asset_content_type: application/zip + files: ${{ steps.build_artifact.outputs.ARTIFACT_PATH }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e7db6abd728a7935ca10062e83e2654abb1d9da4 Mon Sep 17 00:00:00 2001 From: erwan-joly Date: Sun, 30 Aug 2026 20:49:23 +1200 Subject: [PATCH 2/4] ci: read GITHUB_REF from the environment, version the package from the tag Interpolating github.ref into the Bash source lets a crafted tag execute shell code on a runner holding the NuGet key before the regex check runs; reading $GITHUB_REF from the environment removes the injection point. PackageVersion now comes from the tag so a tag no longer has to match the csproj version for the push path to exist. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dotnet.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index dcff035..1583ca6 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -22,7 +22,7 @@ jobs: - name: Check Tag id: check-tag run: | - if [[ ${{ github.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ "$GITHUB_REF" =~ ^refs/tags/[0-9]+.[0-9]+.[0-9]+$ ]]; then echo "match=true" >> $GITHUB_OUTPUT fi @@ -39,7 +39,7 @@ jobs: TAG_NAME=${GITHUB_REF#refs/tags/} dotnet restore dotnet build -c Release - dotnet pack -c Release -o /tmp/nupkgs -v m -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg + dotnet pack -c Release -o /tmp/nupkgs -v m -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -p:PackageVersion=${TAG_NAME} dotnet nuget push /tmp/nupkgs/NosCore.PathFinder.${TAG_NAME}.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} echo "ARTIFACT_PATH=/tmp/nupkgs/NosCore.PathFinder.${TAG_NAME}.nupkg" >> $GITHUB_OUTPUT From de25b18f06553adbe2c4f3207c31547095a040e7 Mon Sep 17 00:00:00 2001 From: erwan-joly Date: Sun, 30 Aug 2026 20:51:35 +1200 Subject: [PATCH 3/4] ci: match literal dots in the tag regex Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 1583ca6..e1f412b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -22,7 +22,7 @@ jobs: - name: Check Tag id: check-tag run: | - if [[ "$GITHUB_REF" =~ ^refs/tags/[0-9]+.[0-9]+.[0-9]+$ ]]; then + if [[ "$GITHUB_REF" =~ ^refs/tags/[0-9]+[.][0-9]+[.][0-9]+$ ]]; then echo "match=true" >> $GITHUB_OUTPUT fi From e33a6e81dc946152a4eebbb61ba6dc3b4f9e9bb4 Mon Sep 17 00:00:00 2001 From: erwan-joly Date: Sun, 30 Aug 2026 21:24:02 +1200 Subject: [PATCH 4/4] ci: read-only build job, write token confined to the tag release job Build and test ran pull-request code in a job holding a contents: write token that checkout also persisted into .git/config. Validation now runs in a read-only job with persist-credentials: false; packing, the NuGet push and the release upload move to a tag-gated job that alone gets contents: write. action-gh-release bumped to v3 for the current runner runtime. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/dotnet.yml | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e1f412b..a7c70f5 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -7,13 +7,38 @@ on: - '*.*.*' pull_request: branches: [ master ] + +permissions: + contents: read + jobs: build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10.0.x' + + - name: Run Unit Tests + run: | + dotnet restore + dotnet build + dotnet test test/NosCore.PathFinder.Tests -v m + + release: + needs: build + if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup .NET uses: actions/setup-dotnet@v5 with: @@ -26,13 +51,7 @@ jobs: echo "match=true" >> $GITHUB_OUTPUT fi - - name: Run Unit Tests - run: | - dotnet restore - dotnet build - dotnet test test/NosCore.PathFinder.Tests -v m - - - name: Build Artifact + - name: Pack and push to NuGet if: steps.check-tag.outputs.match == 'true' id: build_artifact run: | @@ -45,7 +64,7 @@ jobs: - name: Upload Release Asset if: steps.check-tag.outputs.match == 'true' - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: files: ${{ steps.build_artifact.outputs.ARTIFACT_PATH }} env: