repo mirror list: rename REPO column and --repo flag to NAME · Entire
repo mirror list: rename REPO column and --repo flag to NAME
570854e→main·
gtrrz-victor·1w ago·2 files·+83 added/-53 removed
Under the entire repo mirror group, the column header REPO and the
--repo filter stutter against the group noun. Rename the column to
NAME (owner/repo) (inline hint spells out the composite value) and the
filter to --name. --name still matches the whole owner/repo string.
parseSortColumn now accepts a column header with its trailing
parenthetical hint stripped, so the friendly --sort name resolves the
NAME column alongside the full --sort "name (owner/repo)".
--json output is unchanged: it serializes the raw coreapi model, not the column headers, so the wire field stays "repo" and scripts don't break.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
01KX0M0EKRCW08N4FDCHTCDPR8View transcript
Changes
2
cmd/entire/cli
Mrepo_mirror.go+49/-29
Mrepo_mirror_test.go+34/-24
20 unmodified lines
// Column header names, the single source of truth for both the table headers
// (mirrorColumns/availableMirrorColumns) and the --sort key switches. parseSort-
// Column returns the canonical header it matched, so the sort switches compare
// against these constants directly.
const (
colRepo = "REPO"
colName = "NAME (owner/repo)"
colCloneURL = "CLONE URL"
colPrivate = "PRIVATE"
colAccess = "ACCESS"
)
// owner/repo name, the clone URL you'd copy, and whether the upstream is
// private. Owner, provider, and cluster aren't columns of their own — they're
// inferable from the owner/repo pair and the clone URL
// (entire://<cluster>/gh/<owner>/<repo>). `--repo` filters on the repo name
// only; owner/provider/cluster stay server-side filters, and the wire model's
// internal ids are dropped. The clone URL is synthesised from the mirror's
// coords (the form `git clone` accepts), since the list API doesn't return it.
var mirrorColumns = []string{colRepo, colCloneURL, colPrivate}
// mirrorPrivate renders the PRIVATE column ("yes"/"no"), shared by the table
// row and the --sort private key so both agree on the cell value.
// parenHint matches a trailing " (...)", qualifier on a column header, e.g. the
// "(owner/repo)" in "NAME (owner/repo)". Stripping it yields the friendly short
// name a caller can type for --sort.
var parenHint = regexp.MustCompile(`\s*\([^)]*\)$`)
// sortKeyOf returns the header with its trailing parenthetical hint removed,
// i.e. the short name accepted by --sort ("NAME (owner/repo)" -> "NAME").
func sortKeyOf(header string) string {
return parenHint.ReplaceAllString(header, "")
}
// parseSortColumn resolves a --sort spec to the canonical column header it
// names (one of the columns entries) and a direction. It trims first, then
// reads the '-' prefix, so leading/trailing whitespace is handled identically
// on every path (the direction and the column name never disagree). An empty
// spec selects the first column. An unknown name errors naming the valid
// columns. Returning the matched header lets callers switch on the col*
// constants directly.
func parseSortColumn(spec string, columns []string) (col string, desc bool, err error) {
spec = strings.TrimSpace(spec)
desc = strings.HasPrefix(spec, "-" )
return columns[0], desc, nil
}
for _, h := range columns {
if strings.EqualFold(h, name) {
if strings.EqualFold(h, name) || strings.EqualFold(sortKeyOf(h), name) {
return h, desc, nil
}
}
}
// sortMirrors orders mirrors in place by the --sort spec: by the named column's
// value ascending (case-insensitive), always breaking ties by owner/repo then
// cluster host so a repo mirrored across clusters (or rows equal on any other
// column) has a stable, deterministic order rather than arbitrary server order.
// A '-' prefix reverses the whole ordering.
func sortMirrors(mirrors []coreapi.Mirror, spec string) error {
col, desc, err := parseSortColumn(spec, mirrorColumns)
return nil
}
// filterByRepo keeps items whose repo identifier contains substr (case-//
filterByName keeps items whose owner/repo name contains substr (case-
// insensitive). The control plane already filters by owner/provider/cluster
// server-side but not by repo name, so `repo mirror list --repo` narrows that
// last dimension client-side. repoOf returns the item's displayed identifier —
// the callers pass the owner/repo form shown in the REPO column, so a value
// copied from the table (e.g. acme/web) matches the row it came from.
// An empty substr returns items unchanged.
func filterByRepo[T any](items []T, repoOf func(T) string, substr string) []T {
// server-side but not by name, so `repo mirror list --name` narrows that last
// dimension client-side. nameOf returns the item's displayed identifier —
// the callers pass the owner/repo form shown in the NAME column, so a value
// copied from the table (e.g. acme/web) matches the row it came from.
// An empty substr returns items unchanged.
func filterByName[T any](items []T, nameOf func(T) string, substr string) []T {
substr = strings.TrimSpace(substr)
if substr == "" {
return items
}
substr = strings.ToLower(substr)
out := make([]T, 0, len(items))
for _, it := range items {
if strings.Contains(strings.ToLower(repoOf(it)), substr) {
if strings.Contains(strings.ToLower(nameOf(it)), substr) {
out = append(out, it)
}
}
}
return out
}
// clone URL), or "owner-only" (a personal repo of another user; only its
// owner may mirror it). No clone URL column: an un-onboarded repo doesn't
// have one yet.
var availableMirrorColumns = []string{colRepo, colAccess, colStatus}
var availableMirrorColumns = []string{colName, colAccess, colStatus}
func availableMirrorRow(m coreapi.AvailableMirror) []string {
return []string{m.Owner + "/" + m.Repo, string(m.Access), string(m.Status)}
}