diff --git a/.github/build/manifest_version.py b/.github/build/manifest_version.py deleted file mode 100644 index 54cd265..0000000 --- a/.github/build/manifest_version.py +++ /dev/null @@ -1,53 +0,0 @@ -import sys -import os -import toml -from datetime import datetime - -""" -Github Env Variables: - - GITHUB_REF - - GITHUB_RUN_ID -Devops Env Variables: - - BUILD_SOURCEBRANCH - - BUILD_BUILDNUMBER -""" - -RELEASE_BRANCH = "refs/heads/main" - -BUILD_SYSTEM = "github" -REF_KEY = "GITHUB_REF" -BUILD_ID = "GITHUB_RUN_ID" - -if len(sys.argv) != 3: - print(f"Called with wrong arguments, requires: `python {sys.argv[0]} TOML_PATH VERSION_FILE_PATH") - sys.exit(-1) - -if REF_KEY not in os.environ: - print(f"{REF_KEY} not found in environment variable, adjust script for Azure Devops Pipelines or Github Actions") - sys.exit(-1) - -if BUILD_ID not in os.environ: - print(f"{BUILD_ID} not found in environment variable, adjust script for Azure Devops Pipelines or Github Actions") - sys.exit(-1) - -branch = os.environ[REF_KEY] -build_id = os.environ[BUILD_ID] -build_date = datetime.now().strftime("%Y%m%d") - -if BUILD_SYSTEM == "devops": - build_id = build_id.split(".")[1] -_, cargo_file, version_file = sys.argv - -cargo_conf = toml.load(cargo_file) -if branch != RELEASE_BRANCH: - # if it's a release branch, don't touch the TOML - it's fine - original = cargo_conf["package"]["version"] - snapshot = f"{original}-dev{build_date}{build_id.rjust(3,'0')}" - cargo_conf["package"]["version"] = snapshot - with open(cargo_file, "w") as cargo_file_io: - toml.dump(cargo_conf, cargo_file_io) - -version = cargo_conf["package"]["version"] - -with open(version_file, "w") as version_file_io: - version_file_io.write(version) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..60c9b5b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: CI +on: [push, pull_request] +jobs: + codestyle-check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v6 + - name: Check format + run: | + cargo fmt -- --check + - name: Clippy + run: | + cargo clippy --all-targets -- -D warnings + build: + needs: ['codestyle-check'] + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + target: x64 + manylinux: "2014" + args: -m packages/pyo3/Cargo.toml --release --sdist + artifact_name: dist-linux-x64 + - runner: ubuntu-24.04-arm + target: aarch64 + manylinux: "2014" + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-linux-aarch64 + - runner: ubuntu-latest + target: x64 + manylinux: musllinux_1_2 + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-musllinux-x64 + - runner: ubuntu-24.04-arm + target: aarch64 + manylinux: musllinux_1_2 + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-musllinux-aarch64 + - runner: windows-latest + target: x64 + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-windows-x64 + - runner: windows-11-arm + target: aarch64-pc-windows-msvc + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-windows-aarch64 + - runner: macos-latest + target: universal2-apple-darwin + args: -m packages/pyo3/Cargo.toml --release + artifact_name: dist-macos-universal2 + steps: + - uses: actions/checkout@v6 + - uses: astral-sh/setup-uv@v6 + - name: Rust Unittests + run: | + cargo test --manifest-path packages/network_partitions/Cargo.toml + + - uses: PyO3/maturin-action@v1 + if: ${{ !startsWith(matrix.runner, 'windows') }} + name: Maturin Build + with: + command: build + target: ${{ matrix.target }} + args: ${{ matrix.args }} + manylinux: ${{ matrix.manylinux || 'auto' }} + + - uses: PyO3/maturin-action@v1 + if: ${{ startsWith(matrix.runner, 'windows') }} + name: Maturin Build (Windows) + with: + command: build + target: ${{ matrix.target }} + args: ${{ matrix.args }} + + - name: Python Unittests + if: ${{ !matrix.manylinux || matrix.manylinux != 'musllinux_1_2' }} + run: | + cd packages/pyo3 + uv venv + uv pip install ../../target/wheels/*.whl + uv run python -m unittest + shell: bash + + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact_name }} + path: | + target/wheels/*.whl + target/wheels/*.tar.gz + diff --git a/.github/workflows/build.yml b/.github/workflows/publish.yml similarity index 52% rename from .github/workflows/build.yml rename to .github/workflows/publish.yml index 22afa0f..d0f88b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/publish.yml @@ -1,34 +1,38 @@ -name: graspologic-native CI -on: [push, pull_request] +name: Publish to PyPI + +on: + push: + tags: + - "v*" + +permissions: + contents: read + jobs: - codestyle-check: - runs-on: "ubuntu-latest" + verify-tag: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Check format - run: | - cargo fmt -- --check - - name: Clippy + with: + fetch-depth: 0 + - name: Verify tag is on main run: | - cargo clippy --all-targets -- -D warnings - version: - runs-on: "ubuntu-latest" - steps: - - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v6 - - name: Materialize build number + if ! git branch -r --contains "${{ github.sha }}" | grep -q 'origin/main'; then + echo "::error::Tag ${{ github.ref_name }} does not point to a commit on main." + exit 1 + fi + - name: Verify tag matches Cargo.toml version run: | - uv run --with toml python .github/build/manifest_version.py packages/pyo3/Cargo.toml version.txt - - uses: actions/upload-artifact@v4 - with: - name: cargo-toml - path: packages/pyo3/Cargo.toml - - uses: actions/upload-artifact@v4 - with: - name: version-txt - path: version.txt + CARGO_VERSION=$(grep '^version' packages/pyo3/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then + echo "::error::Tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + build: - needs: ['codestyle-check', 'version'] + needs: verify-tag runs-on: ${{ matrix.runner }} strategy: fail-fast: false @@ -69,14 +73,7 @@ jobs: steps: - uses: actions/checkout@v6 - uses: astral-sh/setup-uv@v6 - - uses: actions/download-artifact@v4 - with: - name: cargo-toml - path: materialized - - name: Materialized Cargo Placement - run: | - mv materialized/Cargo.toml packages/pyo3/Cargo.toml - shell: bash + - name: Rust Unittests run: | cargo test --manifest-path packages/network_partitions/Cargo.toml @@ -113,75 +110,39 @@ jobs: path: | target/wheels/*.whl target/wheels/*.tar.gz + publish: runs-on: ubuntu-latest - needs: 'build' - if: github.ref=='refs/heads/main' || github.ref=='refs/heads/dev' + needs: build + environment: pypi-publish permissions: + id-token: write contents: write steps: - uses: actions/checkout@v6 - - uses: astral-sh/setup-uv@v6 - - uses: actions/download-artifact@v4 - with: - name: dist-linux-x64 - path: dist/ - - uses: actions/download-artifact@v4 - with: - name: dist-linux-aarch64 - path: dist/ - - uses: actions/download-artifact@v4 - with: - name: dist-musllinux-x64 - path: dist/ - - uses: actions/download-artifact@v4 - with: - name: dist-musllinux-aarch64 - path: dist/ - - uses: actions/download-artifact@v4 - with: - name: dist-windows-x64 - path: dist/ - uses: actions/download-artifact@v4 with: - name: dist-windows-aarch64 + pattern: dist-* + merge-multiple: true path: dist/ - - uses: actions/download-artifact@v4 - with: - name: dist-macos-universal2 - path: dist/ - - name: Generate SHA256 files for each wheel + + - name: Generate checksums run: | - sha256sum dist/*.whl > checksums.txt - sha256sum dist/*.tar.gz >> checksums.txt + sha256sum dist/*.whl dist/*.tar.gz > checksums.txt cat checksums.txt - - uses: actions/download-artifact@v4 + + - name: Publish to PyPI (trusted publishing) + uses: pypa/gh-action-pypi-publish@release/v1 with: - name: version-txt - path: version/ - - name: Release Tag from Version - run: | - GRASPOLOGIC_VERSION=$(cat version/version.txt) - echo "GRASPOLOGIC_VERSION=$GRASPOLOGIC_VERSION" >> $GITHUB_ENV - echo $GRASPOLOGIC_VERSION - - name: Publish with twine - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - uv run --with twine twine upload dist/* - - name: Create Github Release + packages-dir: dist/ + skip-existing: true + + - name: Create GitHub Release env: GH_TOKEN: ${{ github.token }} run: | - PRERELEASE_FLAG="" - if [ "${{ github.ref }}" = "refs/heads/dev" ]; then - PRERELEASE_FLAG="--prerelease" - fi - gh release create "$GRASPOLOGIC_VERSION" \ - --title "graspologic-native-${GRASPOLOGIC_VERSION}" \ + gh release create "${{ github.ref_name }}" \ + --title "graspologic-native ${{ github.ref_name }}" \ --notes-file checksums.txt \ - $PRERELEASE_FLAG \ dist/*.whl \ dist/*.tar.gz -