Close the loop: build, sanity-check, and publish #1
Loading…
Reference in a new issue
No description provided.
Delete branch "worktree-build-publish-pipeline"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
makepkg(builder.rs), a post-build version sanity check (sanity.rs), andrepo-addpublishing (publisher.rs), wired into a tier 1-3 auto-publish path and a new tier 4-6 review queue (pkgwatch review/pkgwatch review <name> --approve).~/.local/share/pacman/custom([custom]in/etc/pacman.conf) rather than one pkgwatch invents. Stops atrepo-add— installing/upgrading (pacman -Syu/pacman -S) stays a manual, deliberate step.Package::binary_nameafter discovering scaleway-cli's real binary isscw, notscaleway-cli(confirmed viapacman -Qlagainst the installedextrapackage) — without it the build would install alongsideextra's package instead of shadowing it.Test plan
cargo make ciclean (fmt, clippy, complexity, coverage, audit) — 69 testsuv(tier 2) auto-built and published against the real astral-sh/uv release with no human stepscaleway-cli(tier 4) queued for review;pkgwatch review scaleway-cli --approvere-verified, built, and published it against a scratch repo — confirmed the built package contains exactlyusr/bin/scwcustomrepo's database;uvwas subsequently installed viapacman -S custom/uvand confirmed working (uv --versionreports0.12.15, matching upstream, vs. extra's stale0.12.10)scaleway-cli's real-repo review is intentionally left pending — that's a human decision, not this PR's🤖 Generated with Claude Code
Implements the last unimplemented pipeline stage from SPEC.md: PKGBUILD generation + makepkg (builder.rs), a post-build version sanity check (sanity.rs), and repo-add publishing (publisher.rs), wired into main.rs for both the tier 1-3 auto-publish path and a new tier 4-6 review queue (`pkgwatch review` / `pkgwatch review <name> --approve`, persisted via state::{load,save,clear}_pending_version, tracked separately from last-published-version since approving one release isn't a standing auto-publish grant for future ones). Publishing targets an existing, already-registered local pacman repo (~/.local/share/pacman/custom, `[custom]` in /etc/pacman.conf) rather than one pkgwatch invents — found already in real use for a hand-packaged AppImage, which resolves SPEC's open question on where the repo lives without pkgwatch ever touching pacman.conf. Publishing stops at `repo-add`; actually installing/upgrading (`pacman -Syu`/`pacman -S`) is left to the operator, not run automatically. Getting a real second package (scaleway-cli, tier 4) through the new pipeline immediately surfaced a real gap: its pacman package is named `scaleway-cli` but the actual binary is `scw` (confirmed via `pacman -Ql` against the currently-installed extra package) — without a way to declare that, the build would install alongside extra's package under the wrong name instead of shadowing it. Added `Package::binary_name` (config.rs) to cover it. Every upstream-controlled string (version, asset name, download URL) is validated before it touches generated shell content in the PKGBUILD template — rejects anything containing a single quote or newline, since values are embedded in single-quoted bash strings. Verified for real, end to end: uv (tier 2) auto-built and published against the real astral-sh/uv release with no human step; scaleway-cli (tier 4) queued for review, then approved via `pkgwatch review scaleway-cli --approve`, which re-verified, built, and published it — confirmed the built package contains exactly usr/bin/scw. Both landed in the real custom repo's database. Left scaleway-cli's real-repo review pending rather than approving it myself: the tier 4-6 gate exists for a human judgment call, not the agent's. 69 tests, cargo make ci clean (fmt, clippy, complexity, coverage, audit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>A single-agent code review of this branch's diff (builder/pipeline/ publisher/sanity/hash + main/config/fetcher/state/verifier changes) found six real issues, all fixed here: - builder.rs: validate_shell_safe only rejected a literal single quote and newline, written for the single-quoted PKGBUILD fields. But asset_name (via install_source) and binary_name land in the install() line, which is necessarily double-quoted so ${srcdir}/${pkgdir} can expand — where $, backtick, and backslash are still live. Not currently exploitable (the one variable component, version, is already independently constrained by validate_pkgver's strict charset), but a latent gap relying on that coincidence rather than the validator actually covering its real use context. Widened the reject-list to cover both quoting styles, added regression tests including one at the generate_pkgbuild level. Corrected SPEC.md's "single-quoted" claim to match. - pipeline.rs: a verification failure returned Ok(()) from process_package, so run_check never counted it as a failure and the process exited 0 even on a failed cryptographic/attestation check — exactly the event a monitoring setup (systemd OnFailure=, cron mail-on-error) most needs a non-zero exit to catch. Now bails, which run_check already treats as a package failure. Added an integration test against a mocked GitHub server exercising this exact path. - builder.rs: find_built_package hardcoded the .pkg.tar.zst suffix, so a box with a different PKGEXT in makepkg.conf would report a false "makepkg failed" for a build that actually succeeded. Widened to match any .pkg.tar.* compression. Added direct unit tests (it had none). - pipeline.rs: a newer tier 4-6 version silently overwrote a still- unreviewed older pending version with no indication anything was superseded. Now says so explicitly. - hash.rs: builder/verifier each read a whole downloaded artifact into memory via std::fs::read just to hash it, doubling peak memory for no reason since the file's already on disk. Added sha256_hex_file, streamed in fixed-size chunks; both callers switched to it. - Deduplicated two near-identical test-only "write an executable shell script" helpers (publisher.rs, sanity.rs) into a shared src/test_support.rs. 75 tests (was 63), cargo make ci clean. Re-verified end to end against the real astral-sh/uv release after all six fixes — build, sanity check, and publish into a scratch repo all still succeed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>Merging master's claude-code.toml onto this branch surfaced a real gap: builder.rs only knew "bare binary download" and "tarball extracting into a same-named directory" (uv). claude-code's tarball extracts a bare `claude` file with no wrapping directory, and that inner filename doesn't match the package name either — makepkg's package() failed with "cannot stat .../claude-linux-x64/claude-code" (confirmed by actually running the build). Add Package::archive_binary_path, an explicit override for the in-archive path builder.rs installs from, used verbatim when present instead of the stem/binary_name convention. Set binary_name = "claude" too, matching the box's actual command name (/opt/claude-code/bin/claude) rather than the claude-code package name. Also added a sanity_check block (claude --version), matching uv's pattern, confirmed against the real built binary's output ("2.1.276 (Claude Code)"). Verified end to end against the real repo with PKGWATCH_REPO_DIR pointed at a scratch dir: check -> fetch -> verify -> review --approve -> build -> sanity-check -> publish all pass for claude-code v2.1.276. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>