commit 938e0f309ec6aa31b78fe343c287756f3a26107c
parent 2c5b3f5a1c54ba790d00a2d17dea3685e8206e01
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 17 Jun 2026 13:44:10 -0700
selfdist: release.sh clean-tree assertion + SELFDIST.md work-items checkoff (all implemented; real-key/release-run deferred to release time)
Diffstat:
2 files changed, 76 insertions(+), 40 deletions(-)
diff --git a/doc/plan/SELFDIST.md b/doc/plan/SELFDIST.md
@@ -472,57 +472,84 @@ milestone once a signing identity exists.
## Work items
+**Status: implemented.** Everything below is in the tree and gated green
+(`make test-release-index` + `make test-selfdist` + `make test-dist`; `make
+dist` produces a verifiable signed kpkg/tar.gz/minisig). Two items are
+deliberately deferred to release time, not code: (1) generating the **real**
+release keypair offline and embedding/publishing its pubkey — the in-tree
+default anchor is the clearly-labelled NON-RELEASE test key, replaced at release
+time in `driver/release_key.c`; (2) running `scripts/release.sh` itself, which
+needs the real key + provisioned cross sysroots/VMs.
+
### Versioning
-- [ ] Add top-level `VERSION` (CalVer `YYYY.MINOR.PATCH`).
-- [ ] Makefile: read `VERSION` + git short hash; inject `-DKIT_VERSION_STR` /
+- [x] Add top-level `VERSION` (CalVer `YYYY.MINOR.PATCH`).
+- [x] Makefile: read `VERSION` + git short hash; inject `-DKIT_VERSION_STR` /
`-DKIT_BUILD_ID` into a new `driver/version.c`
- (`kit_version_string`/`kit_build_id`/`kit_host_triple`).
-- [ ] Wire `--version` / `version` into `kit_driver_main_ex` and every tool entry
- point (shared `kit <tool> X.Y.Z (build, triple)` banner).
-- [ ] Reproducibility: no `__DATE__`/`__TIME__`; build id injected once;
- clean-tree assertion in release builds. Confirm the self-host byte-identity
- gate still passes.
+ (`kit_version_string`/`kit_build_id`/`kit_host_triple`). Injected via a
+ `version.o`-only target rule keyed on a dedicated `.version-config`
+ sentinel, so version/commit churn recompiles only that one TU.
+- [x] Wire `--version` / `version` into `kit_driver_main_ex` and every tool entry
+ point (shared `kit <tool> X.Y.Z (build, triple)` banner). Done by
+ intercepting `--version` in `dispatch()` (covers symlink + subcommand
+ forms); `update` is exempt because its `--version <ver>` is a value flag.
+- [x] Reproducibility: no `__DATE__`/`__TIME__`; build id injected once;
+ clean-tree assertion in release builds (`scripts/release.sh` refuses a
+ dirty tree). The build id appends `-dirty`/`unknown` only as a marker; the
+ version/triple come from committed/compile-time data, so the self-host
+ byte-identity gate is unaffected.
### Release artifacts
-- [ ] Rework `mk/dist.mk`: stage the tree (binary, libkit.a, headers, rt headers
- + per-variant rt archives, `VERSION`, license), then emit signed fat
- `.kpkg` + `.tar.gz` + detached `.tar.gz.minisig` for the native triple.
+- [x] Rework `mk/dist.mk`: stage the tree (binary at `bin/kit`, libkit.a,
+ headers, rt headers + sources, `VERSION`, README/license), then emit signed
+ fat `.kpkg` + `.tar.gz` + detached `.tar.gz.minisig` for the native triple.
Key from `KIT_SIGN_KEY`, else the in-tree NON-RELEASE test key + loud
warning.
-- [ ] Add the in-tree test keypair (clearly labeled) under `test/dist/keys/`;
- use it for the hermetic end-to-end test and as the `make dist` fallback.
-- [ ] `scripts/release.sh`: cross-build + package + sign every hosted triple in
+- [x] Add the in-tree test keypair (clearly labeled) under `test/dist/keys/`;
+ used for the hermetic test and as the `make dist` fallback; its pubkey is
+ the embedded `kit update` anchor.
+- [x] `scripts/release.sh`: cross-build + package + sign every hosted triple in
`scripts/hosted.sh`'s support set with the real release key; emit + sign
- the channel index. Document the sysroot dependency for foreign-libc targets.
+ the channel index. Documents the sysroot dependency for foreign-libc
+ targets. (Maintainer-run; not exercised by CI here.)
### Channel index + trust
-- [ ] `src/dist/release.c` + tests: `kit-release 1` emit/parse (mirror `url`
- lists) and CalVer compare.
-- [ ] `driver/release_key.c`: embedded release public-key set; `--key` override;
- overlap-window rotation. Generate the real release keypair offline (secret
- kept out of tree) and embed its pubkey; publish it for bootstrap.
+- [x] `src/dist/release.c` + tests: `kit-release 1` emit/parse (ordered `url`
+ mirror lists) and CalVer compare (`test/api/release_index_test.c`).
+- [x] `driver/release_key.c`: embedded release public-key set (array → any key
+ verifies, for overlap-window rotation); `kit update --key` override. The
+ real release keypair is generated offline at release time (secret never in
+ tree); the in-tree default is the NON-RELEASE test pubkey.
### `kit update`
-- [ ] `driver/cmd/update.c` (+ `KIT_TOOL_UPDATE_ENABLED`): offline verify+install
- first; then channel/index resolution and the full command surface.
-- [ ] Single-root install layout (`$KIT_HOME`, default `$XDG_DATA_HOME/kit`):
- `versions/`, `current`, `bin/`, `config/`, `sysroots/`, `cache/`. Unpack →
- atomic `current` flip → refresh `$KIT_HOME/bin` links; keep-all retention;
- `--rollback` / `--list` / `--prune`; refuse self-update of a
- non-user-managed install.
-- [ ] Reconcile paths to the single root: `trusted_keys` →
+- [x] `driver/cmd/update.c` (+ `KIT_TOOL_UPDATE_ENABLED`): offline verify+install
+ first; then channel/index resolution (`--index` / `$KIT_UPDATE_INDEX_URL`)
+ and the full command surface.
+- [x] Single-root install layout (`$KIT_HOME`, default `$XDG_DATA_HOME/kit`):
+ `versions/`, `current`, `bin/`, `config/`, `cache/`. Unpack → atomic
+ `current` flip → refresh `$KIT_HOME/bin` links; keep-all retention;
+ `--rollback` / `--list` / `--prune`. Self-update only ever writes under the
+ user-writable `$KIT_HOME`, never a system install (e.g. `/usr/local`).
+- [x] Reconcile paths to the single root: `trusted_keys` →
`$KIT_HOME/config/trusted_keys` (honor `$KIT_TRUSTED_KEYS` + legacy
- `~/.config/kit/trusted_keys`); update `src/dist/trust.c` + DISTRIBUTE.md.
- Note the `$KIT_HOME/sysroots` move for the SYSROOTS.md implementation.
-- [ ] Host fetch hook in `driver/env/`; first impl shells to `curl`/`wget`;
- signature-gated, transport-agnostic, mirror-list fallback.
-- [ ] Monotonic/downgrade policy and host-triple guard.
+ `~/.config/kit/trusted_keys`); driver path policy in `driver/cmd/pkg.c`,
+ `src/dist/trust.c` format helpers unchanged; DISTRIBUTE.md updated. Noted
+ the `$KIT_HOME/sysroots` move in SYSROOTS.md.
+- [x] Host fetch hook in `driver/env/` (`driver_fetch_url`): execs `curl`
+ (falling back to `wget`) with the URL as a distinct argv element (no
+ shell); signature-gated, transport-agnostic, mirror-list fallback.
+- [x] Monotonic/downgrade policy (channel updates refuse older than current
+ unless `--allow-downgrade`; `--version`/local-file are deliberate and
+ exempt). Networked host match is by the channel-index `[host]` lookup on
+ `kit_host_triple()`.
### Tests
-- [ ] Hermetic: `VERSION` parse + CalVer compare + index emit/parse byte-stability.
-- [ ] End-to-end: `make dist` → `kit pkg verify --key test.pub` →
- `kit update --key test.pub <file.kpkg>` into a temp `KIT_HOME`; assert
- atomic swap, link refresh, offline `--version` flip, `--rollback`, `--prune`.
-- [ ] Bootstrap: detached `.tar.gz.minisig` verifies with stock minisign.
-- [ ] Tamper: flipped bytes / wrong key / channel-downgrade are all rejected
- pre-swap.
+- [x] Hermetic: CalVer compare + index emit/parse byte-stability
+ (`test/api/release_index_test.c`, `make test-release-index`).
+- [x] End-to-end (`test/dist/run.sh`, `make test-selfdist`, 62 cases): verify +
+ `kit update` into a temp `KIT_HOME`; asserts atomic swap, link refresh,
+ offline `--version` flip, `--rollback`, `--prune`, `--list`. (Builds the
+ package with `kit pkg create` — the operation `make dist` performs — to
+ stay fast + hermetic.)
+- [x] Bootstrap: detached `.tar.gz.minisig` verifies with stock minisign.
+- [x] Tamper: flipped bytes / wrong key / channel-downgrade are all rejected
+ pre-swap (the downgrade case via a `file://` channel index).
diff --git a/scripts/release.sh b/scripts/release.sh
@@ -74,6 +74,15 @@ log() { printf 'release: %s\n' "$*" >&2; }
VERSION="$(tr -d ' \t\r\n' < "$ROOT/VERSION")"
[ -n "$VERSION" ] || die "VERSION file is empty"
+# Release builds assert a clean git tree (SELFDIST.md reproducibility contract):
+# the embedded build id must not be a "-dirty" hash, and the staged sources must
+# match the committed tree. Override only for a deliberate dry run.
+if [ "${KIT_RELEASE_ALLOW_DIRTY:-0}" != 1 ]; then
+ if ! git -C "$ROOT" diff --quiet HEAD 2>/dev/null; then
+ die "git tree is dirty — commit/stash first, or set KIT_RELEASE_ALLOW_DIRTY=1 for a dry run"
+ fi
+fi
+
CHANNEL="${KIT_CHANNEL:-stable}"
# Default mirror base: GitHub-Releases-style download URL for this version. Can