gitx: repository access and ref-to-ref diffs on go-git
Implement the git access layer for compare.sr.ht on go-git v5 (no runtime
git binary). Public surface: Open (owner/name validated via core, bare-repo
HEAD check, ErrNotFound on any miss); Refs/DefaultBranch; ResolveCommit/Log/
Parents; Diff/RawDiff/DiffStat/MergeBase/CommitPatch.
- Diffs route through DiffTreeWithOptions(DetectRenames) so old/new tree order
is explicit: three-dot uses the merge base as old side, two-dot uses base;
CommitPatch diffs a commit against its parent (root vs empty tree, merge vs
first parent, ParentSHAs exposed for the banner).
- Patch text is generated in memory then capped (5 MiB page, 50 MiB raw,
injectable override for tests) and cut at a "diff --git" file boundary so the
browser parser never sees a torn hunk; ctx timeout (10s) guards runaways.
- FileChange status/counts/binary derived purely from go-git FilePatches
(mapFilePatches); Log excludes base's full reachable set for correct
base..head semantics.
Tests build fixtures by driving the real git CLI in t.TempDir(). Fidelity gate
(TestPatchFidelity) confirms go-git emits standard git headers — diff --git,
index, rename from/to, "Binary files ... differ", @@ hunks — parseable by the
frontend parsePatchFiles(). 82% coverage.
core validators reject ^/~ so navigation revs are resolved to SHAs in tests.
frontend: pierre diffs/trees bundle and sourcehut theme css
Vendored esbuild bundle (frontend/src/app.ts) rendering @pierre/diffs
FileDiff per file and a @pierre/trees FileTree sidebar against the SSR
#compare-data contract, plus scss/main.scss (base + compare views)
compiled and content-hashed into web/static/.
authz: unified-login identity and git.sr.ht GraphQL authorization
Add the authz package: cookie-derived identity and per-request repository
authorization delegated to git.sr.ht's internal GraphQL API, with no local
database.
- identity.go: UsernameFromRequest decrypts the sr.ht.unified-login.v1 Fernet
cookie to a bare username ("" for anonymous, never rejects); Middleware/
ForContext carry it in the request context.
- authz.go: Authorizer interface + GQLAuthorizer over core-go client.Do. Repo
strips a leading ~ from the owner, maps null user/repository to
core.ErrNotFound (never leaking existence) and transport/GraphQL failures to
a wrapped non-NotFound error. MyRepos paginates me{repositories} to a 500-repo
cap. A mutex-guarded TTL cache memoizes positive and not-found Repo results
(keyed viewer\0owner\0name) but never transport errors, with lazy expiry and
opportunistic sweeps.
- Tests: in-memory config (generated Fernet + ed25519 keys) + httptest server
asserting the Internal auth header decrypts to the expected viewer; covers
cookie round-trip, found/null/500 repo cases, per-viewer cache keying, TTL
expiry, transport-error non-caching, and MyRepos pagination.
compare.sr.ht: project foundation — core package, build scaffolding, core-go fork pin
Bootstrap the go.bigb.es/sourcehut-compare service:
- go.mod (go 1.26.4) pinning core-go to the private fork
git.srht.bigb.es/~bigbes/core-go @ c2c2f3848fa9 via a replace directive;
go.sum populated by a throwaway smoke build importing core-go
config/crypto/client/server + chi + logrus (gates API drift from the
verification baseline fdb3662 to upstream 71b2787).
- core/: pure domain package — sentinel errors, owner/repo/ref validation
(check-ref-format-style, hostile-input hardened), and the compare-spec
grammar (ParseCompareSpec, three-dot/two-dot, percent-unescape). Table
tests at 97% coverage.
- Makefile (all/build/test/css/bundle/run-dev/install), config.example.ini
documenting the shared keys read in place, contrib/ nginx block and
systemd unit, README skeleton, .gitignore.
core-go dep deps and go mod tidy are deferred to later phases per plan.