| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| name: Publish Python 🐍 distribution 📦 to PyPI |
| |
| on: |
| workflow_run: |
| workflows: ["Publish"] # Trigger this workflow after the "publish.yml" workflow completes |
| types: |
| - completed |
| workflow_dispatch: |
| inputs: |
| release_tag: |
| description: 'Release tag (e.g., v0.4.0 or v0.4.0-rc.1)' |
| required: true |
| type: string |
| |
| permissions: |
| contents: read |
| |
| jobs: |
| check-cargo-publish: |
| runs-on: ubuntu-latest |
| # Only run if the triggering workflow succeeded OR if manually triggered |
| if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} |
| steps: |
| - run: echo 'The Publish workflow passed or was manually triggered' |
| |
| validate-release-tag: |
| runs-on: ubuntu-latest |
| needs: [check-cargo-publish] |
| outputs: |
| cargo-version: ${{ steps.validate.outputs.cargo-version }} |
| is-rc: ${{ steps.validate.outputs.is-rc }} |
| steps: |
| - uses: actions/checkout@v6 |
| if: ${{ github.event_name == 'workflow_dispatch' }} |
| |
| - name: Validate release tag format |
| id: validate |
| # Use input for workflow_dispatch, otherwise use `workflow_run.head_branch` |
| # Note, `workflow_run.head_branch` does not contain `refs/tags/` prefix, just the tag name, i.e. `v0.4.0` or `v0.4.0-rc.1` |
| # Valid formats: v<major>.<minor>.<patch> OR v<major>.<minor>.<patch>-rc.<release_candidate> |
| run: | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then |
| RELEASE_TAG="${{ github.event.inputs.release_tag }}" |
| else |
| RELEASE_TAG="${{ github.event.workflow_run.head_branch }}" |
| fi |
| echo "Validating release tag: $RELEASE_TAG" |
| if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then |
| echo "❌ Invalid release tag format: $RELEASE_TAG" |
| echo "Expected format: v<major>.<minor>.<patch> OR v<major>.<minor>.<patch>-rc.<release_candidate>" |
| echo "Examples: v0.4.0, v1.2.3-rc.1" |
| exit 1 |
| fi |
| echo "✅ Release tag format is valid: $RELEASE_TAG" |
| |
| # Strip 'v' prefix for cargo version |
| CARGO_VERSION="${RELEASE_TAG#v}" |
| echo "Cargo version (without v prefix): $CARGO_VERSION" |
| |
| # For manual triggers, validate that the tag matches the version in Cargo.toml |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then |
| # Extract base version (without -rc.X suffix) for comparison with Cargo.toml |
| BASE_VERSION="${CARGO_VERSION%-rc.*}" |
| echo "Base version (for Cargo.toml comparison): $BASE_VERSION" |
| |
| # Read version from Cargo.toml and validate it matches |
| CARGO_TOML_VERSION=$(grep '^version = ' bindings/python/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') |
| echo "Version in bindings/python/Cargo.toml: $CARGO_TOML_VERSION" |
| |
| if [ "$BASE_VERSION" != "$CARGO_TOML_VERSION" ]; then |
| echo "❌ Version mismatch!" |
| echo " Release tag base version: $BASE_VERSION" |
| echo " bindings/python/Cargo.toml version: $CARGO_TOML_VERSION" |
| echo "Please ensure the release tag matches the version in Cargo.toml" |
| exit 1 |
| fi |
| echo "✅ Version matches bindings/python/Cargo.toml" |
| fi |
| |
| # Check if this is a release candidate |
| if [[ "$RELEASE_TAG" =~ -rc\.[0-9]+$ ]]; then |
| IS_RC="true" |
| echo "This is a release candidate" |
| else |
| IS_RC="false" |
| echo "This is a stable release" |
| fi |
| |
| # Set outputs for other jobs to use |
| echo "cargo-version=$CARGO_VERSION" >> $GITHUB_OUTPUT |
| echo "is-rc=$IS_RC" >> $GITHUB_OUTPUT |
| |
| sdist: |
| runs-on: ubuntu-latest |
| needs: [validate-release-tag] |
| steps: |
| - uses: actions/checkout@v6 |
| |
| - name: Install toml-cli |
| if: ${{ needs.validate-release-tag.outputs.is-rc == 'true' }} |
| run: cargo install toml-cli |
| |
| - name: Set cargo version for RC |
| if: ${{ needs.validate-release-tag.outputs.is-rc == 'true' }} |
| working-directory: "bindings/python" |
| run: | |
| echo "Setting cargo version to: ${{ needs.validate-release-tag.outputs.cargo-version }}" |
| toml set Cargo.toml package.version ${{ needs.validate-release-tag.outputs.cargo-version }} > Cargo.toml.tmp |
| # doing this explicitly to avoid issue in Windows where `mv` does not overwrite existing file |
| rm Cargo.toml |
| mv Cargo.toml.tmp Cargo.toml |
| |
| - uses: PyO3/maturin-action@v1 |
| with: |
| working-directory: "bindings/python" |
| command: sdist |
| args: -o dist |
| - name: Upload sdist |
| uses: actions/upload-artifact@v6 |
| with: |
| name: wheels-sdist |
| path: bindings/python/dist |
| |
| wheels: |
| runs-on: "${{ matrix.os }}" |
| needs: [validate-release-tag] |
| strategy: |
| matrix: |
| include: |
| - { os: windows-latest } |
| - { os: macos-latest, target: "universal2-apple-darwin" } |
| - { os: ubuntu-latest, target: "x86_64" } |
| - { |
| os: ubuntu-latest, |
| target: "aarch64", |
| manylinux: "manylinux_2_28", |
| } |
| - { os: ubuntu-latest, target: "armv7l" } |
| steps: |
| - uses: actions/checkout@v6 |
| |
| - name: Install toml-cli |
| if: ${{ needs.validate-release-tag.outputs.is-rc == 'true' }} |
| run: cargo install toml-cli |
| |
| - name: Set cargo version for RC |
| if: ${{ needs.validate-release-tag.outputs.is-rc == 'true' }} |
| working-directory: "bindings/python" |
| run: | |
| echo "Setting cargo version to: ${{ needs.validate-release-tag.outputs.cargo-version }}" |
| toml set Cargo.toml package.version ${{ needs.validate-release-tag.outputs.cargo-version }} > Cargo.toml.tmp |
| # doing this explicitly to avoid issue in Windows where `mv` does not overwrite existing file |
| rm Cargo.toml |
| mv Cargo.toml.tmp Cargo.toml |
| |
| - uses: actions/setup-python@v6 |
| with: |
| python-version: 3.12 |
| - name: Get MSRV |
| id: get-msrv |
| uses: ./.github/actions/get-msrv |
| |
| - name: Setup Rust toolchain |
| uses: ./.github/actions/setup-builder |
| with: |
| rust-version: ${{ steps.get-msrv.outputs.msrv }} |
| - uses: PyO3/maturin-action@v1 |
| with: |
| target: ${{ matrix.target }} |
| manylinux: ${{ matrix.manylinux || 'auto' }} |
| working-directory: "bindings/python" |
| command: build |
| args: --release -o dist |
| - name: Upload wheels |
| uses: actions/upload-artifact@v6 |
| with: |
| name: wheels-${{ matrix.os }}-${{ matrix.target }} |
| path: bindings/python/dist |
| |
| pypi-publish: |
| name: Publish Python 🐍 distribution 📦 to Pypi |
| needs: [sdist, wheels] |
| runs-on: ubuntu-latest |
| |
| environment: |
| name: pypi |
| url: https://pypi.org/p/pyiceberg-core |
| |
| permissions: |
| id-token: write # IMPORTANT: mandatory for trusted publishing |
| |
| steps: |
| - name: Download all the dists |
| uses: actions/download-artifact@v7 |
| with: |
| pattern: wheels-* |
| merge-multiple: true |
| path: bindings/python/dist |
| - name: Publish to PyPI |
| uses: pypa/gh-action-pypi-publish@release/v1 |
| with: |
| skip-existing: true |
| packages-dir: bindings/python/dist |
| verbose: true |