From eb9dfc176f694deda05a87eb6a445154767b473e Mon Sep 17 00:00:00 2001 From: Nick Gallegos Date: Thu, 1 Oct 2026 14:59:24 -0600 Subject: [PATCH 1/3] Add tests for push-nuget-packages action # Conflicts: # .github/actions/push-nuget-packages/action.yml --- .../actions/push-nuget-packages/action.yml | 68 +++++++++---- .github/workflows/CI-actions.yml | 12 +-- .../workflows/test-push-nuget-packages.yml | 98 +++++++++++++++++++ 3 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/test-push-nuget-packages.yml diff --git a/.github/actions/push-nuget-packages/action.yml b/.github/actions/push-nuget-packages/action.yml index 4c6889c..5b79f28 100644 --- a/.github/actions/push-nuget-packages/action.yml +++ b/.github/actions/push-nuget-packages/action.yml @@ -29,6 +29,10 @@ inputs: required: false default: '' description: API key used to authenticate the push (required when publish-mode is api-key) + dry-run: + required: false + default: 'false' + description: If true, skips real release downloads, NuGet trusted-publishing login, the actual push to NuGet and logs what would happen instead. runs: using: composite steps: @@ -68,9 +72,17 @@ runs: shell: pwsh run: | $version = "${{ inputs.version }}" - gh release download $version --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets - if ($LASTEXITCODE -ne 0) { - throw "Failed to download release assets for tag '$version'" + + if ('${{ inputs.dry-run }}' -eq 'true') + { + Write-Output "[dry-run] Would download assets from published release '$version'." + } + else + { + gh release download $version --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets + if ($LASTEXITCODE -ne 0) { + throw "Failed to download release assets for tag '$version'" + } } env: GH_TOKEN: ${{ github.token }} @@ -79,15 +91,23 @@ runs: shell: pwsh run: | $name = "${{ inputs.version }}" - $releases = gh api "repos/${{ github.repository }}/releases" | ConvertFrom-Json - $release = $releases | Where-Object { $_.name -eq $name } | Select-Object -First 1 - if (-not $release) { - throw "Could not find a draft release named '$name'" + + if ('${{ inputs.dry-run }}' -eq 'true') + { + Write-Output "[dry-run] Would look up and download assets from draft release named '$name'." } - Write-Output "Found draft release id $($release.id) with tag '$($release.tag_name)' for '$name'" - gh release download $release.tag_name --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets - if ($LASTEXITCODE -ne 0) { - throw "Failed to download release assets for tag '$($release.tag_name)'" + else + { + $releases = gh api "repos/${{ github.repository }}/releases" | ConvertFrom-Json + $release = $releases | Where-Object { $_.name -eq $name } | Select-Object -First 1 + if (-not $release) { + throw "Could not find a draft release named '$name'" + } + Write-Output "Found draft release id $($release.id) with tag '$($release.tag_name)' for '$name'" + gh release download $release.tag_name --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets + if ($LASTEXITCODE -ne 0) { + throw "Failed to download release assets for tag '$($release.tag_name)'" + } } env: GH_TOKEN: ${{ github.token }} @@ -99,7 +119,7 @@ runs: $domain = ([Uri]"${{ inputs.feed-url }}").GetLeftPart([System.UriPartial]::Authority) Write-Output "domain=$domain" >> $env:GITHUB_OUTPUT - name: Get NuGet trusted publishing API key - if: inputs.publish-mode == 'trusted-publishing' + if: inputs.publish-mode == 'trusted-publishing' && inputs.dry-run != 'true' uses: NuGet/login@v1 id: login with: @@ -109,15 +129,22 @@ runs: - name: Push NuGet packages shell: pwsh run: | - $getChildItemParams = @{ Path = 'nugets/*'; Include = '*.nupkg' } - $excludePattern = "${{ inputs.exclude-package-pattern }}" - if ($excludePattern) { $getChildItemParams.Exclude = $excludePattern } - $packages = Get-ChildItem @getChildItemParams $source = "${{ inputs.feed-url }}" - $apiKey = if ("${{ inputs.publish-mode }}" -eq 'trusted-publishing') { "${{ steps.login.outputs.NUGET_API_KEY }}" } else { "${{ inputs.api-key }}" } - Write-Output "Pushing $($packages.Count) NuGet package(s) to $source`:" - $packages | ForEach-Object { Write-Output " - $($_.Name)" } - $failed = @() + + if ('${{ inputs.dry-run }}' -eq 'true') + { + Write-Output "[dry-run] Would push NuGet package(s) found under 'nugets/' to $source." + } + else + { + $getChildItemParams = @{ Path = 'nugets/*'; Include = '*.nupkg' } + $excludePattern = "${{ inputs.exclude-package-pattern }}" + if ($excludePattern) { $getChildItemParams.Exclude = $excludePattern } + $packages = Get-ChildItem @getChildItemParams + $apiKey = if ("${{ inputs.publish-mode }}" -eq 'trusted-publishing') { "${{ steps.login.outputs.NUGET_API_KEY }}" } else { "${{ inputs.api-key }}" } + Write-Output "Pushing $($packages.Count) NuGet package(s) to $source`:" + $packages | ForEach-Object { Write-Output " - $($_.Name)" } +$failed = @() foreach ($package in $packages) { dotnet nuget push $package.FullName --source $source --api-key $apiKey --skip-duplicate @@ -125,4 +152,5 @@ runs: } if ($failed.Count -gt 0) { throw "Failed to push $($failed.Count) package(s): $($failed -join ', ')" + } } diff --git a/.github/workflows/CI-actions.yml b/.github/workflows/CI-actions.yml index 0546aa1..9696ec6 100644 --- a/.github/workflows/CI-actions.yml +++ b/.github/workflows/CI-actions.yml @@ -7,19 +7,13 @@ on: paths: - '.github/actions/**' jobs: - test-actions: - runs-on: ubuntu-latest - defaults: - run: - shell: pwsh - steps: - - name: Placeholder - run: echo "Placeholder for testing actions" + test-push-nuget-packages: + uses: ./.github/workflows/test-push-nuget-packages.yml validate-action-tests: # So we can add new tests without having to update branch protection rules name: Validate action test results - needs: [test-actions] + needs: [test-push-nuget-packages] if: always() runs-on: ubuntu-latest defaults: diff --git a/.github/workflows/test-push-nuget-packages.yml b/.github/workflows/test-push-nuget-packages.yml new file mode 100644 index 0000000..5445513 --- /dev/null +++ b/.github/workflows/test-push-nuget-packages.yml @@ -0,0 +1,98 @@ +name: Test - push-nuget-packages +on: + workflow_call: +jobs: + test-validation: + name: push-nuget-packages / ${{ matrix.scenario }} + runs-on: ubuntu-latest + defaults: + run: + shell: pwsh + strategy: + fail-fast: false + matrix: + include: + - scenario: invalid publish-mode + publish-mode: not-a-mode + release-state: published + nuget-user: '' + api-key: '' + feed-url: https://nuget.example.org + - scenario: invalid release-state + publish-mode: api-key + release-state: not-a-state + nuget-user: '' + api-key: some-key + feed-url: https://nuget.example.org + - scenario: trusted-publishing missing nuget-user + publish-mode: trusted-publishing + release-state: published + nuget-user: '' + api-key: '' + feed-url: https://nuget.example.org + - scenario: trusted-publishing with conflicting api-key + publish-mode: trusted-publishing + release-state: published + nuget-user: someuser + api-key: some-key + feed-url: https://nuget.example.org + - scenario: api-key mode missing api-key + publish-mode: api-key + release-state: published + nuget-user: '' + api-key: '' + feed-url: https://nuget.example.org + - scenario: invalid feed-url + publish-mode: api-key + release-state: published + nuget-user: '' + api-key: some-key + feed-url: not-a-url + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + - name: Push NuGet packages (expected to fail) + id: push + continue-on-error: true + uses: ./.github/actions/push-nuget-packages + with: + version: 1.2.3 + release-state: ${{ matrix.release-state }} + publish-mode: ${{ matrix.publish-mode }} + nuget-user: ${{ matrix.nuget-user }} + api-key: ${{ matrix.api-key }} + feed-url: ${{ matrix.feed-url }} + dry-run: true + - name: Verify it failed + run: | + $outcome = '${{ steps.push.outcome }}' + if ($outcome -ne 'failure') + { + throw "Expected push-nuget-packages to fail for scenario '${{ matrix.scenario }}', but outcome was '$outcome'." + } + Write-Output "OK: failed as expected ($outcome)" + + test-valid-combinations: + name: push-nuget-packages / valid - ${{ matrix.release-state }} / ${{ matrix.publish-mode }} + runs-on: ubuntu-latest + defaults: + run: + shell: pwsh + strategy: + fail-fast: false + matrix: + release-state: [published, draft] + publish-mode: [trusted-publishing, api-key] + steps: + - name: Checkout + uses: actions/checkout@v7.0.1 + - name: Push NuGet packages + uses: ./.github/actions/push-nuget-packages + with: + version: 1.2.3 + release-state: ${{ matrix.release-state }} + publish-mode: ${{ matrix.publish-mode }} + nuget-user: ${{ matrix.publish-mode == 'trusted-publishing' && 'test-user' || '' }} + api-key: ${{ matrix.publish-mode == 'api-key' && 'test-key' || '' }} + feed-url: https://nuget.example.org + dry-run: true From 3283df122f5d346613a6845acb812f09513e66c9 Mon Sep 17 00:00:00 2001 From: Nick Gallegos Date: Thu, 1 Oct 2026 15:26:33 -0600 Subject: [PATCH 2/3] Try using a single job to make the scenario names show up in the actions side-nav --- .../workflows/test-push-nuget-packages.yml | 88 ++++++++++++------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test-push-nuget-packages.yml b/.github/workflows/test-push-nuget-packages.yml index 5445513..5a314ff 100644 --- a/.github/workflows/test-push-nuget-packages.yml +++ b/.github/workflows/test-push-nuget-packages.yml @@ -2,8 +2,8 @@ name: Test - push-nuget-packages on: workflow_call: jobs: - test-validation: - name: push-nuget-packages / ${{ matrix.scenario }} + test-push-nuget-packages: + name: ${{ matrix.scenario }} runs-on: ubuntu-latest defaults: run: @@ -18,42 +18,76 @@ jobs: nuget-user: '' api-key: '' feed-url: https://nuget.example.org + expect-failure: true - scenario: invalid release-state publish-mode: api-key release-state: not-a-state nuget-user: '' api-key: some-key feed-url: https://nuget.example.org + expect-failure: true - scenario: trusted-publishing missing nuget-user publish-mode: trusted-publishing release-state: published nuget-user: '' api-key: '' feed-url: https://nuget.example.org + expect-failure: true - scenario: trusted-publishing with conflicting api-key publish-mode: trusted-publishing release-state: published nuget-user: someuser api-key: some-key feed-url: https://nuget.example.org + expect-failure: true - scenario: api-key mode missing api-key publish-mode: api-key release-state: published nuget-user: '' api-key: '' feed-url: https://nuget.example.org + expect-failure: true - scenario: invalid feed-url publish-mode: api-key release-state: published nuget-user: '' api-key: some-key feed-url: not-a-url + expect-failure: true + - scenario: valid (published, trusted-publishing) + publish-mode: trusted-publishing + release-state: published + nuget-user: test-user + api-key: '' + feed-url: https://nuget.example.org + expect-failure: false + - scenario: valid (published, api-key) + publish-mode: api-key + release-state: published + nuget-user: '' + api-key: test-key + feed-url: https://nuget.example.org + expect-failure: false + - scenario: valid (draft, trusted-publishing) + publish-mode: trusted-publishing + release-state: draft + nuget-user: test-user + api-key: '' + feed-url: https://nuget.example.org + expect-failure: false + - scenario: valid (draft, api-key) + publish-mode: api-key + release-state: draft + nuget-user: '' + api-key: test-key + feed-url: https://nuget.example.org + expect-failure: false steps: - name: Checkout uses: actions/checkout@v7.0.1 - - name: Push NuGet packages (expected to fail) + - name: Push NuGet packages id: push - continue-on-error: true + continue-on-error: ${{ matrix.expect-failure }} uses: ./.github/actions/push-nuget-packages with: version: 1.2.3 @@ -63,36 +97,24 @@ jobs: api-key: ${{ matrix.api-key }} feed-url: ${{ matrix.feed-url }} dry-run: true - - name: Verify it failed + - name: Verify run: | + $expectFailure = '${{ matrix.expect-failure }}' -eq 'true' $outcome = '${{ steps.push.outcome }}' - if ($outcome -ne 'failure') + + if ($expectFailure) { - throw "Expected push-nuget-packages to fail for scenario '${{ matrix.scenario }}', but outcome was '$outcome'." + if ($outcome -ne 'failure') + { + throw "Expected push-nuget-packages to fail for scenario '${{ matrix.scenario }}', but outcome was '$outcome'." + } + Write-Output "OK: failed as expected ($outcome)" + } + else + { + if ($outcome -ne 'success') + { + throw "Expected push-nuget-packages to succeed for scenario '${{ matrix.scenario }}', but outcome was '$outcome'." + } + Write-Output "OK: succeeded as expected ($outcome)" } - Write-Output "OK: failed as expected ($outcome)" - - test-valid-combinations: - name: push-nuget-packages / valid - ${{ matrix.release-state }} / ${{ matrix.publish-mode }} - runs-on: ubuntu-latest - defaults: - run: - shell: pwsh - strategy: - fail-fast: false - matrix: - release-state: [published, draft] - publish-mode: [trusted-publishing, api-key] - steps: - - name: Checkout - uses: actions/checkout@v7.0.1 - - name: Push NuGet packages - uses: ./.github/actions/push-nuget-packages - with: - version: 1.2.3 - release-state: ${{ matrix.release-state }} - publish-mode: ${{ matrix.publish-mode }} - nuget-user: ${{ matrix.publish-mode == 'trusted-publishing' && 'test-user' || '' }} - api-key: ${{ matrix.publish-mode == 'api-key' && 'test-key' || '' }} - feed-url: https://nuget.example.org - dry-run: true From 3a7530075bbd561d74931e70449381d46e5e221b Mon Sep 17 00:00:00 2001 From: Nick Gallegos Date: Fri, 2 Oct 2026 11:07:12 -0600 Subject: [PATCH 3/3] Fix rebase --- .github/actions/push-nuget-packages/action.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/push-nuget-packages/action.yml b/.github/actions/push-nuget-packages/action.yml index 5b79f28..c1c1428 100644 --- a/.github/actions/push-nuget-packages/action.yml +++ b/.github/actions/push-nuget-packages/action.yml @@ -144,13 +144,13 @@ runs: $apiKey = if ("${{ inputs.publish-mode }}" -eq 'trusted-publishing') { "${{ steps.login.outputs.NUGET_API_KEY }}" } else { "${{ inputs.api-key }}" } Write-Output "Pushing $($packages.Count) NuGet package(s) to $source`:" $packages | ForEach-Object { Write-Output " - $($_.Name)" } -$failed = @() - foreach ($package in $packages) - { - dotnet nuget push $package.FullName --source $source --api-key $apiKey --skip-duplicate - if ($LASTEXITCODE -ne 0) { $failed += $package.Name } - } - if ($failed.Count -gt 0) { - throw "Failed to push $($failed.Count) package(s): $($failed -join ', ')" + $failed = @() + foreach ($package in $packages) + { + dotnet nuget push $package.FullName --source $source --api-key $apiKey --skip-duplicate + if ($LASTEXITCODE -ne 0) { $failed += $package.Name } + } + if ($failed.Count -gt 0) { + throw "Failed to push $($failed.Count) package(s): $($failed -join ', ')" } }