diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87f359c..2ac819a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 5ac9b03..cb1ac6e 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -1,19 +1,28 @@ -name: Draft Release +name: Release on: push: branches: [ "master" ] workflow_dispatch: # manual trigger +permissions: + contents: write + id-token: write + jobs: build_test_pack: runs-on: ubuntu-latest + outputs: + publish: ${{ steps.decide.outputs.publish }} + tag: ${{ steps.csproj_ver.outputs.version }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x @@ -25,37 +34,81 @@ jobs: - name: Test run: dotnet test -c Release --no-build --verbosity normal - - # Compare version from csproj with latest release tag. - # If different, create a draft release. - + - name: Get csproj version id: csproj_ver uses: KageKirin/get-csproj-version@v1.0.0 with: file: src/OrderCloud.SDK/OrderCloud.SDK.csproj - + - name: Get latest release tag id: release_ver uses: InsonusK/get-latest-release@v1.0.1 with: - myToken: ${{ github.token }} #required to see drafts + myToken: ${{ github.token }} exclude_types: prerelease view_top: 1 - + - name: Output versions run: | echo "csproj version: ${{ steps.csproj_ver.outputs.version }}" echo "latest release: ${{ steps.release_ver.outputs.tag_name }}" - - - name: Draft release + + # A release created with GITHUB_TOKEN does not start other workflows + # (https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow#triggering-a-workflow-from-a-workflow). + # Call publish.yml via workflow_call instead of relying on the release event. + - name: Decide whether to publish + id: decide + env: + CSPROJ_VER: ${{ steps.csproj_ver.outputs.version }} + RELEASE_VER: ${{ steps.release_ver.outputs.tag_name }} + run: | + if [ "$CSPROJ_VER" != "$RELEASE_VER" ]; then + echo "publish=true" >> "$GITHUB_OUTPUT" + else + echo "publish=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build release notes + id: notes + if: steps.decide.outputs.publish == 'true' env: CSPROJ_VER: ${{ steps.csproj_ver.outputs.version }} RELEASE_VER: ${{ steps.release_ver.outputs.tag_name }} - if: env.CSPROJ_VER != env.RELEASE_VER + run: | + { + echo "body</dev/null 2>&1; then + echo + echo "### Commits since $RELEASE_VER" + git log --oneline "${RELEASE_VER}..HEAD" --no-merges | head -20 || true + fi + echo "EOF" + } >> "$GITHUB_OUTPUT" + + - name: Create and publish release + if: steps.decide.outputs.publish == 'true' + env: + CSPROJ_VER: ${{ steps.csproj_ver.outputs.version }} uses: ncipollo/release-action@v1.10.0 with: tag: ${{ env.CSPROJ_VER }} - body: "- Updates for latest OrderCloud API version" + name: ${{ env.CSPROJ_VER }} + body: ${{ steps.notes.outputs.body }} artifacts: "**/*.nupkg" - draft: true + draft: false + skipIfReleaseExists: true + + # Same pipeline, not a GITHUB_TOKEN-triggered release event. Keeps nuget.org + # Trusted Publishing policy pointed at publish.yml. + publish: + needs: build_test_pack + if: needs.build_test_pack.outputs.publish == 'true' + uses: ./.github/workflows/publish.yml + with: + tag: ${{ needs.build_test_pack.outputs.tag }} + secrets: inherit + permissions: + id-token: write + contents: read diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 80ccdf1..44c7e78 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,26 +1,65 @@ name: Publish on: + # Human-created GitHub releases (UI) still fire this. Releases created by + # GITHUB_TOKEN in the Release workflow do not; that path calls this file via + # workflow_call instead. release: types: [published] - workflow_dispatch: #manual trigger + workflow_dispatch: + inputs: + tag: + description: Release tag to publish (e.g. 0.19.2). Required for workflow_dispatch. + required: true + type: string + workflow_call: + inputs: + tag: + description: Release tag whose nupkg should be pushed to NuGet. + required: true + type: string jobs: publish: runs-on: ubuntu-latest + # No GitHub Environment yet — nuget.org Trusted Publishing policy has Environment blank. + # When adding nuget-prod: set environment here AND on the nuget.org policy. + permissions: + id-token: write + contents: read steps: - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - + + - name: Resolve release tag + id: tag + env: + EVENT_NAME: ${{ github.event_name }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + INPUT_TAG: ${{ inputs.tag }} + run: | + if [ "$EVENT_NAME" = "release" ]; then + echo "name=$RELEASE_TAG" >> "$GITHUB_OUTPUT" + else + echo "name=$INPUT_TAG" >> "$GITHUB_OUTPUT" + fi + - name: Download package - uses: robinraju/release-downloader@v1.4 + uses: robinraju/release-downloader@v1.13 with: - latest: true - filename: "*.nupkg" + tag: ${{ steps.tag.outputs.name }} + fileName: "*.nupkg" token: ${{ github.token }} - + + - name: NuGet login (OIDC → temp API key) + # Pin to commit SHA (v1.2.0). Tags like v1 can be moved; the SHA cannot. + uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 + id: login + with: + user: ${{ secrets.NUGET_USER }} + - name: Publish to NuGet - run: dotnet nuget push "*.nupkg" --api-key ${{secrets.nuget_api_key}} --source https://api.nuget.org/v3/index.json --skip-duplicate + run: dotnet nuget push "*.nupkg" --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate