diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b9187b7..d3d6884f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,56 +14,87 @@ jobs: release: needs: run_tests - if: github.ref_name == 'master' runs-on: ubuntu-latest + if: github.ref_name == 'master' concurrency: - group: ${{ github.workflow }}-release + group: ${{ github.workflow }}-release-${{ github.ref_name }} cancel-in-progress: false permissions: contents: write steps: - - name: Checkout + - name: Setup | Checkout Repository on Release Branch uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.ref_name }} - - name: Reset to triggered commit - run: git reset --hard ${{ github.sha }} + - name: Setup | Force release branch to be at workflow sha + run: | + git reset --hard ${{ github.sha }} - - name: Python Semantic Release + - name: Action | Semantic Version Release id: release + # Adjust tag with desired version if applicable. uses: python-semantic-release/python-semantic-release@39dd2052f2ce8282a5d932c31d58a2ca06d2550e # v10.6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} - git_committer_name: "github-actions[bot]" - git_committer_email: "github-actions[bot]@users.noreply.github.com" + git_committer_name: "github-actions" + git_committer_email: "actions@users.noreply.github.com" changelog: "false" + # Commit, tag, push and build, but don't create the GitHub release. + # We create it ourselves in the next step so that the distributions + # are attached before the release is published. See that step for why. + vcs_release: "false" - - name: Upload dist artifacts + # This repo has immutable releases enabled, which freezes a release's + # assets the moment it is published, so assets cannot be attached + # afterwards. `gh release create` handles this by creating the release as + # a draft, uploading the assets, and only then publishing it: + # https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/immutable-releases + - name: Publish | Create GitHub Release with Assets if: steps.release.outputs.released == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_NOTES: ${{ steps.release.outputs.release_notes }} + TAG: ${{ steps.release.outputs.tag }} + run: | + printf '%s' "$RELEASE_NOTES" > "$RUNNER_TEMP/release_notes.md" + gh release create "$TAG" \ + --verify-tag \ + --title "$TAG" \ + --notes-file "$RUNNER_TEMP/release_notes.md" \ + dist/* + + - name: Upload | Distribution Artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: steps.release.outputs.released == 'true' with: name: dist path: dist/ + if-no-files-found: error outputs: - released: ${{ steps.release.outputs.released }} + released: ${{ steps.release.outputs.released || 'false' }} version: ${{ steps.release.outputs.version }} publish_to_pypi: - needs: release - if: needs.release.outputs.released == 'true' + # 1. Separate out the publish step from the github release step to run each step at + # the least amount of token privilege + # 2. Also, publishing can fail, and its better to have a separate job if you need to retry + # and it won't require reversing the release. runs-on: ubuntu-latest + needs: release + if: github.ref_name == 'master' && needs.release.outputs.released == 'true' permissions: contents: read id-token: write steps: - - name: Download dist artifacts + - name: Setup | Download Build Artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + id: artifact-download with: name: dist path: dist/