Skip to content

Commit f97b53b

Browse files
gtrrz-victorclaude
andcommitted
polish(repo): VISIBILITY column with audience colors, record labels in header yellow
PRIVATE yes/no becomes VISIBILITY Public/Private on `mirror list` and the ULID `get` view (--sort key: visibility). The value carries an audience color shared by list and record: Public green (openly reachable), Private magenta (restricted, distinct from the status colors sharing its rows). The `get <owner/repo>` record's section labels (Name:, Visibility:, Access:) switch from dim to the same yellow as the table headers below them, so the record reads as one styled document. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 01KY7A0AQ2728AYXQRDE3RAC07
1 parent ba533b8 commit f97b53b

2 files changed

Lines changed: 74 additions & 50 deletions

File tree

cmd/entire/cli/repo_mirror.go

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ type column struct {
3434
// Keys are lower-case, single shell tokens (kebab-case for multi-word columns)
3535
// so `--sort clone-url` needs no quoting; headers stay upper-case display text.
3636
var (
37-
colName = column{key: "name", header: "NAME (owner/repo)"}
38-
colCloneURL = column{key: "clone-url", header: "CLONE URL"}
39-
colClusters = column{key: "clusters", header: "CLUSTERS"}
40-
colPrivate = column{key: "private", header: "PRIVATE"}
41-
colAccess = column{key: "access", header: "ACCESS"}
42-
colStatus = column{key: "status", header: "STATUS"}
37+
colName = column{key: "name", header: "NAME (owner/repo)"}
38+
colCloneURL = column{key: "clone-url", header: "CLONE URL"}
39+
colClusters = column{key: "clusters", header: "CLUSTERS"}
40+
colVisibility = column{key: "visibility", header: "VISIBILITY"}
41+
colAccess = column{key: "access", header: "ACCESS"}
42+
colStatus = column{key: "status", header: "STATUS"}
4343
)
4444

4545
// columnHeaders is the display-header view of a column set, for the table/field
@@ -61,18 +61,19 @@ func columnHeaders(cols []column) []string {
6161
// model's internal ids are dropped. The clone URL is synthesised from the
6262
// mirror's coords (the form `git clone` accepts), since the list API doesn't
6363
// return it.
64-
var mirrorColumns = []column{colName, colCloneURL, colPrivate}
64+
var mirrorColumns = []column{colName, colCloneURL, colVisibility}
6565

66-
// mirrorPrivate renders the PRIVATE column ("yes"/"no") for a `get` mirror,
67-
// sharing yesNo with the `list` directory row so both agree on the cell value.
68-
func mirrorPrivate(m coreapi.Mirror) string {
69-
return yesNo(m.IsPrivate.Or(false))
66+
// mirrorVisibility renders the VISIBILITY column for a `get` mirror, sharing
67+
// visibilityDisplay with the `list` directory row so both agree on the cell
68+
// value.
69+
func mirrorVisibility(m coreapi.Mirror) string {
70+
return visibilityDisplay(m.IsPrivate.Or(false))
7071
}
7172

7273
func mirrorRow(m coreapi.Mirror) []string {
7374
repo := m.Owner + "/" + m.Repo
7475
cloneURL := mirrorCloneURL(m.ClusterHost, m.Owner, m.Repo)
75-
return []string{repo, cloneURL, mirrorPrivate(m)}
76+
return []string{repo, cloneURL, mirrorVisibility(m)}
7677
}
7778

7879
// parseSortColumn resolves a --sort spec to the column it names and a
@@ -103,12 +104,12 @@ func parseSortColumn(spec string, columns []column) (col column, desc bool, err
103104

104105
// repoDirColumns is the merged `repo mirror list` view: existing mirrors and
105106
// onboardable GitHub candidates in one table, from GET /repos?scope=all. NAME
106-
// and PRIVATE come from every row; CLUSTERS and the placement STATUS are
107+
// and VISIBILITY come from every row; CLUSTERS and the placement STATUS are
107108
// onboarded-only; ACCESS is candidate-only. Sparse cells render as "-".
108109
// Per-placement detail (clone URLs, per-cluster status) lives one step down,
109110
// in `repo mirror get <owner/repo>` — a directory this size stays one row per
110111
// repo, not one per placement.
111-
var repoDirColumns = []column{colName, colClusters, colPrivate, colStatus, colAccess}
112+
var repoDirColumns = []column{colName, colClusters, colVisibility, colStatus, colAccess}
112113

113114
// repoDirPlacement is one GitHub-mirror placement of a directory row's repo.
114115
// CloneURL is omitted from JSON when the placement's cluster host couldn't be
@@ -151,11 +152,11 @@ func repoDirClusters(r repoDirRow) string {
151152
}
152153

153154
func repoDirCells(r repoDirRow) []string {
154-
return []string{r.Repo, orDash(repoDirClusters(r)), yesNo(r.Private), r.Status, orDash(r.Access)}
155+
return []string{r.Repo, orDash(repoDirClusters(r)), visibilityDisplay(r.Private), r.Status, orDash(r.Access)}
155156
}
156157

157158
// repoDirCellsStyled wraps repoDirCells with trail-list-style cell coloring:
158-
// clusters/access cyan, private gray, status by lifecycle (see
159+
// clusters/access cyan, visibility by audience, status by lifecycle (see
159160
// repoStatusColor); NAME stays the terminal's default foreground as the
160161
// primary identifier. Cells are pre-colored and the table renderer measures
161162
// widths with lipgloss.Width (ANSI-agnostic), so color never shifts columns.
@@ -171,7 +172,7 @@ func repoDirCellsStyled(st statusStyles) func(repoDirRow) []string {
171172
if cells[1] != "-" {
172173
cells[1] = st.render(st.cyan, cells[1])
173174
}
174-
cells[2] = st.render(st.gray, cells[2])
175+
cells[2] = st.render(visibilityColor(st, r.Private), cells[2])
175176
if style, ok := repoStatusColor(st, r.Status); ok {
176177
cells[3] = st.render(style, cells[3])
177178
}
@@ -223,11 +224,24 @@ func orDash(s string) string {
223224
return s
224225
}
225226

226-
func yesNo(b bool) string {
227-
if b {
228-
return "yes"
227+
// visibilityDisplay renders the VISIBILITY cell (and the `get` record's
228+
// Visibility section): the repo's audience in GitHub's terms, not a yes/no.
229+
func visibilityDisplay(private bool) string {
230+
if private {
231+
return "Private"
229232
}
230-
return "no"
233+
return "Public"
234+
}
235+
236+
// visibilityColor maps a visibility value to its color: Public green (openly
237+
// reachable), Private magenta (restricted — the accent, distinct from every
238+
// status color that shares a row with it). Shared by the list column and the
239+
// `get` record so the same value always looks the same.
240+
func visibilityColor(st statusStyles, private bool) lipgloss.Style {
241+
if private {
242+
return st.magenta
243+
}
244+
return st.green
231245
}
232246

233247
// clusterHostBySlug maps each cluster's slug to the validated bare host of its
@@ -321,8 +335,8 @@ func sortRepoDir(rows []repoDirRow, spec string) error {
321335
switch col {
322336
case colClusters:
323337
return strings.ToLower(repoDirClusters(r))
324-
case colPrivate:
325-
return yesNo(r.Private)
338+
case colVisibility:
339+
return strings.ToLower(visibilityDisplay(r.Private))
326340
case colStatus:
327341
return strings.ToLower(r.Status)
328342
case colAccess:
@@ -1129,20 +1143,25 @@ func mirrorRepoDetailRow(e coreapi.RepoIndexEntry, hostBySlug map[string]string)
11291143
return row
11301144
}
11311145

1132-
// renderRepoDetail prints the `get <owner/repo>` record: bold repo name,
1133-
// dim-labeled identity fields, then the placements table — cluster cyan,
1134-
// clone URL the default foreground (it is the payload of this view), status
1135-
// by lifecycle. A candidate (no placements, availability in Status) states
1136-
// its availability instead of an empty table; a native-only repo states it
1137-
// has no GitHub mirrors.
1146+
// renderRepoDetail prints the `get <owner/repo>` record as labeled sections —
1147+
// the label line in the same yellow as the table headers below it, the value
1148+
// indented beneath — then the placements table: cluster cyan, clone URL the
1149+
// default foreground (it is the payload of this view), status by lifecycle.
1150+
// Visibility carries its audience color (Public green, Private magenta),
1151+
// matching the list's VISIBILITY column. A candidate (no placements,
1152+
// availability in Status) states its availability instead of an empty table;
1153+
// a native-only repo states it has no GitHub mirrors.
11381154
func renderRepoDetail(w io.Writer, row repoDirRow) {
11391155
st := newStatusStyles(w)
1140-
fmt.Fprintln(w, st.render(st.bold, row.Repo))
1141-
fields := []explainRow{{Label: "private", Value: yesNo(row.Private)}}
1156+
section := func(label, value string) {
1157+
fmt.Fprintln(w, st.render(st.yellow, label+":"))
1158+
fmt.Fprintf(w, " %s\n", value)
1159+
}
1160+
section("Name", st.render(st.bold, row.Repo))
1161+
section("Visibility", st.render(visibilityColor(st, row.Private), visibilityDisplay(row.Private)))
11421162
if row.Access != "" {
1143-
fields = append(fields, explainRow{Label: "access", Value: row.Access})
1163+
section("Access", row.Access)
11441164
}
1145-
fmt.Fprint(w, st.metadataRows(fields))
11461165
fmt.Fprintln(w)
11471166

11481167
if len(row.Placements) == 0 {

cmd/entire/cli/repo_mirror_test.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -565,11 +565,11 @@ func TestRepoMirrorList_Merged(t *testing.T) {
565565
require.Equal(t, testReposPath, rec.path)
566566
require.Equal(t, "all", rec.query.Get("scope"), "list must request the unified directory")
567567
require.Contains(t, stderr, "Listing repos on")
568-
for _, h := range []string{"NAME", "CLUSTERS", "PRIVATE", "STATUS", "ACCESS"} {
568+
for _, h := range []string{"NAME", "CLUSTERS", "VISIBILITY", "STATUS", "ACCESS"} {
569569
require.Contains(t, stdout, h)
570570
}
571571
// Mirror row: cluster slug + clone status, access dashed.
572-
require.Regexp(t, `acme/web\s+us\s+yes\s+ready`, stdout)
572+
require.Regexp(t, `acme/web\s+us\s+Private\s+ready`, stdout)
573573
// Candidate rows: availability status + access, clusters dashed.
574574
require.Contains(t, stdout, "acme/marketing")
575575
require.Contains(t, stdout, "available")
@@ -589,7 +589,7 @@ func TestRepoMirrorList_Merged(t *testing.T) {
589589
}, false)
590590
stdout, _ := runMirrorList(t)
591591
require.Equal(t, 1, strings.Count(stdout, "acme/web"), "one row per repo, not one per placement")
592-
require.Regexp(t, `acme/web\s+us, eu\s+yes\s+ready`, stdout)
592+
require.Regexp(t, `acme/web\s+us, eu\s+Private\s+ready`, stdout)
593593
})
594594

595595
t.Run("the detail hint is withheld from empty tables and --json", func(t *testing.T) {
@@ -1416,8 +1416,8 @@ func TestRepoMirrorGet_Routing(t *testing.T) {
14161416
require.NoError(t, err)
14171417
require.Equal(t, "entirehq/entiredb", *gotFilter, "the lookup must be the server-side exact-match filter")
14181418
requireOrder(t, out,
1419-
"entirehq/entiredb",
1420-
"private", "yes",
1419+
"Name:", "entirehq/entiredb",
1420+
"Visibility:", "Private",
14211421
"CLUSTER", "CLONE URL", "STATUS",
14221422
"eu", "entire://eu-west-1.entire.io/gh/entirehq/entiredb", "failed",
14231423
"us", "entire://aws-us-east-2.entire.io/gh/entirehq/entiredb", "ready",
@@ -1431,7 +1431,12 @@ func TestRepoMirrorGet_Routing(t *testing.T) {
14311431

14321432
out, err := runGet(t, "entirehq/notyet")
14331433
require.NoError(t, err)
1434-
requireOrder(t, out, "entirehq/notyet", "private", "yes", "access", "write", "Not mirrored on any cluster (available).")
1434+
requireOrder(t, out,
1435+
"Name:", "entirehq/notyet",
1436+
"Visibility:", "Private",
1437+
"Access:", "write",
1438+
"Not mirrored on any cluster (available).",
1439+
)
14351440
require.NotContains(t, out, "CLONE URL", "a candidate has no placements table")
14361441
})
14371442

@@ -1494,12 +1499,12 @@ func TestMirrorRow(t *testing.T) {
14941499
{
14951500
name: "private mirror synthesises clone URL",
14961501
mirror: coreapi.Mirror{Owner: "entirehq", Repo: "entire.io", ClusterHost: "aws-us-east-2.entire.io", IsPrivate: coreapi.NewOptBool(true)},
1497-
want: []string{"entirehq/entire.io", "entire://aws-us-east-2.entire.io/gh/entirehq/entire.io", "yes"},
1502+
want: []string{"entirehq/entire.io", "entire://aws-us-east-2.entire.io/gh/entirehq/entire.io", "Private"},
14981503
},
14991504
{
1500-
name: "public mirror, unset IsPrivate defaults to no",
1505+
name: "public mirror, unset IsPrivate defaults to Public",
15011506
mirror: coreapi.Mirror{Owner: "octocat", Repo: "hello", ClusterHost: "eu-west-1.entire.io"},
1502-
want: []string{"octocat/hello", "entire://eu-west-1.entire.io/gh/octocat/hello", "no"},
1507+
want: []string{"octocat/hello", "entire://eu-west-1.entire.io/gh/octocat/hello", "Public"},
15031508
},
15041509
}
15051510
for _, tt := range tests {
@@ -1530,20 +1535,20 @@ func TestRepoDirCells(t *testing.T) {
15301535
row: repoDirRow{Repo: "acme/web", Private: true, Status: "ready", Placements: []repoDirPlacement{
15311536
{Cluster: "us", Status: "ready", CloneURL: "entire://h/gh/acme/web"},
15321537
}},
1533-
want: []string{"acme/web", "us", "yes", "ready", "-"},
1538+
want: []string{"acme/web", "us", "Private", "ready", "-"},
15341539
},
15351540
{
15361541
name: "multi-cluster mirror row joins its slugs in one cell",
15371542
row: repoDirRow{Repo: "acme/web", Private: true, Status: "ready", Placements: []repoDirPlacement{
15381543
{Cluster: "us", Status: "ready"},
15391544
{Cluster: "eu", Status: "ready"},
15401545
}},
1541-
want: []string{"acme/web", "us, eu", "yes", "ready", "-"},
1546+
want: []string{"acme/web", "us, eu", "Private", "ready", "-"},
15421547
},
15431548
{
15441549
name: "candidate row: access + availability, clusters dashed",
15451550
row: repoDirRow{Repo: "acme/mkt", Private: false, Status: "available", Access: "admin"},
1546-
want: []string{"acme/mkt", "-", "no", "available", "admin"},
1551+
want: []string{"acme/mkt", "-", "Public", "available", "admin"},
15471552
},
15481553
}
15491554
for _, tt := range tests {
@@ -1932,16 +1937,16 @@ func TestSortRepoDir(t *testing.T) {
19321937

19331938
t.Run("non-name column sort falls back to the name tiebreak", func(t *testing.T) {
19341939
t.Parallel()
1935-
// beta/api and acme/api collide on "ready"+public; within the tie the
1940+
// beta/api and acme/api collide on "ready"+Public; within the tie the
19361941
// order must fall back to repo name, not the input order.
19371942
r := base()
1938-
require.NoError(t, sortRepoDir(r, "private"))
1943+
require.NoError(t, sortRepoDir(r, "visibility"))
19391944
require.Equal(t, []string{
1940-
// "no" (public) group first, ordered by repo name.
1945+
// "private" sorts before "public"; the Private row leads, the
1946+
// Public group follows ordered by repo name.
1947+
"acme/web@us, eu",
19411948
"acme/api@us",
19421949
"beta/api@eu",
1943-
// "yes" (private) group last.
1944-
"acme/web@us, eu",
19451950
}, repoDirKeys(r))
19461951
})
19471952

0 commit comments

Comments
 (0)