diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5e8c7ce..63b9b5b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,8 +1,14 @@ # Test + clippy on every push/PR; on a version tag (`vX.Y.Z`) build wheels + sdist and attach them -# to a GitHub Release. crates.io is published manually (`cargo publish`). No PyPI — install the -# Python package from a release wheel or `pip install git+https://github.com/prostomarkeloff/directiva`. +# to a GitHub Release. crates.io is published manually (`cargo publish`). # -# No secrets needed — the GitHub Release uses the built-in GITHUB_TOKEN. +# PyPI is published manually too, from the all-platform wheels this matrix produces (a local +# `uv build` only covers the current platform, so publish from the CI set): +# gh release download vX.Y.Z -D dist # every wheel + the sdist the release job attached +# uv publish dist/* # needs a PyPI token (UV_PUBLISH_TOKEN / --token) +# `make dist-fetch TAG=vX.Y.Z` + `make publish` wrap exactly those two steps. +# +# No secrets needed in CI — the GitHub Release uses the built-in GITHUB_TOKEN; the PyPI token lives +# only on the maintainer's machine for `uv publish`. # # abi3 (pyo3 abi3-py39) → one wheel per platform/arch works on CPython 3.9+, so no per-Python matrix. name: CI @@ -65,6 +71,30 @@ jobs: name: wheels-linux-${{ matrix.target }} path: dist + # musl wheels (Alpine and the many slim Docker bases that ship musl libc, not glibc). Without + # these, `pip install directiva` on Alpine falls back to the sdist and needs a Rust toolchain. + musllinux: + runs-on: ubuntu-latest + strategy: + matrix: + target: [x86_64, aarch64] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist + sccache: "true" + manylinux: musllinux_1_2 + - uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.target }} + path: dist + macos: runs-on: macos-latest strategy: @@ -122,7 +152,7 @@ jobs: name: GitHub release (wheels + sdist) runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') - needs: [test, linux, macos, windows, sdist] + needs: [test, linux, musllinux, macos, windows, sdist] permissions: contents: write steps: diff --git a/Makefile b/Makefile index a706ba3..1cfb550 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ .DEFAULT_GOAL := help -.PHONY: help check test clippy fmt fmt-check lint build py-dev py-test fuzz-parse fuzz-glob clean +.PHONY: help check test clippy fmt fmt-check lint build py-dev py-test fuzz-parse fuzz-glob \ + dist dist-fetch publish clean help: @echo "directiva targets:" @@ -21,6 +22,9 @@ help: @echo " make py-test py-dev + a Python import/parse/match/lint smoke test" @echo " make fuzz-parse cargo-fuzz the parser (needs nightly + cargo-fuzz)" @echo " make fuzz-glob cargo-fuzz the glob engine (needs nightly + cargo-fuzz)" + @echo " make dist uv build → sdist + a wheel for THIS platform only (local smoke check)" + @echo " make dist-fetch TAG=vX.Y.Z download every CI-built wheel + sdist from the GH release" + @echo " make publish uv publish dist/* (run after dist-fetch; needs a PyPI token)" @echo " make clean cargo clean + remove .venv and built artefacts" check: fmt-check clippy test @@ -68,6 +72,34 @@ fuzz-parse: fuzz-glob: cargo +nightly fuzz run glob -- -max_total_time=$(FUZZ_TIME) +# ── publishing ────────────────────────────────────────────────────────────────── +# PyPI gets PREBUILT wheels for every platform so `pip install directiva` never needs a compiler. +# Those wheels come from the CI matrix (linux x86_64/aarch64, macOS x86_64/arm64, windows x64) + +# the sdist — NOT from a local build. Release flow: +# 1. bump `version` in Cargo.toml, commit +# 2. git tag vX.Y.Z && git push --tags → CI builds the matrix, attaches it to the GH release +# 3. make dist-fetch TAG=vX.Y.Z → pull that whole set into ./dist +# 4. make publish → uv publish dist/* (PyPI token via UV_PUBLISH_TOKEN) + +# Local build — ONE wheel (this platform) + the sdist. A smoke check / for inspecting the artifact; +# never the thing you publish (it would leave every other platform falling back to a source build). +dist: + rm -rf dist + uv build + +# Pull the all-platform wheels + sdist the CI `release` job attached to TAG, into ./dist. This is +# the set to publish. Needs the `gh` CLI authenticated to the repo. +dist-fetch: + @test -n "$(TAG)" || { echo "usage: make dist-fetch TAG=vX.Y.Z"; exit 2; } + rm -rf dist && mkdir -p dist + gh release download "$(TAG)" -D dist + @echo "fetched into ./dist:" && ls dist + +# Upload everything in ./dist to PyPI. Run after `make dist-fetch TAG=…`. Reads the token from +# UV_PUBLISH_TOKEN (or pass `uv publish --token …` yourself). +publish: + uv publish dist/* + clean: cargo clean rm -rf .venv dist fuzz/target fuzz/corpus fuzz/artifacts diff --git a/README.md b/README.md index 19cc5a1..dab5c6d 100644 --- a/README.md +++ b/README.md @@ -280,22 +280,17 @@ The package is **typed** (`py.typed` + stubs; `Severity` is a `Literal`), and ga `python` cargo feature so the pure-Rust crate keeps **zero** Python dependency by default. Built with [maturin](https://github.com/PyO3/maturin) (abi3 — one wheel per platform works on **CPython ≥ 3.9**). -**No PyPI.** Two ways to install: - -**1. Prebuilt wheel — no Rust toolchain.** Grab the wheel for your platform from the -[Releases page](https://github.com/prostomarkeloff/directiva/releases/latest) and install it by URL: - -```bash -pip install https://github.com/prostomarkeloff/directiva/releases/download/v0.2.0/directiva-0.2.0-cp39-abi3-macosx_11_0_arm64.whl -``` - -**2. From source — needs a Rust toolchain** (pip drives maturin automatically): +**Install — prebuilt, no Rust toolchain:** ```bash -pip install git+https://github.com/prostomarkeloff/directiva +pip install directiva # or: uv add directiva ``` -Build locally into a venv with `maturin develop --features python`. +Prebuilt abi3 wheels are on PyPI for Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows +(x64) — pip picks the one for your platform, no compiler involved. On a platform with no matching +wheel, pip falls back to the sdist, which **does** need a Rust toolchain (it drives maturin +automatically). For local development, build the extension into a venv with `make py-dev` +(`maturin develop --features python`). ---