aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md54
1 files changed, 33 insertions, 21 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index c93c237..a20247a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -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