diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0e00b99..fbeafaa 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,6 +37,15 @@ jobs: - name: Build sdist and wheel run: python -m build + - name: Install verification tools + run: uv pip install --system check-manifest==0.51 twine==6.2.0 + + - name: Verify sdist completeness + run: check-manifest --verbose + + - name: Validate distribution metadata + run: twine check --strict dist/* + - name: Upload distribution artifacts uses: actions/upload-artifact@v4 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8e58ee..f8e2ee5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,3 +24,12 @@ repos: additional_dependencies: - pycparser - click + + - repo: https://github.com/mgedmin/check-manifest + rev: "0.51" + hooks: + - id: check-manifest + args: [--no-build-isolation] + additional_dependencies: + - setuptools>=61 + - wheel diff --git a/CHANGELOG.md b/CHANGELOG.md index 69b3438..cac0185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.2.3] - 2026-06-05 + +### Fixed +- **Incomplete source distribution** - The PyPI sdist now ships the full test + suite, including helper modules (`test/assertions.py`, `test/cython_utils.py`, + `test/conftest.py`, `test/header_cache.py`, `test/library_detection.py`), + fixtures, and data directories. Previously these were omitted, causing + `ModuleNotFoundError: No module named 'test.assertions'` when downstream + packagers ran the tests from the sdist (NixOS/nixpkgs#524341). The release + workflow now runs `check-manifest` and `twine check --strict` before publishing + so an incomplete sdist cannot ship again. + + Note for downstream packagers without network access: the `real_headers` tests + download library headers at runtime and must be deselected with + `pytest -m "not real_headers"`. +- **Missing runtime stub in wheel and sdist** - Ship + `autopxd/stubs/darwin-include/.supports-builtin-modules` in both the wheel and + the sdist. The `stubs/darwin-include/**/*` package-data glob silently skipped + leading-dot files, so this runtime include-tree stub was missing from every + previous wheel. + ## [3.2.2] - 2025-12-19 ### Fixed @@ -179,7 +200,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - macOS support -[Unreleased]: https://github.com/elijahr/python-autopxd2/compare/v3.2.0...HEAD +[Unreleased]: https://github.com/elijahr/python-autopxd2/compare/v3.2.3...HEAD +[3.2.3]: https://github.com/elijahr/python-autopxd2/compare/v3.2.2...v3.2.3 +[3.2.2]: https://github.com/elijahr/python-autopxd2/compare/v3.2.1...v3.2.2 +[3.2.1]: https://github.com/elijahr/python-autopxd2/compare/v3.2.0...v3.2.1 [3.2.0]: https://github.com/elijahr/python-autopxd2/compare/v3.1.1...v3.2.0 [3.1.1]: https://github.com/elijahr/python-autopxd2/compare/v3.1.0...v3.1.1 [3.1.0]: https://github.com/elijahr/python-autopxd2/compare/v3.0.0...v3.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9426155..bc890ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -158,6 +158,24 @@ This section is for project maintainers who can publish releases. git push origin master ``` +> **Important:** The git ref you release from (the tag, or the SHA passed to +> the manual `workflow_dispatch`) MUST contain the `pyproject.toml` version +> bump. `workflow_dispatch` accepts an arbitrary ref and will happily build +> and publish from a ref that lacks the bump. + +### Sdist completeness guard + +The publish workflow builds the sdist and wheel, then runs `check-manifest` +and `twine check --strict` **before** uploading the distribution artifacts. If +the sdist is missing any version-controlled file that belongs in it (for +example, a new test helper or fixture), `check-manifest` fails, the `build` +job fails, and because `publish-pypi` depends on `build`, nothing is published. + +`check-manifest` also runs locally via pre-commit, so drift is usually caught +before you push. If it flags a new file, update `MANIFEST.in` (to ship it) or +the `[tool.check-manifest]` `ignore` list in `pyproject.toml` (to keep it out +of the sdist) — never weaken the gate to make it pass. + ### 2. Create a GitHub Release 1. Go to [Releases](https://github.com/elijahr/python-autopxd2/releases) diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e70fcc9 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,34 @@ +# Sdist content spec. The wheel is unaffected (include_package_data is not set; +# packages are controlled by [tool.setuptools.packages.find]). +# Ship the complete test tree so downstream packagers (e.g. nixpkgs) can run the +# suite from the sdist. See NixOS/nixpkgs#524341. + +# --- Test suite: helpers, fixtures, and data (the fix) --- +# Ship the entire tracked test tree; byte-compiled cruft is excluded below. +graft test + +# --- Runtime stub dotfile dropped by setuptools globbing --- +# The "stubs/darwin-include/**/*" package-data glob does not match leading-dot +# files, so the sdist's default include misses this tracked stub. It is read at +# runtime (clang -I include path), so ship it explicitly. +include autopxd/stubs/darwin-include/.supports-builtin-modules + +# --- Top-level docs/metadata worth shipping in source form --- +include CHANGELOG.md +include CONTRIBUTING.md +include regenerate_stubs.py + +# --- Test-library install helpers referenced by test/test_real_headers.py --- +graft scripts + +# --- Repo-only infrastructure: never ship --- +exclude .gitignore +exclude .dockerignore +exclude .pre-commit-config.yaml +exclude Dockerfile +exclude mkdocs.yml +prune .github +prune docs + +# --- Hygiene: keep graft from sweeping byte-compiled files --- +global-exclude *.py[cod] diff --git a/pyproject.toml b/pyproject.toml index abd7bf2..9a8fcd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools>=61", "wheel"] build-backend = "setuptools.build_meta" [project] name = "autopxd2" -version = "3.2.2" +version = "3.2.3" description = "Automatically generate Cython pxd files from C headers" readme = "README.md" license = { file = "LICENSE" } @@ -67,6 +67,11 @@ autopxd = [ "stubs/include/**/*", "stubs/darwin-include/*", "stubs/darwin-include/**/*", + # Leading-dot files are not matched by the "*" globs above (setuptools + # mirrors shell semantics where "*" skips dotfiles). This stub is consumed + # at runtime via the clang -I include path (declarations.py / + # pycparser_backend.py), so it must ship in the wheel as well as the sdist. + "stubs/darwin-include/.supports-builtin-modules", ] [tool.ruff] @@ -120,3 +125,14 @@ markers = [ "libclang: tests/parameterizations using the libclang backend (includes C++ tests)", "real_headers: tests against real system libraries (requires library installation)", ] + +[tool.check-manifest] +ignore = [ + ".gitignore", + ".dockerignore", + ".pre-commit-config.yaml", + "Dockerfile", + "mkdocs.yml", + ".github/**", + "docs/**", +]