Repository: python-telegram-bot/python-telegram-bot

Issue:
Improve PyPI Automation
With v21.4 we've seen a first successfull application of the new automation introduced in #4364. However, while making the first release I've seen a few steps that can use improvement:

* the tag still needs to be created manually. Moreover, it's currently created *before* the pypi release is there. If the pypi release fails and changes have to be made, the tag needs to be moved, which is [not ideal](https://github.com/astral-sh/ruff/issues/4468)
* separation of test-pypi and pypi release are not working as I'd hoped: The tag-push currently also triggers the test-pypi release and since the compute-sigantures steps "needs" it, I have to manually approve/reject it before the signatures are created and the gh release is created

Even if it means some code duplication (which I'd originally tried to avoid by having everything in one workflow), I therefore will split the test-pypi and pypi flows in two separate workflows and set different triggers:

* manual for test-pypi
* for pypi either manual or maybe based on regex commit-message



Discussion:

Patch:
diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml
index bcd1794c468..8ebfd48887e 100644
--- a/.github/workflows/release_pypi.yml
+++ b/.github/workflows/release_pypi.yml
@@ -1,17 +1,15 @@
 name: Publish to PyPI
 
 on:
-  # Run on any tag
-  push:
-    tags:
-      - '**'
-  # manually trigger the workflow - for testing only
+  # manually trigger the workflow
   workflow_dispatch:
 
 jobs:
   build:
     name: Build Distribution
     runs-on: ubuntu-latest
+    outputs:
+      TAG: ${{ steps.get_tag.outputs.TAG }}
 
     steps:
       - uses: actions/checkout@v4
@@ -29,11 +27,15 @@ jobs:
         with:
           name: python-package-distributions
           path: dist/
+      - name: Get Tag Name
+        id: get_tag
+        run: |
+          pip install .
+          TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
+          echo "TAG=$TAG" >> $GITHUB_OUTPUT
 
   publish-to-pypi:
     name: Publish to PyPI
-    # only publish to PyPI on tag pushes
-    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
     needs:
       - build
     runs-on: ubuntu-latest
@@ -52,42 +54,11 @@ jobs:
       - name: Publish to PyPI
         uses: pypa/gh-action-pypi-publish@release/v1
 
-  publish-to-test-pypi:
-    name: Publish to Test PyPI
-    needs:
-      - build
-    runs-on: ubuntu-latest
-    environment:
-      name: release_test_pypi
-      url: https://test.pypi.org/p/python-telegram-bot
-    permissions:
-      id-token: write  # IMPORTANT: mandatory for trusted publishing
-
-    steps:
-      - name: Download all the dists
-        uses: actions/download-artifact@v4
-        with:
-          name: python-package-distributions
-          path: dist/
-      - name: Publish to Test PyPI
-        uses: pypa/gh-action-pypi-publish@release/v1
-        with:
-          repository-url: https://test.pypi.org/legacy/
-
   compute-signatures:
     name: Compute SHA1 Sums and Sign with Sigstore
     runs-on: ubuntu-latest
     needs:
       - publish-to-pypi
-      - publish-to-test-pypi
-    # run if either of the publishing jobs ran successfully
-    # see also:
-    # https://github.com/actions/runner/issues/491#issuecomment-850884422
-    if: |
-      always() && (
-        (needs.publish-to-pypi.result == 'success') ||
-        (needs.publish-to-test-pypi.result == 'success')
-      )
 
     permissions:
       id-token: write  # IMPORTANT: mandatory for sigstore
@@ -106,7 +77,7 @@ jobs:
               sha1sum $file > $file.sha1
           done
       - name: Sign the dists with Sigstore
-        uses: sigstore/gh-action-sigstore-python@v2.1.1
+        uses: sigstore/gh-action-sigstore-python@v3.0.0
         with:
           inputs: >-
             ./dist/*.tar.gz
@@ -120,56 +91,9 @@ jobs:
   github-release:
     name: Upload to GitHub Release
     needs:
-      - publish-to-pypi
+      - build
       - compute-signatures
-    if: |
-      always() && (
-        (needs.publish-to-pypi.result == 'success') &&
-        (needs.compute-signatures.result == 'success')
-      )
-
-    runs-on: ubuntu-latest
-
-    permissions:
-      contents: write  # IMPORTANT: mandatory for making GitHub Releases
-
-    steps:
-      - name: Download all the dists
-        uses: actions/download-artifact@v4
-        with:
-          name: python-package-distributions-and-signatures
-          path: dist/
-      - name: Create GitHub Release
-        env:
-          GITHUB_TOKEN: ${{ github.token }}
-        # Create a GitHub Release for this tag. The description can be changed later, as for now
-        # we don't define it through this workflow.
-        run: >-
-          gh release create
-          '${{ github.ref_name }}'
-          --repo '${{ github.repository }}'
-          --generate-notes
-      - name: Upload artifact signatures to GitHub Release
-        env:
-          GITHUB_TOKEN: ${{ github.token }}
-        # Upload to GitHub Release using the `gh` CLI.
-        # `dist/` contains the built packages, and the
-        # sigstore-produced signatures and certificates.
-        run: >-
-          gh release upload
-          '${{ github.ref_name }}' dist/**
-          --repo '${{ github.repository }}'
 
-  github-test-release:
-    name: Upload to GitHub Release Draft
-    needs:
-      - publish-to-test-pypi
-      - compute-signatures
-    if: |
-      always() && (
-        (needs.publish-to-test-pypi.result == 'success') &&
-        (needs.compute-signatures.result == 'success')
-      )
     runs-on: ubuntu-latest
 
     permissions:
@@ -184,21 +108,22 @@ jobs:
       - name: Create GitHub Release
         env:
           GITHUB_TOKEN: ${{ github.token }}
-        # Create a GitHub Release *draft*. The description can be changed later, as for now
+          TAG: ${{ needs.build.outputs.TAG }}
+        # Create a tag and a GitHub Release. The description can be changed later, as for now
         # we don't define it through this workflow.
         run: >-
           gh release create
-          '${{ github.ref_name }}'
+          '${{ env.TAG }}'
           --repo '${{ github.repository }}'
           --generate-notes
-          --draft
       - name: Upload artifact signatures to GitHub Release
         env:
           GITHUB_TOKEN: ${{ github.token }}
+          TAG: ${{ needs.build.outputs.TAG }}
         # Upload to GitHub Release using the `gh` CLI.
         # `dist/` contains the built packages, and the
         # sigstore-produced signatures and certificates.
         run: >-
           gh release upload
-          '${{ github.ref_name }}' dist/**
+          '${{ env.TAG }}' dist/**
           --repo '${{ github.repository }}'


Test patch:
diff --git a/.github/workflows/release_test_pypi.yml b/.github/workflows/release_test_pypi.yml
new file mode 100644
index 00000000000..6009a98d7e0
--- /dev/null
+++ b/.github/workflows/release_test_pypi.yml
@@ -0,0 +1,132 @@
+name: Publish to Test PyPI
+
+on:
+  # manually trigger the workflow
+  workflow_dispatch:
+
+jobs:
+  build:
+    name: Build Distribution
+    runs-on: ubuntu-latest
+    outputs:
+      TAG: ${{ steps.get_tag.outputs.TAG }}
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+      - name: Install pypa/build
+        run: >-
+          python3 -m pip install build --user
+      - name: Build a binary wheel and a source tarball
+        run: python3 -m build
+      - name: Store the distribution packages
+        uses: actions/upload-artifact@v4
+        with:
+          name: python-package-distributions
+          path: dist/
+      - name: Get Tag Name
+        id: get_tag
+        run: |
+          pip install .
+          TAG=$(python -c "from telegram import __version__; print(f'v{__version__}')")
+          echo "TAG=$TAG" >> $GITHUB_OUTPUT
+
+  publish-to-test-pypi:
+    name: Publish to Test PyPI
+    needs:
+      - build
+    runs-on: ubuntu-latest
+    environment:
+      name: release_test_pypi
+      url: https://test.pypi.org/p/python-telegram-bot
+    permissions:
+      id-token: write  # IMPORTANT: mandatory for trusted publishing
+
+    steps:
+      - name: Download all the dists
+        uses: actions/download-artifact@v4
+        with:
+          name: python-package-distributions
+          path: dist/
+      - name: Publish to Test PyPI
+        uses: pypa/gh-action-pypi-publish@release/v1
+        with:
+          repository-url: https://test.pypi.org/legacy/
+
+  compute-signatures:
+    name: Compute SHA1 Sums and Sign with Sigstore
+    runs-on: ubuntu-latest
+    needs:
+      - publish-to-test-pypi
+
+    permissions:
+      id-token: write  # IMPORTANT: mandatory for sigstore
+
+    steps:
+      - name: Download all the dists
+        uses: actions/download-artifact@v4
+        with:
+          name: python-package-distributions
+          path: dist/
+      - name: Compute SHA1 Sums
+        run: |
+          # Compute SHA1 sum of the distribution packages and save it to a file with the same name,
+          # but with .sha1 extension
+          for file in dist/*; do
+              sha1sum $file > $file.sha1
+          done
+      - name: Sign the dists with Sigstore
+        uses: sigstore/gh-action-sigstore-python@v3.0.0
+        with:
+          inputs: >-
+            ./dist/*.tar.gz
+            ./dist/*.whl
+      - name: Store the distribution packages and signatures
+        uses: actions/upload-artifact@v4
+        with:
+          name: python-package-distributions-and-signatures
+          path: dist/
+
+  github-test-release:
+    name: Upload to GitHub Release Draft
+    needs:
+      - build
+      - compute-signatures
+
+    runs-on: ubuntu-latest
+
+    permissions:
+      contents: write  # IMPORTANT: mandatory for making GitHub Releases
+
+    steps:
+      - name: Download all the dists
+        uses: actions/download-artifact@v4
+        with:
+          name: python-package-distributions-and-signatures
+          path: dist/
+      - name: Create GitHub Release
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+          TAG: ${{ needs.build.outputs.TAG }}
+        # Create a GitHub Release *draft*. The description can be changed later, as for now
+        # we don't define it through this workflow.
+        run: >-
+          gh release create
+          '${{ env.TAG }}'
+          --repo '${{ github.repository }}'
+          --generate-notes
+          --draft
+      - name: Upload artifact signatures to GitHub Release
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+          TAG: ${{ needs.build.outputs.TAG }}
+        # Upload to GitHub Release using the `gh` CLI.
+        # `dist/` contains the built packages, and the
+        # sigstore-produced signatures and certificates.
+        run: >-
+          gh release upload
+          '${{ env.TAG }}' dist/**
+          --repo '${{ github.repository }}'