The previously-installed claude-code package (2.1.273-1, an AUR build)
wraps its real binary in a /usr/bin/claude script that sets
DISABLE_UPDATES=1 and DISABLE_INSTALLATION_CHECKS=1 before exec-ing
/opt/claude-code/bin/claude — almost certainly to stop Claude Code's own
self-updater from fighting with a package manager already managing it,
which applies just as much to a pkgwatch-managed install. The generated
PKGBUILD had no way to replicate that: it only ever wrote one file.
Add Package::env (a sorted BTreeMap for deterministic output). When set
and non-empty, builder.rs now installs the real binary under
/usr/lib/<pkgname>/ and generates a /usr/bin/<binary_name> wrapper that
exports the declared vars before exec-ing it, written inline via a
quoted heredoc (no bash expansion at PKGBUILD-build time). The wrapper
finds its sibling binary via $(dirname "$0") rather than a hardcoded
absolute path, since /bin/sh is bash on this box and sets $0 to the
full resolved path when found via PATH (confirmed empirically) — so the
same wrapper resolves correctly both under sanity.rs's staging-directory
pkgdir check and after a real pacman install.
Wired claude-code.toml to declare both vars. Verified end to end against
a scratch repo: build succeeds, the sanity check (which now runs through
the wrapper, not the raw binary) passes, and the built package's wrapper
genuinely exports both vars at runtime before exec-ing the real binary
(confirmed by hand, substituting the exec line for an env dump).
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>
Same-origin-sha256 (tier 4) against SHASUMS256.txt — no GitHub
attestations upstream. A PGP signature over the checksum file also
ships (SHASUMS256.txt.sig) but pkgwatch has no minisign/GPG verifier
yet; revisit once tier 1 lands. Tracks the glibc x86_64 Linux build.
Confirmed working end to end: check -> fetch -> verify against the
real repo (v2.1.276 at time of writing).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Wire up scaleway/scaleway-cli as a watched package (extra's copy lags
weeks behind upstream). Getting a real second package running exposed
gaps uv's config never hit:
- asset_pattern/checksum_asset_pattern now support a {version}
placeholder for release assets that embed the version in the
filename (checker::version_from_tag strips a tag's leading `v`).
- same-origin-sha256 now matches the checksum line by filename instead
of assuming a single-hash file, to support combined multi-asset
checksum files like scaleway-cli's SHA256SUMS.
- latest_github_release now confirms each Atom-feed candidate against
the releases API instead of trusting the newest entry outright —
scaleway-cli publishes a `-dbg1` tag with no real Release object that
otherwise sorts newest in the feed.
Confirmed correct against the real repo: no attestations upstream, so
tier 4 (same-origin-sha256) applies, and the checker correctly skips
the dbg1 tag to land on the real latest release.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rust PoC (cargo, packages.d/*.toml config) that checks astral-sh/uv's
GitHub Atom feed for a new release, fetches the matching asset via the
GitHub API, and verifies it. Confirmed live against the real repo: uv
actually ships GitHub build-provenance attestations (sigstore bundle) on
every release, so it's a tier-2 package, not the tier-4 same-origin-sha256
guessed in the original spec draft. Verified via `gh attestation verify`
rather than reimplementing sigstore in Rust. State persists across runs so
a second run correctly reports "up to date."
Also folds the finding back into SPEC.md: updates the uv example to
tier 2, derives tier from verification method instead of storing both
(avoids a tier/method mismatch that would mean nothing), marks the
packages.d/ layout question resolved, and updates Architecture/Status to
say what's actually implemented vs. still sketch (build/publish/review
queue, same-origin-sha256 against a live repo, scheduling, non-GitHub
sources, minisign are all still open).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2FEut5tVMNjeVjqhgVZbr