diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..09aa859 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,121 @@ +name: cd + +on: + push: + branches: + - main + tags: + - '**' + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: build on ${{ matrix.os }} (${{ matrix.target }}${{ matrix.os == 'linux' && format(' - {0}', matrix.manylinux == 'auto' && 'manylinux' || matrix.manylinux) || '' }}) + # only run on push to tags, main branch, or explicit full build + if: github.ref_type == 'tag' || github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Full Build') + strategy: + fail-fast: false + matrix: + os: [linux, macos, windows] + target: [x86_64, aarch64] + manylinux: ['2_28'] + include: + # manylinux for various platforms + #- { os: linux, manylinux: '2_28', target: i686 } + - { os: linux, manylinux: '2_28', target: armv7 } + - { os: linux, manylinux: '2_28', target: ppc64le } + #- { os: linux, manylinux: '2_28', target: s390x } + # musl + - { os: linux, manylinux: musllinux_1_2, target: x86_64 } + - { os: linux, manylinux: musllinux_1_2, target: aarch64 } + - { os: linux, manylinux: musllinux_1_2, target: armv7 } + # windows + - { os: windows, target: i686, python-architecture: x86 } + runs-on: ${{ (matrix.os == 'linux' && 'ubuntu') || matrix.os }}-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + architecture: ${{ matrix.python-architecture || 'x64' }} + - run: pip install twine + - uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + manylinux: ${{ matrix.manylinux }} + args: --release --out dist --interpreter '3.11 3.12 3.13' + rust-toolchain: stable + docker-options: -e CI + before-script-linux: | + # If we're running on rhel centos, install needed packages. + if command -v yum &> /dev/null; then + yum update -y && yum install -y perl-core + fi + - run: ${{ (matrix.os == 'windows' && 'dir') || 'ls -lh' }} dist/ + - run: twine check --strict dist/* + - uses: actions/upload-artifact@v4 + with: + name: pypi-wheels-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux }} + path: dist + + inspect: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pypi-wheels-* + merge-multiple: true + path: dist/ + - run: ls -lh dist/ + # TODO: some more checks? `twine` is already run above + + # If git tag is a version, verify that it matches the package metadata version (or fail job and skip `publish`) + # If git tag is not a version, set output `version` to "" (also skipping `publish`) + version: + if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v') + needs: build + outputs: + version: ${{ steps.version.outputs.version }} + is_prerelease: ${{ steps.version.outputs.is_prerelease }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + - uses: actions/download-artifact@v4 + with: + name: pypi-wheels-linux-x86_64-2_28 + path: dist/ + - name: Install zarrs-python + run: pip install packaging dist/*manylinux_2_28_x86_64.whl + - name: Get zarrs-python version and tag + id: version + run: python .github/workflows/version-cmp.py + + publish: + if: needs.version.outputs.version != '' + runs-on: ubuntu-latest + needs: [inspect, version] + environment: pypi + permissions: + id-token: write # to authenticate as Trusted Publisher to pypi.org + steps: + - uses: actions/download-artifact@v4 + with: + pattern: pypi-wheels-* + merge-multiple: true + path: dist/ + - name: "Publishing version ${{ needs.version.outputs.version }}" + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/ + repository-url: https://test.pypi.org/legacy/ + - uses: ncipollo/release-action@v1 + with: + name: ${{ needs.version.outputs.version }} + prerelease: ${{ needs.version.outputs.is_prerelease }} diff --git a/.github/workflows/CI.yml b/.github/workflows/ci.yml similarity index 96% rename from .github/workflows/CI.yml rename to .github/workflows/ci.yml index bcaad44..3d8b07f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: zarrs-python +name: ci on: push: @@ -16,7 +16,7 @@ env: jobs: build_and_test: - name: zarrs-python + name: build and test strategy: fail-fast: false matrix: @@ -30,6 +30,7 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust_toolchain }} + components: rustfmt - name: Install rust-cache uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/version-cmp.py b/.github/workflows/version-cmp.py new file mode 100644 index 0000000..d391a23 --- /dev/null +++ b/.github/workflows/version-cmp.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# Can’t be an isolated script since we want to access zarrs’ metadata + +import importlib.metadata as im +import os +import sys +from pathlib import Path + +from packaging.version import InvalidVersion, Version + + +def set_outputs(version: Version | str) -> None: + is_prerelease = version.is_prerelease if isinstance(version, Version) else False + is_prerelease_json = "true" if is_prerelease else "false" + with Path(os.environ["GITHUB_OUTPUT"]).open("a") as f: + print(f"version={version}", file=f) + print(f"is_prerelease={is_prerelease_json}", file=f) + + +version_tag_str = os.environ["GITHUB_REF_NAME"] +assert version_tag_str.startswith("v"), "should be enforced in `if:` condition" +try: + version_tag = Version(version_tag_str[1:]) +except InvalidVersion: + set_outputs("") + sys.exit(0) + +if version_tag_str[1:] != str(version_tag): + sys.exit(f"Tag version not normalized: {version_tag_str} should be v{version_tag}") + +if version_tag != (version_meta := Version(im.version("zarrs"))): + sys.exit(f"Version mismatch: {version_tag} (tag) != {version_meta} (metadata)") + +set_outputs(version_meta) diff --git a/Cargo.toml b/Cargo.toml index ed9615c..517874f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,14 @@ name = "zarrs-python" version = "0.1.0" edition = "2021" +publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "zarrs_python" crate-type = ["cdylib", "rlib"] [dependencies] -pyo3 = "0.22.6" +pyo3 = { version = "0.22.6", features = ["abi3-py311"] } zarrs = "0.18.0-beta.0" rayon_iter_concurrent_limit = "0.2.0" rayon = "1.10.0"