fangorn/ex_git_objectstore
public
ref:main
feat: full-featured Git LFS integration #58
closed
Opened by cole.christensen@gmail.com
Links
No links yet.
Integrate the newly-merged LFS support in ex_git_objectstore as a full-featured Anvil feature. Single PR covering the backend, Ecto-backed locks, UI, quota, GC, telemetry, and audit log, gated behind config :anvil, :lfs_enabled.
In scope
- Schema:
lfs_enabled,lfs_storage_mode,lfs_quota_bytes,lfs_used_bytesonrepositories; newlfs_locksandlfs_objectstables; newlfs_audit_eventstable. - Config:
:anvil, :lfs_storageinruntime.exsmirroring git-storage (S3 withlfs/prefix by default, filesystem fallback for dev). - Routes / controller:
LfsControllerexposing batch, transfer, verify, locks endpoints under/:org/:repo_slug_git/info/lfs/*, guarded by the existingGitHttpAuthplug andAuthorizationmodule. Actions:lfs_readand:lfs_writemapped to existing:read/:writepermissions. - Ecto-backed locks: replace the library’s default JSON-blob Locks storage with an Anvil module backed by the
lfs_lockstable (upstream adds a@behaviour; version bump). - Object index:
lfs_objectsrows written on upload-verified success, transactionally decrementinglfs_used_byteson delete.mix anvil.lfs.reindextask rebuilds from S3. - Quota: pre-flight check in batch handler returns 507 on over-quota. Banner on repo page when near/over.
- GC: periodic
Anvil.Lfs.GcJobremoves orphaned objects (not referenced by any pointer in any reachable commit, older than N days). - Telemetry: subscribe to
[:ex_git_objectstore, :lfs, :*]spans, emit metrics for uploads/downloads/lock ops, quota pressure. - Audit log:
lfs_audit_eventsrecords upload (ok / oid_mismatch / quota_exceeded), lock create / force-unlock. Retained 90 days. Admin page to browse. - UI (LiveView)
RepoLive.Settings: LFS toggle, quota bar, orphaned-objects indicator + “Run GC now” button.RepoLive.Lfs.Locks: list, filter, force-unlock (admin), PubSub live updates.- Org dashboard: aggregate LFS usage per repo.
- Diff/commit/PR views: LFS-pointer-aware rendering — shows “changed from … (N MB) to … (M MB)” with download links, not the pointer-blob diff.
- File viewer / blob page: resolves pointer, proxies or 302-redirects to a signed download URL, renders images/videos inline when renderable.
- Clone panel: shows
git lfs installnotice when repo has LFS enabled. - First-push banner: when LFS is enabled but
lfs_used_bytes = 0, showgit lfs tracksnippet. - Quota error banner: surfaces quota-exceeded state independently of git push output.
- Lock-conflict warning: PR/commit pages flag paths currently locked by another user.
- Feature flag:
config :anvil, :lfs_enabled, falsedefault. When false, routes return 404 and UI is hidden. - Multi-instance safety:
lfs_locksunique(repository_id, path)serializes concurrent lock creation via DB.UPDATE ... SET lfs_used_bytes = lfs_used_bytes + ?for counter. Documented expiry windows on presigned URLs.
Out of scope (v1)
- Web-based file upload
- Custom transfer adapters (Lua/WASM hooks)
- Webhook events for LFS operations
- Billing integration
- Anonymous download rate-limiting UI
- Per-file access controls beyond repo-level
Acceptance criteria
- Full interop passes: real
git lfs push/pull,git lfs lock/unlock/locks, fullgit clone+git lfs pullend-to-end, with PAT auth via the existingGitHttpAuthplug. - Over-quota push is rejected with 507 and a banner on the repo page.
- Lock created by user A is visible to user B’s
git lfs locksand blocks user B’s unlock without--force. - Admin force-unlock works via UI and via
git lfs unlock --force. - Audit log captures at least: upload (ok/rejected), lock create, lock force-unlock.
- Repo-settings page LiveView test covers toggle, quota edit, GC trigger.
- Blob-viewer test covers rendering an LFS-pointer-backed image inline.
- GC job test covers orphan detection and reclamation.
mix anvil.lfs.reindextest covers rebuild from an S3 state with out-of-sync counters.- Feature flag off → all LFS routes 404 and UI elements hidden; all non-LFS tests still pass.
anvil requirement statuspasses.