Skip to content

Commit fb0c64a

Browse files
Repair missing repo records from config
Co-authored-by: Andrew <andrewxhill@gmail.com>
1 parent 6bc46c2 commit fb0c64a

3 files changed

Lines changed: 113 additions & 3 deletions

File tree

internal/app/publish.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package app
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"flag"
78
"fmt"
89
"io"
@@ -126,10 +127,35 @@ func loadRepoContext(repoPath string) (git.RepositoryLayout, string, policy.File
126127
return git.RepositoryLayout{}, "", policy.File{}, state.RepositoryRecord{}, state.Store{}, err
127128
}
128129

129-
repoRecord, err := store.GetRepositoryByPath(context.Background(), repoRoot)
130+
repoRecord, _, err := ensureRepositoryRecord(context.Background(), store, repoRoot, cfg)
130131
if err != nil {
131132
return git.RepositoryLayout{}, "", policy.File{}, state.RepositoryRecord{}, state.Store{}, err
132133
}
133134

134135
return layout, repoRoot, cfg, repoRecord, store, nil
135136
}
137+
138+
func ensureRepositoryRecord(ctx context.Context, store state.Store, repoRoot string, cfg policy.File) (state.RepositoryRecord, bool, error) {
139+
record, err := store.GetRepositoryByPath(ctx, repoRoot)
140+
if err == nil {
141+
return record, false, nil
142+
}
143+
if !errors.Is(err, state.ErrNotFound) {
144+
return state.RepositoryRecord{}, false, err
145+
}
146+
if cfg.Repo.ProtectedBranch == "" {
147+
return state.RepositoryRecord{}, false, fmt.Errorf("repository state exists at %s but no repo record matches %s; run `mq repo init --repo %s` to repair it", store.Path, repoRoot, repoRoot)
148+
}
149+
150+
record, err = store.UpsertRepository(ctx, state.RepositoryRecord{
151+
CanonicalPath: repoRoot,
152+
ProtectedBranch: cfg.Repo.ProtectedBranch,
153+
RemoteName: cfg.Repo.RemoteName,
154+
MainWorktree: cfg.Repo.MainWorktree,
155+
PolicyVersion: "v1",
156+
})
157+
if err != nil {
158+
return state.RepositoryRecord{}, false, err
159+
}
160+
return record, true, nil
161+
}

