Bundle data files in pip package and fix hardcoded paths #82
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: build-and-publish | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled, unlabeled] | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| force_build: | |
| description: "Force build jobs (manual/PR testing)" | |
| required: false | |
| type: boolean | |
| publish_s2and: | |
| description: "Publish s2and to PyPI (manual run)" | |
| required: false | |
| type: boolean | |
| publish_rust: | |
| description: "Publish s2and-rust to PyPI (manual run)" | |
| required: false | |
| type: boolean | |
| jobs: | |
| detect-versions: | |
| name: detect version changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| s2and_changed: ${{ steps.detect.outputs.s2and_changed }} | |
| rust_changed: ${{ steps.detect.outputs.rust_changed }} | |
| publish_any: ${{ steps.detect.outputs.publish_any }} | |
| publish_s2and: ${{ steps.detect.outputs.publish_s2and }} | |
| publish_rust: ${{ steps.detect.outputs.publish_rust }} | |
| force_build: ${{ steps.detect.outputs.force_build }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect version changes | |
| id: detect | |
| env: | |
| BEFORE_SHA: ${{ github.event.before }} | |
| run: | | |
| python - <<'PY' | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| import tomllib | |
| event_name = os.environ.get("GITHUB_EVENT_NAME") or "" | |
| event_path = os.environ.get("GITHUB_EVENT_PATH") or "" | |
| event = None | |
| if event_path and os.path.exists(event_path): | |
| with open(event_path, "r", encoding="utf-8") as f: | |
| event = json.load(f) | |
| force_build = False | |
| publish_s2and = False | |
| publish_rust = False | |
| if event_name == "workflow_dispatch": | |
| inputs = (event or {}).get("inputs", {}) | |
| force_build = str(inputs.get("force_build", "")).lower() in {"1", "true", "yes", "on"} | |
| publish_s2and = str(inputs.get("publish_s2and", "")).lower() in {"1", "true", "yes", "on"} | |
| publish_rust = str(inputs.get("publish_rust", "")).lower() in {"1", "true", "yes", "on"} | |
| elif event_name == "pull_request": | |
| labels = (event or {}).get("pull_request", {}).get("labels", []) | |
| force_build = any((label or {}).get("name", "").lower() == "force-build" for label in labels) | |
| before = os.environ.get("BEFORE_SHA") or "" | |
| if event_name == "pull_request" and event: | |
| before = (event.get("pull_request", {}).get("base", {}).get("sha", "")) or "" | |
| if before.startswith("0000000"): | |
| before = "" | |
| def read_toml_at(path, rev=None): | |
| if rev: | |
| try: | |
| data = subprocess.check_output(["git", "show", f"{rev}:{path}"], text=True) | |
| except subprocess.CalledProcessError: | |
| return None | |
| return tomllib.loads(data) | |
| with open(path, "rb") as f: | |
| return tomllib.load(f) | |
| def read_version_file(rev=None): | |
| version_path = "VERSION" | |
| if rev: | |
| try: | |
| data = subprocess.check_output(["git", "show", f"{rev}:{version_path}"], text=True) | |
| except subprocess.CalledProcessError: | |
| return None | |
| return data.strip() | |
| if not os.path.exists(version_path): | |
| return None | |
| with open(version_path, "r", encoding="utf-8") as f: | |
| return f.read().strip() | |
| def get_project_version(toml_obj, path, rev=None): | |
| if toml_obj is None: | |
| return None | |
| try: | |
| return toml_obj["project"]["version"] | |
| except KeyError: | |
| # Allow dynamic version from VERSION for the root pyproject.toml. | |
| if path == "pyproject.toml": | |
| version = read_version_file(rev) | |
| if version: | |
| return version | |
| raise SystemExit(f"Missing [project].version in {path}") | |
| def get_cargo_version(toml_obj, path): | |
| if toml_obj is None: | |
| return None | |
| try: | |
| return toml_obj["package"]["version"] | |
| except KeyError: | |
| raise SystemExit(f"Missing [package].version in {path}") | |
| cur_py = read_toml_at("pyproject.toml") | |
| cur_rust_py = read_toml_at("s2and_rust/pyproject.toml") | |
| cur_rust_cargo = read_toml_at("s2and_rust/Cargo.toml") | |
| cur_py_ver = get_project_version(cur_py, "pyproject.toml") | |
| cur_rust_py_ver = get_project_version(cur_rust_py, "s2and_rust/pyproject.toml") | |
| cur_rust_cargo_ver = get_cargo_version(cur_rust_cargo, "s2and_rust/Cargo.toml") | |
| if cur_rust_py_ver != cur_rust_cargo_ver: | |
| raise SystemExit( | |
| f"Rust version mismatch: pyproject={cur_rust_py_ver} cargo={cur_rust_cargo_ver}" | |
| ) | |
| before_py_ver = ( | |
| get_project_version(read_toml_at("pyproject.toml", before), "pyproject.toml", before) | |
| if before | |
| else None | |
| ) | |
| before_rust_py_ver = ( | |
| get_project_version(read_toml_at("s2and_rust/pyproject.toml", before), "s2and_rust/pyproject.toml") | |
| if before | |
| else None | |
| ) | |
| s2and_changed = before_py_ver != cur_py_ver | |
| rust_changed = before_rust_py_ver != cur_rust_py_ver | |
| publish_any = s2and_changed or rust_changed or publish_s2and or publish_rust | |
| out_path = os.environ["GITHUB_OUTPUT"] | |
| with open(out_path, "a", encoding="utf-8") as f: | |
| f.write(f"s2and_changed={'true' if s2and_changed else 'false'}\n") | |
| f.write(f"rust_changed={'true' if rust_changed else 'false'}\n") | |
| f.write(f"publish_any={'true' if publish_any else 'false'}\n") | |
| f.write(f"publish_s2and={'true' if publish_s2and else 'false'}\n") | |
| f.write(f"publish_rust={'true' if publish_rust else 'false'}\n") | |
| f.write(f"force_build={'true' if force_build else 'false'}\n") | |
| print(f"s2and: {before_py_ver} -> {cur_py_ver} changed={s2and_changed}") | |
| print(f"rust: {before_rust_py_ver} -> {cur_rust_py_ver} changed={rust_changed}") | |
| print(f"force_build: {force_build}") | |
| print(f"publish_s2and: {publish_s2and}") | |
| print(f"publish_rust: {publish_rust}") | |
| PY | |
| s2and-dist: | |
| name: s2and sdist+wheel | |
| runs-on: ubuntu-latest | |
| needs: [detect-versions] | |
| if: needs.detect-versions.outputs.s2and_changed == 'true' || needs.detect-versions.outputs.force_build == 'true' || needs.detect-versions.outputs.publish_s2and == 'true' | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.11" | |
| - name: Build s2and distributions | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install build | |
| python -m build --sdist --wheel --outdir dist | |
| - uses: actions/upload-artifact@v6 | |
| with: | |
| name: dist-s2and | |
| path: dist/* | |
| wheels-windows: | |
| name: wheels (windows, py${{ matrix.py }}) | |
| runs-on: windows-latest | |
| needs: [detect-versions] | |
| if: needs.detect-versions.outputs.rust_changed == 'true' || needs.detect-versions.outputs.force_build == 'true' || needs.detect-versions.outputs.publish_rust == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| py: ["3.10", "3.11", "3.12"] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.py }} | |
| - name: Build wheels | |
| uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 | |
| with: | |
| maturin-version: v1.11.5 | |
| command: build | |
| args: --release --locked --compatibility pypi --out dist | |
| working-directory: s2and_rust | |
| - uses: actions/upload-artifact@v6 | |
| with: | |
| name: dist-s2and-rust-windows-py${{ matrix.py }} | |
| path: s2and_rust/dist/* | |
| wheels-macos: | |
| name: wheels (macos universal2, py${{ matrix.py }}) | |
| runs-on: macos-latest | |
| needs: [detect-versions] | |
| if: needs.detect-versions.outputs.rust_changed == 'true' || needs.detect-versions.outputs.force_build == 'true' || needs.detect-versions.outputs.publish_rust == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| py: ["3.10", "3.11", "3.12"] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ matrix.py }} | |
| - name: Install Rust targets (universal2) | |
| run: rustup target add x86_64-apple-darwin aarch64-apple-darwin | |
| - name: Build wheels (universal2) | |
| uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 | |
| env: | |
| ARCHFLAGS: "-arch x86_64 -arch arm64" | |
| with: | |
| maturin-version: v1.11.5 | |
| command: build | |
| args: --release --locked --compatibility pypi --out dist | |
| working-directory: s2and_rust | |
| - uses: actions/upload-artifact@v6 | |
| with: | |
| name: dist-s2and-rust-macos-universal2-py${{ matrix.py }} | |
| path: s2and_rust/dist/* | |
| wheels-linux: | |
| name: wheels (linux ${{ matrix.platform.name }}) | |
| runs-on: ubuntu-latest | |
| needs: [detect-versions] | |
| if: needs.detect-versions.outputs.rust_changed == 'true' || needs.detect-versions.outputs.force_build == 'true' || needs.detect-versions.outputs.publish_rust == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - name: manylinux2014-x86_64 | |
| target: x86_64-unknown-linux-gnu | |
| manylinux: "2_17" | |
| # Optional: enable when you want aarch64 wheels. | |
| - name: manylinux2014-aarch64 | |
| target: aarch64-unknown-linux-gnu | |
| manylinux: "2_17" | |
| - name: musllinux-x86_64 | |
| target: x86_64-unknown-linux-musl | |
| manylinux: "musllinux_1_2" | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Build wheels (manylinux/musllinux) | |
| uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 | |
| with: | |
| maturin-version: v1.11.5 | |
| command: build | |
| target: ${{ matrix.platform.target }} | |
| manylinux: ${{ matrix.platform.manylinux }} | |
| args: > | |
| --release --locked --compatibility pypi --out dist | |
| -i python3.10 -i python3.11 -i python3.12 | |
| working-directory: s2and_rust | |
| - uses: actions/upload-artifact@v6 | |
| with: | |
| name: dist-s2and-rust-linux-${{ matrix.platform.name }} | |
| path: s2and_rust/dist/* | |
| sdist: | |
| name: sdist | |
| runs-on: ubuntu-latest | |
| needs: [detect-versions] | |
| if: needs.detect-versions.outputs.rust_changed == 'true' || needs.detect-versions.outputs.force_build == 'true' || needs.detect-versions.outputs.publish_rust == 'true' | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Build sdist | |
| uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 | |
| with: | |
| maturin-version: v1.11.5 | |
| command: sdist | |
| args: --out dist | |
| working-directory: s2and_rust | |
| - uses: actions/upload-artifact@v6 | |
| with: | |
| name: dist-s2and-rust-sdist | |
| path: s2and_rust/dist/* | |
| # Publish is split so skipped build jobs don't block unrelated publishes. | |
| # Manual runs can publish by setting workflow_dispatch inputs. | |
| publish-s2and: | |
| name: publish s2and to PyPI | |
| if: (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-versions.outputs.s2and_changed == 'true') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && needs.detect-versions.outputs.publish_s2and == 'true') | |
| needs: [detect-versions, s2and-dist] | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download s2and dists | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: dist-s2and | |
| path: dist/s2and | |
| - name: Publish s2and | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/s2and | |
| publish-rust: | |
| name: publish s2and-rust to PyPI | |
| if: (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-versions.outputs.rust_changed == 'true') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && needs.detect-versions.outputs.publish_rust == 'true') | |
| needs: [detect-versions, wheels-windows, wheels-macos, wheels-linux, sdist] | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download s2and-rust dists | |
| uses: actions/download-artifact@v7 | |
| with: | |
| pattern: dist-s2and-rust-* | |
| merge-multiple: true | |
| path: dist/s2and-rust | |
| - name: Publish s2and-rust | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/s2and-rust |