fix(grant): resolve repo by name via the singular `repo` field (COR-699) · Entire
fix(grant): resolve repo by name via the singular `repo` field (COR-699)
71e6e4c·
toothbrush·2w ago·2 files·+14 added/-7 removed
The control plane returns a name-filtered repo list under the singular
`repo` field (like org/project), not the plural `repos` array — `repos`
is only populated for an unfiltered page. resolveRepoRef read `repos`, so
`grant repo {list,add,remove} ` always 404'd with
"no repo named" even when the repo existed. The fixture mocked the plural
array too, hiding it; it now mirrors the real server's singular field. Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com 0a7fba82dafbView transcript [? 2 cmd/entire/cli Mresolveref.go+7/-5 Mresolveref_test.go+7/-2 Mcmd/entire/cli/resolveref.go+7/-5Sessions
Improve Grant CLI Verb Syntax and HandlingClaude Code·Opus 4.8[1m]·1 step](/content/gh/entireio/cli/session/fbd23c93-1510-47c9-9201-6c325b4f3251#timeline-0a7fba82dafb/index.html)Changes
171 unmodified lines
172
173
174
175
176
177
175
176
177
178
179
180
181
12 unmodified lines
194
195
196
196
197
198
199
200
199
201
202
203
204
171 unmodified lines
// resolveRepoRef turns a repo reference into its ULID. A ULID passes through.
// A name requires a project scope (projectRef, itself a name or ULID) because
// repo names are unique only within a project: the repo is resolved via the
// server's case-insensitive by-name lookup, scoped to that project. Unlike the
// org/project endpoints, the repo list response has no singular field, so a
// name-filtered query returns the single match (or none) under `repos`.
// server's case-insensitive by-name lookup, scoped to that project. Like the
// org/project endpoints, a name-filtered list returns the single match under the
// response's singular \`repo\` field (the plural \`repos\` is only populated for an
// unfiltered page) — reading \`repos\` here was the COR-699 bug.
func resolveRepoRef(ctx context.Context, c *coreapi.Client, ref, projectRef string) (string, error) {
if looksLikeULID(ref) {
return ref, nil
12 unmodified lines
}
return "", err
}
if len(out.Repos) == 0 {
repo, ok := out.Repo.Get()
if !ok {
return "", noRepoNamedErr(ref)
}
return out.Repos[0].ID, nil
return repo.ID, nil
}
func noOrgNamedErr(name string) error {
203 unmodified lines
204
205
206
207
208
207
208
209
210
211
212
213
214
215
216
203 unmodified lines
var gotName string
c, calls := resolveTestClient(t, func(w http.ResponseWriter, r *http.Request) {
gotName = r.URL.Query().Get("name")
if err := writeJSON(w, &coreapi.ListProjectReposOutputBody{Repos: []coreapi.Repo{{ID: ulidRepoWeb, Name: "web"}}}); err != nil {
t.Errorf("encode repos: %v", err)
// A name-filtered list returns the single match under the singular
// \`repo\` field (like org/project) — NOT the plural \`repos\` array,
// which is only populated for an unfiltered page. Reading \`repos\`
// here was the COR-699 bug, so the fixture must mirror the real
// server's singular field to keep that regression covered.
if err := writeJSON(w, &coreapi.ListProjectReposOutputBody{Repo: coreapi.NewOptRepo(coreapi.Repo{ID: ulidRepoWeb, Name: "web"})}); err != nil {
t.Errorf("encode repo: %v", err)
}
})
// Project passed as a ULID so resolveProjectRef short-circuits (no call);