Skip to content

Commit 071201f

Browse files
committed
feat: add ENABLE_NUGET switch and NUGET_USE_OIDC dual-mode NuGet publishing
- ENABLE_NUGET (default true): controls entire NuGet pipeline (pack + push) - NUGET_USE_OIDC (default false): false = API key, true = OIDC Trusted Publishing - When enabled, misconfigured credentials fail immediately (no silent fallback) - When disabled, all NuGet steps are cleanly skipped - Attestation subjects adapt dynamically based on ENABLE_NUGET Made-with: Cursor
1 parent f275bc7 commit 071201f

1 file changed

Lines changed: 73 additions & 19 deletions

File tree

.github/workflows/ci.yml

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ env:
3939
DOTNET_CLI_TELEMETRY_OPTOUT: true
4040
NUKE_TELEMETRY_OPTOUT: true
4141
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
42+
ENABLE_NUGET: 'true'
43+
NUGET_USE_OIDC: 'false'
4244
ENABLE_ANDROID: 'false'
4345
ENABLE_IOS: 'false'
4446

@@ -184,10 +186,12 @@ jobs:
184186
shell: bash
185187
run: |
186188
IS_RELEASE="${{ steps.resolve.outputs.is_release }}"
189+
PACK=$( [ "$ENABLE_NUGET" = "true" ] && echo "true" || echo "false" )
190+
187191
if [ "$IS_RELEASE" = "true" ]; then
188-
ENTRIES='{"os":"ubuntu-latest","runtime":"linux-x64","pack":true,"publish":true,"workloads":""}'
189-
ENTRIES+=',{"os":"windows-latest","runtime":"win-x64","pack":false,"publish":true,"workloads":""}'
190-
ENTRIES+=',{"os":"macos-latest","runtime":"osx-arm64","pack":false,"publish":true,"workloads":""}'
192+
ENTRIES="{\"os\":\"ubuntu-latest\",\"runtime\":\"linux-x64\",\"pack\":$PACK,\"publish\":true,\"workloads\":\"\"}"
193+
ENTRIES+=",{\"os\":\"windows-latest\",\"runtime\":\"win-x64\",\"pack\":false,\"publish\":true,\"workloads\":\"\"}"
194+
ENTRIES+=",{\"os\":\"macos-latest\",\"runtime\":\"osx-arm64\",\"pack\":false,\"publish\":true,\"workloads\":\"\"}"
191195
192196
if [ "$ENABLE_ANDROID" = "true" ]; then
193197
ENTRIES+=',{"os":"ubuntu-latest","runtime":"android","pack":false,"publish":false,"workloads":"android"}'
@@ -196,9 +200,9 @@ jobs:
196200
ENTRIES+=',{"os":"macos-latest","runtime":"ios","pack":false,"publish":false,"workloads":"ios"}'
197201
fi
198202
else
199-
ENTRIES='{"os":"ubuntu-latest","runtime":"linux-x64","pack":true,"publish":false,"workloads":""}'
200-
ENTRIES+=',{"os":"windows-latest","runtime":"win-x64","pack":false,"publish":false,"workloads":""}'
201-
ENTRIES+=',{"os":"macos-latest","runtime":"osx-arm64","pack":false,"publish":false,"workloads":""}'
203+
ENTRIES="{\"os\":\"ubuntu-latest\",\"runtime\":\"linux-x64\",\"pack\":$PACK,\"publish\":false,\"workloads\":\"\"}"
204+
ENTRIES+=",{\"os\":\"windows-latest\",\"runtime\":\"win-x64\",\"pack\":false,\"publish\":false,\"workloads\":\"\"}"
205+
ENTRIES+=",{\"os\":\"macos-latest\",\"runtime\":\"osx-arm64\",\"pack\":false,\"publish\":false,\"workloads\":\"\"}"
202206
203207
if [ "$ENABLE_ANDROID" = "true" ]; then
204208
ENTRIES+=',{"os":"ubuntu-latest","runtime":"android","pack":false,"publish":false,"workloads":"android"}'
@@ -433,6 +437,7 @@ jobs:
433437
global-json-file: global.json
434438

435439
- name: Download packages
440+
if: env.ENABLE_NUGET == 'true'
436441
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
437442
with:
438443
name: packages
@@ -449,15 +454,48 @@ jobs:
449454
run: chmod +x build.sh
450455

451456
- name: Verify release manifest
457+
if: env.ENABLE_NUGET == 'true'
452458
shell: bash
453459
run: ./build.sh ValidateReleaseManifest
454460

