diff --git a/.github/workflows/release-and-publish.yml b/.github/workflows/release-and-publish.yml index 5c226d722..1e4ce898b 100644 --- a/.github/workflows/release-and-publish.yml +++ b/.github/workflows/release-and-publish.yml @@ -19,6 +19,12 @@ on: permissions: contents: read +# Temporary compatibility pin for GitPython 3.1.60. Upstream PR #1477 removes +# python-semantic-release's use of Actor.name_email_regex. Return to the pinned +# published actions after upstream ships that fix in a release. +env: + SEMANTIC_RELEASE_SOURCE: "python-semantic-release @ git+https://github.com/python-semantic-release/python-semantic-release.git@4ad93f1f2a70e092612e9b1709c01ebbb0d35434" + concurrency: group: release cancel-in-progress: false @@ -46,14 +52,26 @@ jobs: echo "skip=true" >> "$GITHUB_OUTPUT" fi + - name: Set up Python for Semantic Release + if: steps.check_skip.outputs.skip != 'true' + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + - name: Install compatible Semantic Release + if: steps.check_skip.outputs.skip != 'true' + run: python -m pip install --disable-pip-version-check "$SEMANTIC_RELEASE_SOURCE" + - name: Python Semantic Release if: steps.check_skip.outputs.skip != 'true' id: release - uses: python-semantic-release/python-semantic-release@39dd2052f2ce8282a5d932c31d58a2ca06d2550e # v10.6.1 - with: - github_token: ${{ secrets.ADMIN_TOKEN }} - git_committer_name: "OpenAdapt Bot" - git_committer_email: "bot@openadapt.ai" + env: + GH_TOKEN: ${{ secrets.ADMIN_TOKEN }} + run: | + git config user.name "OpenAdapt Bot" + git config user.email "bot@openadapt.ai" + export GIT_COMMIT_AUTHOR="OpenAdapt Bot " + semantic-release -v version build-and-attest: needs: release @@ -145,6 +163,14 @@ jobs: ref: main fetch-depth: 0 + - name: Set up Python for Semantic Release + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + - name: Install compatible Semantic Release + run: python -m pip install --disable-pip-version-check "$SEMANTIC_RELEASE_SOURCE" + - name: Download attested release artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -152,10 +178,10 @@ jobs: path: dist/ - name: Attach artifacts to GitHub Release - uses: python-semantic-release/publish-action@5a5718ce47b892ef699f2972dae122297771d641 # v10.6.1 - with: - github_token: ${{ github.token }} - tag: ${{ needs.release.outputs.tag }} + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ needs.release.outputs.tag }} + run: semantic-release -v publish --tag "$RELEASE_TAG" reconcile-platform-manifest: needs: [release, publish-pypi] diff --git a/tests/test_release_lock.py b/tests/test_release_lock.py index ba8e4fe71..1c081a6a8 100644 --- a/tests/test_release_lock.py +++ b/tests/test_release_lock.py @@ -111,6 +111,46 @@ def test_release_workflow_pins_actions_and_separates_permissions(): assert jobs["report-release-failure"]["permissions"] == {"issues": "write"} +def test_release_workflow_uses_the_reviewed_gitpython_compatibility_fix(): + workflow_path = ROOT / ".github/workflows/release-and-publish.yml" + document = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) + workflow = workflow_path.read_text(encoding="utf-8") + + source = document["env"]["SEMANTIC_RELEASE_SOURCE"] + assert source.endswith("@4ad93f1f2a70e092612e9b1709c01ebbb0d35434") + assert "Upstream PR #1477" in workflow + + jobs = document["jobs"] + release_steps = jobs["release"]["steps"] + publish_steps = jobs["publish-github"]["steps"] + release_install = next( + step + for step in release_steps + if step["name"] == "Install compatible Semantic Release" + ) + publish_install = next( + step + for step in publish_steps + if step["name"] == "Install compatible Semantic Release" + ) + assert release_install["run"].endswith('"$SEMANTIC_RELEASE_SOURCE"') + assert publish_install["run"] == release_install["run"] + + release = next( + step for step in release_steps if step["name"] == "Python Semantic Release" + ) + publish = next( + step + for step in publish_steps + if step["name"] == "Attach artifacts to GitHub Release" + ) + assert "semantic-release -v version" in release["run"] + assert release["env"]["GH_TOKEN"] == "${{ secrets.ADMIN_TOKEN }}" + assert publish["run"] == 'semantic-release -v publish --tag "$RELEASE_TAG"' + assert publish["env"]["GH_TOKEN"] == "${{ github.token }}" + assert publish["env"]["RELEASE_TAG"] == "${{ needs.release.outputs.tag }}" + + def test_release_workflow_publishes_the_attested_bytes_to_both_destinations(): workflow_path = ROOT / ".github/workflows/release-and-publish.yml" document = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) @@ -135,11 +175,33 @@ def test_release_workflow_publishes_the_attested_bytes_to_both_destinations(): pypi_steps = jobs["publish-pypi"]["steps"] github_steps = jobs["publish-github"]["steps"] - assert pypi_steps[0]["with"]["name"] == transfer["with"]["name"] - assert github_steps[1]["with"]["name"] == transfer["with"]["name"] - assert pypi_steps[1]["with"]["attestations"] is False - assert github_steps[2]["with"]["tag"] == "${{ needs.release.outputs.tag }}" + pypi_download = next( + step + for step in pypi_steps + if step["name"] == "Download attested release artifacts" + ) + github_download = next( + step + for step in github_steps + if step["name"] == "Download attested release artifacts" + ) + pypi_publish = next( + step for step in pypi_steps if step["name"] == "Publish to PyPI" + ) + github_publish = next( + step + for step in github_steps + if step["name"] == "Attach artifacts to GitHub Release" + ) + assert pypi_download["with"]["name"] == transfer["with"]["name"] + assert github_download["with"]["name"] == transfer["with"]["name"] + assert pypi_publish["with"]["attestations"] is False + assert github_publish["env"]["RELEASE_TAG"] == ( + "${{ needs.release.outputs.tag }}" + ) - checkout = github_steps[0] + checkout = next( + step for step in github_steps if step["name"] == "Checkout release branch" + ) assert checkout["name"] == "Checkout release branch" assert checkout["with"] == {"ref": "main", "fetch-depth": 0}