internal/app/repo.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ Flags:
271271
ctx := context.Background()
272272
var record state.RepositoryRecord
273273
if store.Exists() {
274-
if found, err := store.GetRepositoryByPath(ctx, repoRoot); err == nil {
274+
if found, _, err := ensureRepositoryRecord(ctx, store, repoRoot, cfg); err == nil {
275275
record = found
276276
}
277277
}
@@ -569,7 +569,7 @@ Flags:
569569
var repoRecord state.RepositoryRecord
570570
var hasRepoRecord bool
571571
if store.Exists() {
572-
if record, err := store.GetRepositoryByPath(ctx, repoRoot); err == nil {
572+
if record, _, err := ensureRepositoryRecord(ctx, store, repoRoot, cfg); err == nil {
573573
repoRecord = record
574574
hasRepoRecord = true
575575
count, err := store.CountUnfinishedItems(ctx, record.ID)

internal/app/repo_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package app
33
import (
44
"bytes"
55
"context"
6+
"database/sql"
67
"encoding/json"
78
"os"
89
"os/exec"
@@ -55,6 +56,50 @@ func TestRepoInitAndShow(t *testing.T) {
5556
}
5657
}
5758

59+
func TestRepoShowRepairsMissingRepositoryRecord(t *testing.T) {
60+
repoRoot, worktreePath := createTestRepo(t)
61+
62+
var initOut bytes.Buffer
63+
var initErr bytes.Buffer
64+
if err := runRepoInit([]string{"--repo", repoRoot, "--main-worktree", worktreePath}, &initOut, &initErr); err != nil {
65+
t.Fatalf("runRepoInit returned error: %v", err)
66+
}
67+
68+
layout, err := git.DiscoverRepositoryLayout(repoRoot)
69+
if err != nil {
70+
t.Fatalf("DiscoverRepositoryLayout: %v", err)
71+
}
72+
statePath := state.DefaultPath(layout.GitDir)
73+
db, err := sql.Open("sqlite", statePath)
74+
if err != nil {
75+
t.Fatalf("sql.Open: %v", err)
76+
}
77+
defer db.Close()
78+
if _, err := db.Exec(`DELETE FROM repositories`); err != nil {
79+
t.Fatalf("DELETE repositories: %v", err)
80+
}
81+
82+
var showOut bytes.Buffer
83+
var showErr bytes.Buffer
84+
if err := runRepoShow([]string{"--repo", repoRoot}, &showOut, &showErr); err != nil {
85+
t.Fatalf("runRepoShow returned error: %v", err)
86+
}
87+
88+
output := showOut.String()
89+
if !strings.Contains(output, "State path: "+statePath) {
90+
t.Fatalf("expected state path after record repair, got %q", output)
91+
}
92+
93+
store := state.NewStore(statePath)
94+
record, err := store.GetRepositoryByPath(context.Background(), layout.RepositoryRoot)
95+
if err != nil {
96+
t.Fatalf("GetRepositoryByPath after repair: %v", err)
97+
}
98+
if record.CanonicalPath != layout.RepositoryRoot {
99+
t.Fatalf("expected repaired canonical path %q, got %+v", layout.RepositoryRoot, record)
100+
}
101+
}
102+
58103
func TestRepoInitSupportsJSONOutput(t *testing.T) {
59104
repoRoot, worktreePath := createTestRepo(t)
60105

@@ -115,6 +160,45 @@ func TestDoctorDetectsDirtyProtectedBranch(t *testing.T) {
115160
}
116161
}
117162

163+
func TestDoctorRepairsMissingRepositoryRecord(t *testing.T) {
164+
repoRoot, worktreePath := createTestRepo(t)
165+
166+
var initOut bytes.Buffer
167+
var initErr bytes.Buffer
168+
if err := runRepoInit([]string{"--repo", repoRoot, "--main-worktree", worktreePath}, &initOut, &initErr); err != nil {
169+
t.Fatalf("runRepoInit returned error: %v", err)
170+
}
171+
172+
layout, err := git.DiscoverRepositoryLayout(repoRoot)
173+
if err != nil {
174+
t.Fatalf("DiscoverRepositoryLayout: %v", err)
175+
}
176+
statePath := state.DefaultPath(layout.GitDir)
177+
db, err := sql.Open("sqlite", statePath)
178+
if err != nil {
179+
t.Fatalf("sql.Open: %v", err)
180+
}
181+
defer db.Close()
182+
if _, err := db.Exec(`DELETE FROM repositories`); err != nil {
183+
t.Fatalf("DELETE repositories: %v", err)
184+
}
185+
186+
var doctorOut bytes.Buffer
187+
var doctorErr bytes.Buffer
188+
if err := runDoctor([]string{"--repo", repoRoot}, &doctorOut, &doctorErr); err != nil {
189+
t.Fatalf("runDoctor returned error: %v", err)
190+
}
191+
192+
store := state.NewStore(statePath)
193+
record, err := store.GetRepositoryByPath(context.Background(), layout.RepositoryRoot)
194+
if err != nil {
195+
t.Fatalf("GetRepositoryByPath after doctor repair: %v", err)
196+
}
197+
if record.CanonicalPath != layout.RepositoryRoot {
198+
t.Fatalf("expected repaired canonical path %q, got %+v", layout.RepositoryRoot, record)
199+
}
200+
}
201+
118202
func TestDoctorDetectsMissingCanonicalWorktree(t *testing.T) {
119203
repoRoot, _ := createTestRepo(t)
120204
missingWorktree := filepath.Join(repoRoot, "missing-main-worktree")

0 commit comments

Comments
 (0)