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_bytes on repositories; new lfs_locks and lfs_objects tables; new lfs_audit_events table.
  • Config: :anvil, :lfs_storage in runtime.exs mirroring git-storage (S3 with lfs/ prefix by default, filesystem fallback for dev).
  • Routes / controller: LfsController exposing batch, transfer, verify, locks endpoints under /:org/:repo_slug_git/info/lfs/*, guarded by the existing GitHttpAuth plug and Authorization module. Actions :lfs_read and :lfs_write mapped to existing :read/:write permissions.
  • Ecto-backed locks: replace the library’s default JSON-blob Locks storage with an Anvil module backed by the lfs_locks table (upstream adds a @behaviour; version bump).
  • Object index: lfs_objects rows written on upload-verified success, transactionally decrementing lfs_used_bytes on delete. mix anvil.lfs.reindex task 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.GcJob removes 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_events records 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 install notice when repo has LFS enabled.
    • First-push banner: when LFS is enabled but lfs_used_bytes = 0, show git lfs track snippet.
    • 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, false default. When false, routes return 404 and UI is hidden.
  • Multi-instance safety: lfs_locks unique (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, full git clone + git lfs pull end-to-end, with PAT auth via the existing GitHttpAuth plug.
  • 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 locks and 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.reindex test 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 status passes.