diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 54 |
1 files changed, 33 insertions, 21 deletions
@@ -10,7 +10,7 @@ pattern, applied to Python). ``` mkwheels # the whole CLI (single-file bash) -selftest # reproducibility check (builds six twice, asserts md5 match) +selftest # reproducibility check (both modes, asserts md5 match) LICENSE # GPLv2 full text README.md # user-facing usage + rationale docs/superpowers/ # design spec + implementation plan @@ -21,35 +21,46 @@ outgrows one file. ## Invocation +Two subcommands; all options are explicit flags, no positionals. + ``` -mkwheels <pkg> <ver> [epoch] +mkwheels pypi --name PKG --ver VER [--epoch N] +mkwheels gh --repo OWNER/REPO --ver VER [--name PKG] [--tag TAG] [--epoch N] ``` -- `<pkg> <ver>` — PyPI package and exact version. -- `[epoch]` — optional `SOURCE_DATE_EPOCH`. Omitted → auto-derived from the - PyPI release upload time (earliest file's `upload_time_iso_8601`), with a - warning. Pass it explicitly to override. +- `--ver` / `--tag` strip a single leading `v`; the output version is always + without `v`. Output: `<name>-wheels-<ver>.tar.gz` + `requirements.txt`. +- `--epoch` optional in both modes; omitted → auto-derived (with a warning): + - `pypi`: earliest file's `upload_time_iso_8601` from the PyPI JSON. + - `gh`: the GitHub release `published_at` for the tag. +- `gh` defaults: `--name` = repo basename lowercased; `--tag` = normalized + `--ver`; the real ref is resolved by trying `<tag>` then `v<tag>`. - `OUTPUT` env var — output dir (default: `$PWD`). -Outputs `<pkg>-wheels-<ver>.tar.gz` + `requirements.txt`, prints md5 + epoch. - ## How it works -1. Arg parse + required-tool check (`python3`+pip, `jq`, `curl`, `tar`, `gzip`, - `md5sum`). -2. Resolve `SOURCE_DATE_EPOCH` (explicit arg, else PyPI JSON via `jq`). -3. Throwaway venv + `pip download <pkg>==<ver>` into `wheels/`. -4. Emit pinned + hashed `requirements.txt` (audit record only, not the install +1. Arg parse (mode selector + flags) + required-tool check (`python3`+pip, + `jq`, `curl`, `tar`, `gzip`, `md5sum`). +2. Mode resolution sets name, epoch, and how `wheels/` is populated: + - `pypi`: epoch from PyPI JSON; `pip download <name>==<ver>` (pre-built + wheels, deterministic). + - `gh`: resolve release ref + `published_at`; download+unpack the tagged + source; `pip wheel <src_dir>` builds the project **and all deps** (PyPI + + `git+` deps) to wheels. `pip download <dir>` is wrong here — it only + resolves metadata and leaves the local project unmaterialized. +3. Emit pinned + hashed `requirements.txt` (audit record only, not the install input). -5. Pack a byte-reproducible `.tar.gz`: sorted entries, `--mtime=@epoch`, +4. Pack a byte-reproducible `.tar.gz`: sorted entries, `--mtime=@epoch`, `--owner=0 --group=0 --numeric-owner`, `gzip -n`. ## Reproducibility -This is the whole point. The same `<pkg> <ver> [epoch]` MUST yield a -byte-identical tarball. The tar normalization (step 5) plus `set -o pipefail` -(so a `tar` failure can't be masked by `gzip` exiting 0) are what guarantees -it. +This is the whole point. The same inputs + epoch MUST yield a byte-identical +tarball. The tar normalization (step 4) plus `set -o pipefail` (so a `tar` +failure can't be masked by `gzip` exiting 0) are what guarantees it. In `gh` +mode the project is built from source, so reproducibility holds per-machine +(build once on the target platform, upload, pin md5); wheels with compiled +extensions may differ across toolchains. **Git-sourced deps** (packages whose upstream pins a git URL, e.g. NetExec's impacket) are frozen at download time: `pip download` resolves whatever is @@ -66,9 +77,10 @@ current, and the tarball, once built, is the source of truth. The ## Testing -`./selftest` — builds `six` twice with a fixed epoch and asserts the two -tarballs are byte-identical. Run it after any change to the tar/packing logic. -Needs network (pypi.org). No test framework. +`./selftest` — builds twice with a fixed epoch in both modes (`pypi` six, +`gh` pyparsing) and asserts each pair of tarballs is byte-identical. Run it +after any change to the tar/packing or mode-resolution logic. Needs network +(pypi.org, github.com). No test framework. ## Maintainer |
