From 56e3456cfcc0e74c1535482a9a94d13def2d957c Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Sun, 21 Jun 2026 22:05:00 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20handoff=2075=20=E2=80=94=20D3=20package?= =?UTF-8?q?=20managers=20complete=20(Scoop/Homebrew/winget)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the session that finished the D3 rollout: install.ps1 Windows 5.1 fixes, Scoop + Homebrew wiring and validation, the Homebrew-403 presigned-URL saga fixed via a server-side Caddy HEAD rule, and the winget job + komac bootstrap (PR #391335). Records the infra created off-repo (Caddy rule, the lazyeval Gitea/GitHub bots + tokens), the gotchas, and the parked signing tracks. --- docs/handoff/20260621-handoff-75.md | 145 ++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/handoff/20260621-handoff-75.md diff --git a/docs/handoff/20260621-handoff-75.md b/docs/handoff/20260621-handoff-75.md new file mode 100644 index 0000000..c91d245 --- /dev/null +++ b/docs/handoff/20260621-handoff-75.md @@ -0,0 +1,145 @@ +# Session handoff — 2026-06-21 (75) + +Continues from handoff-74 (v0.2.0 live on crates.io). This session **finished +the D3 package-manager rollout** — Scoop, Homebrew, and winget — plus the +Windows `install.ps1` fixes, and included a deep **presigned-URL / Homebrew** +debugging saga that's fixed by a server-side Caddy rule. Five commits; one +new ADR amendment arc (ADR-0056 Amendments 2–4). + +## §1. State + +**Branch `main`.** **No crate (Rust) code changed this session** — only +`.gitea/workflows/`, `scripts/`, and `docs/`. So the `cargo test` baseline +from handoff-74 (**2509 pass / 0 fail / 1 ignored**, clippy + `fmt --check` +clean) stands unchanged by construction; not re-run (nothing in the build +graph moved). New coverage this session is **`scripts/test-package-renders.sh`** +(shellcheck-clean, green), now gated on every push by a new **`ci.yaml` +`manifests` job** (bash + node; ruby-absent in CI degrades gracefully). + +**Commits this session** (`cabc813`→`0208c67`; the workflow-bearing ones were +pushed, since the live `publish` dispatches + raw-URL installs the user tested +needed them on `main`): +- `42b40bc` install.ps1 → Windows PowerShell 5.1 compat +- `c0531aa` install.ps1 → immediate-use PATH + honest messaging +- `6d54c1e` Scoop + Homebrew jobs (D3 §3b/§3c) +- `6bb2288` Scoop/Homebrew validated + Caddy fix recorded (Amendment 3) +- `0208c67` winget job via komac (D3 §3d, Amendment 4) + +**External / infra state created this session (NOT all in the repo):** +- **Caddy Tier-A HEAD rule on the Gitea server** — *load-bearing for Homebrew*, + lives in the Gitea edge config, **not** this repo (see §4). If lost on a + server rebuild, brew 403s again. +- **Gitea:** `lazyeval` org with repos **`scoop-bucket`** + **`homebrew-tap`**; + **`lazyeval-ci`** Gitea bot user (org team, Write to those repos); + **`LAZYEVAL_PKG_TOKEN`** secret on `oli/rdbms-playground`. +- **GitHub:** **`lazyeval-ci`** bot account; classic `public_repo` PAT → + **`WINGET_GITHUB_TOKEN`** secret on `oli/rdbms-playground`; fork + **`lazyeval-ci/winget-pkgs`**. +- **winget bootstrap PR [#391335](https://github.com/microsoft/winget-pkgs/pull/391335)** + submitted, in Microsoft review. +- **komac 2.16.0** installed locally at `~/.local/bin/komac`. + +## §2. D3 — the package-manager set (the session's throughline) + +| Channel | State | +| --- | --- | +| crates.io / `cargo binstall` | **live** (handoff-74) | +| `install.sh` / `install.ps1` | **live** — PS1 validated on ARM Windows 11 (5.1 + 7.6) | +| Scoop (`lazyeval/scoop-bucket`) | **live + validated** (install + run) | +| Homebrew (`lazyeval/homebrew-tap`) | **live + validated** — needs the Caddy rule (§4) | +| winget (`LazyEvaluation.RdbmsPlayground`) | **wired**; PR #391335 awaiting Microsoft merge | + +How Scoop/Homebrew/winget are wired: sibling jobs in +`.gitea/workflows/publish.yaml` (manual `workflow_dispatch`, `tag` input), +each idempotent + independent. Scoop/Homebrew render **dependency-free bash** +manifests (`scripts/render-{scoop-manifest,homebrew-formula}.sh`) from the +release `.sha256` sidecars and push to the org repos via `LAZYEVAL_PKG_TOKEN`. +winget runs `komac update --submit` to PR `microsoft/winget-pkgs`, guarded +against duplicate PRs. ADR-0056 **Amendments 2–4** carry the full design. + +## §3. The Homebrew 403 saga + Caddy fix (don't lose this) + +Homebrew `brew install` 403'd while every other tool worked. Root cause, +reproduced: Gitea (≥1.25; here 1.26.2) **303-redirects release downloads to a +method-bound AWS-SigV4 presigned S3 URL** (OVH), signed for the *incoming* +request's HTTP verb. Homebrew **resolves with a HEAD, captures the returned +HEAD-signed URL, then runs the download GET against it** → GET-on-a-HEAD-signed +URL → 403. (`GET on HEAD-resolved = 403`, `HEAD on HEAD-resolved = 200`, +`GET on GET-resolved = 200`.) GET-only tools (install.sh/ps1, binstall, curl) +are unaffected. This is a **Homebrew defect** (reusing an ephemeral +method-scoped credential as a durable locator); Gitea's per-request signing is +correct, and SigV4 *cannot* sign one URL for two verbs. `SERVE_DIRECT=false` +would fix it but was **declined** (don't reshape storage for one client). + +**Fix (deployed, "Tier A"):** a Caddy rule answering **HEAD on +`…/releases/download/…` directly** (200, no redirect, no body) so brew's +resolve records the *original* URL and its download GET redirects fresh → +GET-signed → 200. GET untouched; a HEAD carries no payload so nothing can +break (a HEAD-probing download manager at most loses a progress-bar size). +Reference Caddyfile is in **ADR-0056 Amendment 3**. + +## §4. Immediate next steps + +1. **winget PR #391335:** watch validation — the one likely speed bump is an + **AV/SmartScreen manual-review label** (unsigned binary); usually clears. + Once a moderator merges, winget is live. +2. **Post-merge check (Windows):** `winget install LazyEvaluation.RdbmsPlayground`, + confirm the command is **`rdbms-playground`** (it is — a direct single-exe + portable takes its PATH alias from `Commands[0]`, which komac set; see §5). +3. After merge, future releases are hands-off: the CI `winget` job's + `komac update` carries each version (alias inherited from the merged + manifest). The release ritual is unchanged from ADR-0054, with the + `publish` dispatch now also doing Scoop/Homebrew/winget. + +## §5. Gotchas learned (don't relearn the hard way) + +- **The Caddy HEAD rule is server-side, not in this repo.** Homebrew (and any + HEAD-then-GET client) depends on it. Record/back it up. +- **GitHub tokens:** komac needs a **classic `public_repo`** PAT — **fine-grained + tokens can't open the cross-fork PR** (komac #310: the PR is created on the + *target* repo you don't administer, which the fine-grained model can't + express). A classic token can't be repo-scoped, so it lives on a **dedicated + bot** (`lazyeval-ci`) to bound the blast radius. +- **komac fork must pre-exist:** `komac new` auto-fork *races* on winget-pkgs + (huge repo) → *"Could not resolve to a Repository 'lazyeval-ci/winget-pkgs'"*. + Fork manually first (`gh repo fork microsoft/winget-pkgs --clone=false`), + wait, then re-run. Pre-fill `komac new` flags to skip the long interactive + prompt parade. +- **Direct portable alias = `Commands[0]`, NOT `PortableCommandAlias`.** The + latter is **archive/nested-portable-only**; adding it to a bare-exe portable + is wrong (komac correctly omits it). The "Commands" prompt → `rdbms-playground` + is the on-PATH command. Verified vs winget-cli logic + the 1.6.0 schema. +- **install.ps1 / Windows PowerShell 5.1** (the in-box shell — PS7 is opt-in): + arch via `PROCESSOR_ARCHITECTURE` env (not `RuntimeInformation::OSArchitecture`, + which is absent under 5.1's .NET-Framework facade + StrictMode); force TLS 1.2; + `-UseBasicParsing`. Also update `$env:Path` in-session (the persisted User PATH + only reaches *new* processes; "restart your shell" was wrong — sign-out/in or + the in-session update). +- **Gitea release downloads go through OVH S3 presigned URLs** — method-bound, + 300 s expiry. Never assume HEAD-resolve-then-GET works against them. + +## §6. Parked / deferred (user decisions) + +- **macOS Developer-ID signing + notarization** — pending the Apple account → + Organization conversion. Ad-hoc signing covers all package-manager paths + (brew-installed binary *runs* on Apple Silicon); Developer-ID only matters for + browser-download Gatekeeper trust. +- **Windows code signing (Trusted Signing)** — not required to *ship* winget + (portable; only MSIX needs signing). Unsigned can earn an AV/SmartScreen + manual-review label + a user-run warning. **Lazy Evaluation Ltd (since 2012) + clears Trusted Signing's 3-year-org bar** when the user wants to remove the + warning (individual onboarding is currently paused; org path is open). +- **Release notes / CHANGELOG** — raised this session, not done. If wanted: + a `CHANGELOG.md` and/or populated Gitea release bodies, then the CI `winget` + job can pass komac `--release-notes-url …/releases/tag/$TAG`. + +## §7. How to take over + +1. Read handoffs 73 → 74 → 75, `CLAUDE.md`, `docs/requirements.md` (D1/D3), + and **ADR-0056 (esp. Amendments 1–4)** + the GA plan + `docs/plans/20260616-public-availability.md`. +2. Workflow unchanged: phased, test-first, `/runda` + DA before commits, ADR + amendment + README index-upkeep for decided-area changes, confirm commit + messages, never push. +3. If `cargo test` is needed, the baseline is **2509 / 1 ignored** (handoff-74). +4. Consider a `cargo sweep` at this milestone (`target/` grows).