Conversation
Code Review Completed! 🔥The code review was successfully completed based on your current configurations. Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
The publishTip GORM update in session_store_sia.go runs without Kody rule violation: Disallow GORM queries without timeout |
| store := gitremote.NewSessionStoreFromSession(session).ForLocker(bind.Locker) | ||
| remoteRefs, err := store.List(ctx, false) | ||
| if err != nil { | ||
| return fmt.Errorf("git status: list archive refs: %w", err) | ||
| } |
There was a problem hiding this comment.
The git status command fails unconditionally when store.List returns ErrSiaNotWired, preventing the local/archive comparison in builds without the gitarchive_sia tag. Mirror the sibling handling in git_show.go:83 and git_doctor.go:117 by checking errors.Is(err, gitremote.ErrSiaNotWired) and treating the remote as empty instead of returning the error.
remoteRefs, err := store.List(ctx, false)
if err != nil && !errors.Is(err, gitremote.ErrSiaNotWired) {
return fmt.Errorf("git status: list archive refs: %w", err)
}Prompt for LLM
File internal/cli/git_status.go:
Line 71 to 75:
The `git status` command fails unconditionally when `store.List` returns ErrSiaNotWired, preventing the local/archive comparison in builds without the `gitarchive_sia` tag. Mirror the sibling handling in git_show.go:83 and git_doctor.go:117 by checking `errors.Is(err, gitremote.ErrSiaNotWired)` and treating the remote as empty instead of returning the error.
Suggested Code:
remoteRefs, err := store.List(ctx, false)
if err != nil && !errors.Is(err, gitremote.ErrSiaNotWired) {
return fmt.Errorf("git status: list archive refs: %w", err)
}
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.
| chosen, err := resolveLocker(cc, session, abs, key) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Record the locker (git_repos) and the bind (git_binds) before touching | ||
| // the archive remote so a later failure still leaves a recoverable state. | ||
|
|
||
| if _, err := gitarchive.EnsureRepo(session.DB, chosen, gitarchive.DefaultLockerName(abs), key); err != nil { | ||
| return err | ||
| } | ||
| bind, err := gitarchive.AddBind(session.DB, chosen, abs) | ||
| if err != nil { | ||
| return err | ||
| } |
There was a problem hiding this comment.
AddBind upserts by locker with a unique index, so watching a second checkout with the same root-commit lineage overwrites the first checkout's RepoPath, orphaning the original bind and leaving it reported as "not bound to a git archive." Key the bind row on repo_path with locker as a non-unique column, and adjust AddBind/FindBindByRepo/DeleteBind to use repo path lookups.
// key the bind row on repo_path so multiple checkouts can share a locker:
func AddBind(db *gorm.DB, locker, repoPath string) (GitBind, error) {
bind := GitBind{Locker: locker, Remote: RemoteName, RepoPath: repoPath}
err := db.Where("repo_path = ?", repoPath).First(&existing).Error
if err == nil {
existing.Locker = locker
existing.Remote = RemoteName
...Prompt for LLM
File internal/cli/git_watch.go:
Line 88 to 102:
AddBind upserts by locker with a unique index, so watching a second checkout with the same root-commit lineage overwrites the first checkout's RepoPath, orphaning the original bind and leaving it reported as "not bound to a git archive." Key the bind row on repo_path with locker as a non-unique column, and adjust AddBind/FindBindByRepo/DeleteBind to use repo path lookups.
Suggested Code:
// key the bind row on repo_path so multiple checkouts can share a locker:
func AddBind(db *gorm.DB, locker, repoPath string) (GitBind, error) {
bind := GitBind{Locker: locker, Remote: RemoteName, RepoPath: repoPath}
err := db.Where("repo_path = ?", repoPath).First(&existing).Error
if err == nil {
existing.Locker = locker
existing.Remote = RemoteName
...
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.
| if len(data) > 0 { | ||
| uo, err := gitarchive.UploadPack(ctx, s.session, s.locker, | ||
| fmt.Sprintf("%s:%s:%d", dstRef, srcHash, gen), false, bytes.NewReader(data)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| newPacks = append(newPacks, gitarchive.PackRef{Key: uo.Key.String(), Full: false, Digest: uo.Digest}) | ||
| } | ||
|
|
||
| // Gen CAS: publish must not move a generation that changed after we read it. | ||
| if err := s.casPublish(ctx, oldKey); err != nil { | ||
| return err |
There was a problem hiding this comment.
SessionStore.Upload persists the pack to Sia and records a git_objects row before checking the CAS in casPublish, leaving orphaned objects and stale DB rows when the CAS rejects a concurrent publish. Move the casPublish check before the upload so the pack and row are only persisted after the CAS passes.
if err := s.casPublish(ctx, oldKey); err != nil {
return err
}
if len(data) > 0 {
uo, err := gitarchive.UploadPack(ctx, s.session, s.locker, ...)
if err != nil { return err }
newPacks = append(newPacks, gitarchive.PackRef{...})
}Prompt for LLM
File internal/cli/gitremote/session_store_sia.go:
Line 147 to 158:
SessionStore.Upload persists the pack to Sia and records a git_objects row before checking the CAS in casPublish, leaving orphaned objects and stale DB rows when the CAS rejects a concurrent publish. Move the casPublish check before the upload so the pack and row are only persisted after the CAS passes.
Suggested Code:
if err := s.casPublish(ctx, oldKey); err != nil {
return err
}
if len(data) > 0 {
uo, err := gitarchive.UploadPack(ctx, s.session, s.locker, ...)
if err != nil { return err }
newPacks = append(newPacks, gitarchive.PackRef{...})
}
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.
d9a2411 to
0235ad2
Compare
Code Coverage ReportTotal Coverage: 55.2% Generated from commit: b1f087d |
Adds a Git archive workflow to pinner that binds local repositories, publishes
pushed refs as encrypted Sia objects, and serves them back through a git remote
helper.
Adds
pinner gitsubcommands for watch, status, ls, show, unwatch, and doctor,with an optional post-push hook that publishes refs on every push.
This pull request adds Git archive hosting on Sia, enabling users to back up and share Git repositories through the
pinnerCLI. The implementation introduces a full Git remote-helper protocol (via agit-remote-pinnerentry point) plus a newpinner gitcommand tree for managing archives.Key Additions
Git archive domain (
internal/core/gitarchive)git_repos,git_objects,git_binds).internal/core/objmeta): a card schema for non-vault Sia objects (git.pack,git.tip,git.share) that deliberately fails the vault file parser to avoid vault sync interference.archiveremote, and installs/removes a post-push hook.Remote-helper protocol (
internal/cli/gitremote)pinnerdispatches by argv‑0:git-remote-pinnerruns the remote helper;pinnerruns the CLI.capabilities,list,fetch, andpushusing go‑git in-process pack encoding/ingestion.Storeinterface with three implementations: a go‑git bare repo (for testing), a session‑backed store with a real Sia backend behind a build tag, and a read‑only share store serving pre‑signed URLs without a profile.pinner gitCLI commandswatch– bind a repository to a locker and publish all local branches.status– compare local branches against archived refs.ls– list known lockers.show– browse archived refs, commits, and file trees via go‑git.share– mint pre‑signed bearer URLs for the archive tip and packs.unwatch– unbind a repository, removing its remote and locker registration.doctor– diagnose and repair bindings, remotes, and object state.Testing
Extensive unit tests cover the protocol engine, go‑git store round‑trips, share URL flows, digest verification, generation-based concurrency control (CAS), and CLI command registration.
The feature is designed to operate incrementally: the real Sia backend is compiled in only with the
gitarchive_siabuild tag; without it, data operations return a clear "Sia backend not wired" sentinel.