# mkwheels

A standalone bash CLI that builds a **reproducible, pinned Python wheels
tarball** for a given package + version. Primary use: vendoring a Python
project's full dependency tree into a SlackBuild so the build installs into a
venv from local wheels with no network (the feroxbuster vendored-crates
pattern, applied to Python).

## Layout

```
mkwheels          # the whole CLI (single-file bash)
selftest          # reproducibility check (builds six twice, asserts md5 match)
LICENSE           # GPLv2 full text
README.md         # user-facing usage + rationale
docs/superpowers/ # design spec + implementation plan
```

Single-file script by design. Keep it that way unless the tool genuinely
outgrows one file.

## Invocation

```
mkwheels <pkg> <ver> [epoch]
```

- `<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.
- `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
   input).
5. 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.

**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
current, and the tarball, once built, is the source of truth. The
`requirements.txt` records the exact resolved versions.

## Conventions

- `bash`, no third-party Python packages (the tool only drives `pip`).
- `set -eu` + `set -o pipefail`; temp workdir removed via `trap` on EXIT.
- GPLv2 (v2-only): per-file header, `Copyright (C) <year> Danilo M.
  <danix@danix.xyz>`, License section in README.
- Commits GPG-signed. Work directly on master (small project).

## 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.

## Maintainer

danix — danix@danix.xyz