455-
- name: Push to NuGet.org
456-
if: secrets.NUGET_API_KEY != ''
461+
- name: NuGet login (Trusted Publishing / OIDC)
462+
if: env.ENABLE_NUGET == 'true' && env.NUGET_USE_OIDC == 'true'
463+
id: nuget-login
464+
uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544 # v1.1.0
465+
with:
466+
user: ${{ vars.NUGET_USER || github.repository_owner }}
467+
468+
- name: Push to NuGet.org (OIDC)
469+
if: env.ENABLE_NUGET == 'true' && env.NUGET_USE_OIDC == 'true'
470+
shell: bash
471+
env:
472+
API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
473+
run: |
474+
shopt -s nullglob
475+
PACKAGES=(artifacts/packages/*.nupkg)
476+
if [ ${#PACKAGES[@]} -eq 0 ]; then
477+
echo "::error::No .nupkg files found"
478+
exit 1
479+
fi
480+
for pkg in "${PACKAGES[@]}"; do
481+
echo "Pushing $pkg"
482+
dotnet nuget push "$pkg" \
483+
--api-key "$API_KEY" \
484+
--source https://api.nuget.org/v3/index.json \
485+
--skip-duplicate
486+
done
487+
echo "All packages pushed to NuGet.org via Trusted Publishing"
488+
489+
- name: Push to NuGet.org (API key)
490+
if: env.ENABLE_NUGET == 'true' && env.NUGET_USE_OIDC != 'true'
457491
shell: bash
458492
env:
459493
API_KEY: ${{ secrets.NUGET_API_KEY }}
460494
run: |
495+
if [ -z "$API_KEY" ]; then
496+
echo "::error::ENABLE_NUGET is true but NUGET_API_KEY secret is not set. Either set the secret or change NUGET_USE_OIDC to true."
497+
exit 1
498+
fi
461499
shopt -s nullglob
462500
PACKAGES=(artifacts/packages/*.nupkg)
463501
if [ ${#PACKAGES[@]} -eq 0 ]; then
@@ -471,12 +509,12 @@ jobs:
471509
--source https://api.nuget.org/v3/index.json \
472510
--skip-duplicate
473511
done
474-
echo "All packages pushed to NuGet.org"
512+
echo "All packages pushed to NuGet.org via API key"
475513
476-
- name: NuGet push skipped
477-
if: secrets.NUGET_API_KEY == ''
514+
- name: NuGet disabled
515+
if: env.ENABLE_NUGET != 'true'
478516
shell: bash
479-
run: echo "::notice::NUGET_API_KEY secret is not configured. Skipping NuGet push."
517+
run: echo "::notice::NuGet is disabled (ENABLE_NUGET != true). Skipping NuGet push."
480518

481519
- name: Generate SBOM
482520
uses: anchore/sbom-action@57aae528053a48a3f6235f2d9461b05fbcb7366d # v0.23.1
@@ -485,20 +523,36 @@ jobs:
485523
output-file: sbom-spdx.json
486524
format: spdx-json
487525

526+
- name: Collect attestation subjects
527+
id: attest-subjects
528+
shell: bash
529+
run: |
530+
shopt -s nullglob
531+
SUBJECTS=""
532+
if [ "$ENABLE_NUGET" = "true" ]; then
533+
for f in artifacts/packages/*.nupkg; do
534+
SUBJECTS+="$f"$'\n'
535+
done
536+
fi
537+
for f in artifacts/installers/*.zip; do
538+
SUBJECTS+="$f"$'\n'
539+
done
540+
SUBJECTS=$(echo "$SUBJECTS" | sed '/^$/d')
541+
echo "subjects<<EOF" >> "$GITHUB_OUTPUT"
542+
echo "$SUBJECTS" >> "$GITHUB_OUTPUT"
543+
echo "EOF" >> "$GITHUB_OUTPUT"
544+
488545
- name: Attest build provenance
546+
if: steps.attest-subjects.outputs.subjects != ''
489547
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
490548
with:
491-
subject-path: |
492-
artifacts/packages/*.nupkg
493-
artifacts/installers/*.zip
549+
subject-path: ${{ steps.attest-subjects.outputs.subjects }}
494550

495551
- name: Attest SBOM
496-
if: hashFiles('sbom-spdx.json') != ''
552+
if: steps.attest-subjects.outputs.subjects != '' && hashFiles('sbom-spdx.json') != ''
497553
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
498554
with:
499-
subject-path: |
500-
artifacts/packages/*.nupkg
501-
artifacts/installers/*.zip
555+
subject-path: ${{ steps.attest-subjects.outputs.subjects }}
502556
sbom-path: sbom-spdx.json
503557

504558
- name: Create and push tag

0 commit comments

Comments
 (0)