From 96450fd129ef20b95c9d2300660bfad063178d8e Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Mon, 23 Feb 2026 10:51:25 -0800 Subject: [PATCH] feat: More robust release workflow for PyPi --- .github/workflows/_test_release.yml | 3 +- .github/workflows/lint.yml | 13 ++++----- .github/workflows/local.yml | 12 ++++---- .github/workflows/main.yml | 12 ++++---- .github/workflows/release.yml | 44 +++++++++++++++++++++++++++-- .github/workflows/unit.yml | 10 +++---- 6 files changed, 63 insertions(+), 31 deletions(-) diff --git a/.github/workflows/_test_release.yml b/.github/workflows/_test_release.yml index d2ea9087..d983e2bc 100644 --- a/.github/workflows/_test_release.yml +++ b/.github/workflows/_test_release.yml @@ -83,5 +83,4 @@ jobs: repository-url: https://test.pypi.org/legacy/ # This setting ONLY IN CI AND ON TEST PYPI! See https://github.com/pypa/gh-action-pypi-publish#tolerating-release-package-file-duplicates skip-existing: true - # TODO determine whether to enable attestations later on, and how - attestations: false + attestations: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 69becbf1..8ab2b7a6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,16 +14,15 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + + - name: Set up Python + uv + uses: "./.github/actions/uv_setup" with: - python-version: '3.11' # Or any version you prefer + python-version: "3.11" - name: Install dependencies - run: | - python -m pip install --upgrade pip - pipx install uv - make venv + run: uv sync --dev + shell: bash - name: Ruff Linting AstraPy run: | diff --git a/.github/workflows/local.yml b/.github/workflows/local.yml index e5c48583..40f69e20 100644 --- a/.github/workflows/local.yml +++ b/.github/workflows/local.yml @@ -50,16 +50,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + - name: Set up Python + uv + uses: "./.github/actions/uv_setup" with: - python-version: 3.12 + python-version: "3.12" - name: Install dependencies - run: | - python -m pip install --upgrade pip - pipx install uv - make venv + run: uv sync --dev + shell: bash - name: Configure AWS credentials from OIDC uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4b0e32b0..e2658ae9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,16 +34,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + - name: Set up Python + uv + uses: "./.github/actions/uv_setup" with: - python-version: 3.12 + python-version: "3.12" - name: Install dependencies - run: | - python -m pip install --upgrade pip - pipx install uv - make venv + run: uv sync --dev + shell: bash - name: Run pytest run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cccbdd9d..f9e11892 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,7 @@ jobs: outputs: pkg-name: ${{ steps.check-version.outputs.pkg-name }} version: ${{ steps.check-version.outputs.version }} + version-exists: ${{ steps.check-pypi.outputs.version-exists }} steps: - uses: actions/checkout@v4 @@ -61,9 +62,43 @@ jobs: f.write(f"pkg-name={pkg_name}\n") f.write(f"version={version}\n") + - name: Check if version exists on PyPI + id: check-pypi + shell: python + env: + PKG_NAME: ${{ steps.check-version.outputs.pkg-name }} + VERSION: ${{ steps.check-version.outputs.version }} + run: | + import json + import os + import urllib.request + import urllib.error + + pkg_name = os.environ["PKG_NAME"] + version = os.environ["VERSION"] + + try: + url = f"https://pypi.org/pypi/{pkg_name}/{version}/json" + with urllib.request.urlopen(url) as response: + # If we get here, the version exists on PyPI + print(f"Version {version} already exists on PyPI") + version_exists = "true" + except urllib.error.HTTPError as e: + if e.code == 404: + # Version doesn't exist, we can proceed + print(f"Version {version} does not exist on PyPI, proceeding with release") + version_exists = "false" + else: + # Other HTTP error, fail the workflow + raise + + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"version-exists={version_exists}\n") + test-pypi-publish: needs: - build + if: needs.build.outputs.version-exists == 'false' uses: ./.github/workflows/_test_release.yml permissions: write-all @@ -75,6 +110,7 @@ jobs: needs: - build - test-pypi-publish + if: needs.build.outputs.version-exists == 'false' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -131,6 +167,7 @@ jobs: needs: - build - test-pypi-publish + if: needs.build.outputs.version-exists == 'false' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -168,8 +205,11 @@ jobs: - test-pypi-publish - pre-release-checks - pre-release-unit-lowest-python + if: needs.build.outputs.version-exists == 'false' runs-on: ubuntu-latest # This requires an 'environment' with this name on the github repo (and is best practice to restrict permissions) + # NOTE: Once this trusted publishing workflow is fully verified with actual releases, + # revoke any legacy PyPI API tokens/keys for astrapy for enhanced security. environment: pypi permissions: # Needed by trusted publish: https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/ @@ -195,8 +235,7 @@ jobs: packages-dir: dist/ verbose: true print-hash: true - # TODO determine whether to enable attestations later on, and how - attestations: false + attestations: true mark-release: needs: @@ -205,6 +244,7 @@ jobs: - pre-release-checks - pre-release-unit-lowest-python - publish + if: needs.build.outputs.version-exists == 'false' runs-on: ubuntu-latest permissions: # Needed by `ncipollo/release-action` for creating the release diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index d9f67f95..c94bdd92 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -42,16 +42,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + - name: Set up Python + uv + uses: "./.github/actions/uv_setup" with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: | - python -m pip install --upgrade pip - pipx install uv - make venv + run: uv sync --dev + shell: bash - name: Run pytest run: |