From e2f9fcaeb0cf64bd925fc923774e3610b74ab09d Mon Sep 17 00:00:00 2001 From: martincham Date: Wed, 22 Jul 2026 15:16:38 -0400 Subject: [PATCH] fix(packaging): bundle scripts/python into the wheel core_pack (#3665) `specify init --script py` generated skills that invoke `python3 .specify/scripts/python/.py`, but the wheel's force-include list only carried `scripts/bash` and `scripts/powershell`. Installs from PyPI/Homebrew therefore shipped commands pointing at files that were never packaged, leaving `--script py` non-functional while `sh`/`ps` kept working. Force-include `scripts/python` alongside the other two variants, and add a contract test that asserts every script variant present in the repo is bundled, so a future variant cannot be dropped the same way. Co-Authored-By: Claude Opus 4.8 --- pyproject.toml | 1 + .../contract/test_wheel_core_pack_scripts.py | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/contract/test_wheel_core_pack_scripts.py diff --git a/pyproject.toml b/pyproject.toml index b15b535ced..c0bd91c6ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ packages = ["src/specify_cli"] "templates/commands" = "specify_cli/core_pack/commands" "scripts/bash" = "specify_cli/core_pack/scripts/bash" "scripts/powershell" = "specify_cli/core_pack/scripts/powershell" +"scripts/python" = "specify_cli/core_pack/scripts/python" # Bundled extensions (installable via `specify extension add `) "extensions/git" = "specify_cli/core_pack/extensions/git" "extensions/agent-context" = "specify_cli/core_pack/extensions/agent-context" diff --git a/tests/contract/test_wheel_core_pack_scripts.py b/tests/contract/test_wheel_core_pack_scripts.py new file mode 100644 index 0000000000..559accc8f3 --- /dev/null +++ b/tests/contract/test_wheel_core_pack_scripts.py @@ -0,0 +1,40 @@ +"""Contract tests for the script variants bundled into the wheel's core_pack. + +``specify init --script `` installs from ``specify_cli/core_pack/scripts/`` +when the CLI runs from a wheel. Any script variant that lives in the repository +must therefore be force-included at build time, otherwise the generated +commands reference scripts the released package never ships (#3665). +""" + +from __future__ import annotations + +import tomllib +from pathlib import Path + +REPO_ROOT = Path(__file__).parents[2] + + +def _force_include() -> dict[str, str]: + with (REPO_ROOT / "pyproject.toml").open("rb") as pyproject_file: + pyproject = tomllib.load(pyproject_file) + return pyproject["tool"]["hatch"]["build"]["targets"]["wheel"]["force-include"] + + +def test_every_script_variant_is_bundled_into_core_pack(): + force_include = _force_include() + variants = sorted( + path.name for path in (REPO_ROOT / "scripts").iterdir() if path.is_dir() + ) + + assert variants, "expected at least one script variant under scripts/" + for variant in variants: + assert force_include.get(f"scripts/{variant}") == ( + f"specify_cli/core_pack/scripts/{variant}" + ), f"scripts/{variant} is missing from the wheel force-include list" + + +def test_python_script_variant_is_bundled(): + # Explicit regression guard for #3665: `--script py` shipped skills that + # invoked python3 .specify/scripts/python/*.py while the wheel bundled + # only the bash and PowerShell variants. + assert _force_include()["scripts/python"] == "specify_cli/core_pack/scripts/python"