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

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)}
}