Commit b8cd3fd9d0
Verified · cmc
internal/control/admin.go +33 −15
| @@ -128,13 +128,15 @@ func runAdminUserList(c *Ctx, args []string) int { | ||
| 128 | 128 | ds = append(ds, adminUserRow(u)) |
| 129 | 129 | } |
| 130 | 130 | return c.emitPage(p, ds, next, func(w io.Writer) { |
| 131 | tb := c.table(w, "USERNAME", "STATE", "ADMIN", "CREATED", "LAST SEEN") | |
| 131 | 132 | for _, d := range ds { |
| 132 | 133 | mark := "" |
| 133 | 134 | if d.Admin { |
| 134 | 135 | mark = "admin" |
| 135 | 136 | } |
| 136 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", d.Username, d.State, mark, d.CreatedAt, d.LastSeen) | |
| 137 | tb.row(cRef(d.Username), cState(d.State), cText(mark), cAge(d.CreatedAt), cAge(d.LastSeen)) | |
| 137 | 138 | } |
| 139 | tb.flush() | |
| 138 | 140 | }) |
| 139 | 141 | } |
| 140 | 142 | |
| @@ -257,37 +259,47 @@ func runAdminUserShow(c *Ctx, args []string) int { | ||
| 257 | 259 | } |
| 258 | 260 | fmt.Fprintf(w, "repos\t%d\nweb sessions\t%d\n", d.Repos, d.WebSessions) |
| 259 | 261 | fmt.Fprintln(w, "keys:") |
| 262 | tk := c.table(w, "FINGERPRINT", "ALGO", "SCOPE", "LAST USED") | |
| 260 | 263 | for _, k := range d.Keys { |
| 261 | fmt.Fprintf(w, " %s\t%s\t%s\t%s\n", k.Fingerprint, k.Algo, k.Scope, k.LastUsedAt) | |
| 264 | tk.row(cRef(" "+k.Fingerprint), cText(k.Algo), cState(k.Scope), cAge(k.LastUsedAt)) | |
| 262 | 265 | } |
| 266 | tk.flush() | |
| 263 | 267 | fmt.Fprintln(w, "emails:") |
| 268 | te := c.table(w, "ADDRESS", "STATE") | |
| 264 | 269 | for _, e := range d.Emails { |
| 265 | 270 | state := "unverified" |
| 266 | 271 | if e.Verified { |
| 267 | 272 | state = "verified by " + e.VerifiedBy |
| 268 | 273 | } |
| 269 | mark := "" | |
| 274 | cells := []cell{cRef(" " + e.Address), cState(state)} | |
| 270 | 275 | if e.Primary { |
| 271 | mark = "\tprimary" | |
| 276 | cells = append(cells, cText("primary")) | |
| 272 | 277 | } |
| 273 | fmt.Fprintf(w, " %s\t%s%s\n", e.Address, state, mark) | |
| 278 | te.row(cells...) | |
| 274 | 279 | } |
| 280 | te.flush() | |
| 275 | 281 | fmt.Fprintln(w, "pgp keys:") |
| 282 | tp := c.table(w, "FINGERPRINT") | |
| 276 | 283 | for _, k := range d.PGPKeys { |
| 277 | fmt.Fprintf(w, " %s\n", k.Fingerprint) | |
| 284 | tp.row(cRef(" " + k.Fingerprint)) | |
| 278 | 285 | } |
| 286 | tp.flush() | |
| 279 | 287 | fmt.Fprintln(w, "orgs:") |
| 288 | to := c.table(w, "ORG", "ROLE") | |
| 280 | 289 | for _, o := range d.Orgs { |
| 281 | fmt.Fprintf(w, " %s\t%s\n", o.Org, o.Role) | |
| 290 | to.row(cRef(" "+o.Org), cState(o.Role)) | |
| 282 | 291 | } |
| 292 | to.flush() | |
| 283 | 293 | fmt.Fprintln(w, "api tokens:") |
| 294 | tt := c.table(w, "NAME", "SCOPE", "LAST USED") | |
| 284 | 295 | for _, t := range d.APITokens { |
| 285 | 296 | used := "" |
| 286 | 297 | if t.LastUsedAt != nil { |
| 287 | used = t.LastUsedAt.UTC().Format(time.RFC3339) | |
| 298 | used = t.LastUsedAt.UTC().Format(time.RFC3339Nano) | |
| 288 | 299 | } |
| 289 | fmt.Fprintf(w, " %s\t%s\t%s\n", t.Name, t.Scope, strings.TrimSpace(used)) | |
| 300 | tt.row(cRef(" "+t.Name), cState(t.Scope), cAge(used)) | |
| 290 | 301 | } |
| 302 | tt.flush() | |
| 291 | 303 | }) |
| 292 | 304 | } |
| 293 | 305 | |
| @@ -382,13 +394,15 @@ func runAdminRepoList(c *Ctx, args []string) int { | ||
| 382 | 394 | ds = append(ds, out{r.Path, r.Visibility, r.Archived, r.CreatedAt, r.LastPush, size}) |
| 383 | 395 | } |
| 384 | 396 | return c.emitPage(p, ds, next, func(w io.Writer) { |
| 397 | tb := c.table(w, "PATH", "VISIBILITY", "BYTES", "CREATED", "LAST PUSH") | |
| 385 | 398 | for _, d := range ds { |
| 386 | mark := "" | |
| 399 | cells := []cell{cRef(d.Path), cState(d.Visibility), cNum(d.Bytes), cAge(d.CreatedAt), cAge(d.LastPush)} | |
| 387 | 400 | if d.Archived { |
| 388 | mark = "\t[archived]" | |
| 401 | cells = append(cells, cText("[archived]")) | |
| 389 | 402 | } |
| 390 | fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s%s\n", d.Path, d.Visibility, d.Bytes, d.CreatedAt, d.LastPush, mark) | |
| 403 | tb.row(cells...) | |
| 391 | 404 | } |
| 405 | tb.flush() | |
| 392 | 406 | }) |
| 393 | 407 | } |
| 394 | 408 | |
| @@ -517,6 +531,7 @@ func runAdminRunners(c *Ctx, args []string) int { | ||
| 517 | 531 | return c.emit(d, func(w io.Writer) { |
| 518 | 532 | fmt.Fprintf(w, "queue: %d pending; last 24h: %d claimed, wait avg %ds max %ds, %d reaped\n", |
| 519 | 533 | queue.Pending, queue.Claimed24h, queue.ClaimWaitAvgS, queue.ClaimWaitMaxS, queue.Reaped24h) |
| 534 | tb := c.table(w, "USER", "FINGERPRINT", "LAST SEEN", "SCOPE", "HELD") | |
| 520 | 535 | for _, r := range runners { |
| 521 | 536 | scope := r.Scope |
| 522 | 537 | if scope == "" { |
| @@ -526,8 +541,9 @@ func runAdminRunners(c *Ctx, args []string) int { | ||
| 526 | 541 | if r.BuildNumber != 0 { |
| 527 | 542 | held = fmt.Sprintf("%s #%d %s since %s", r.BuildRepo, r.BuildNumber, r.BuildJob, r.StartedAt) |
| 528 | 543 | } |
| 529 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", r.Username, r.Fingerprint, r.LastSeen, scope, held) | |
| 544 | tb.row(cText(r.Username), cRef(r.Fingerprint), cAge(r.LastSeen), cText(scope), cText(held)) | |
| 530 | 545 | } |
| 546 | tb.flush() | |
| 531 | 547 | }) |
| 532 | 548 | } |
| 533 | 549 | |
| @@ -609,12 +625,14 @@ func runAdminMRPrune(c *Ctx, args []string) int { | ||
| 609 | 625 | return c.fail(protocol.ExitFailure, "%v; the head refs are deleted but the objects are not yet pruned; re-run the same command", err) |
| 610 | 626 | } |
| 611 | 627 | return c.emit(rows, func(w io.Writer) { |
| 628 | tb := c.table(w, "!", "HEAD") | |
| 612 | 629 | for _, r := range rows { |
| 613 | 630 | if r.Head == "" { |
| 614 | fmt.Fprintf(w, "!%d\talready gone\n", r.Number) | |
| 631 | tb.row(cRef(fmt.Sprintf("!%d", r.Number)), cText("already gone")) | |
| 615 | 632 | continue |
| 616 | 633 | } |
| 617 | fmt.Fprintf(w, "!%d\t%s\n", r.Number, r.Head) | |
| 634 | tb.row(cRef(fmt.Sprintf("!%d", r.Number)), cRef(r.Head)) | |
| 618 | 635 | } |
| 636 | tb.flush() | |
| 619 | 637 | }) |
| 620 | 638 | } |
internal/control/audit.go +3 −2
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | package control |
| 2 | 2 | |
| 3 | 3 | import ( |
| 4 | "fmt" | |
| 5 | 4 | "io" |
| 6 | 5 | "strconv" |
| 7 | 6 | "strings" |
| @@ -47,13 +46,15 @@ func runAudit(c *Ctx, args []string) int { | ||
| 47 | 46 | return c.fail(protocol.ExitFailure, "%v", err) |
| 48 | 47 | } |
| 49 | 48 | return c.emit(entries, func(w io.Writer) { |
| 49 | tb := c.table(w, "WHEN", "ACTOR", "ACTION", "DATA") | |
| 50 | 50 | for _, e := range entries { |
| 51 | 51 | actor := e.Actor |
| 52 | 52 | if actor == "" { |
| 53 | 53 | actor = "-" |
| 54 | 54 | } |
| 55 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", e.CreatedAt, actor, e.Action, e.Data) | |
| 55 | tb.row(cAge(e.CreatedAt), cText(actor), cText(e.Action), cFlex(e.Data)) | |
| 56 | 56 | } |
| 57 | tb.flush() | |
| 57 | 58 | }) |
| 58 | 59 | } |
| 59 | 60 | |
internal/control/dashboard.go +93 −45
| @@ -5,6 +5,7 @@ import ( | ||
| 5 | 5 | "fmt" |
| 6 | 6 | "io" |
| 7 | 7 | "strconv" |
| 8 | "strings" | |
| 8 | 9 | |
| 9 | 10 | "gitbay.org/gitbay/internal/buildinfo" |
| 10 | 11 | "gitbay.org/gitbay/internal/gitutil" |
| @@ -163,90 +164,135 @@ func runDashboard(c *Ctx, args []string) int { | ||
| 163 | 164 | } |
| 164 | 165 | |
| 165 | 166 | return c.emit(d, func(w io.Writer) { |
| 167 | section := func(title string, header []string, rows [][]cell) { | |
| 168 | if c.Term.Cols > 0 { | |
| 169 | fmt.Fprintln(w, c.Term.paint(sgrBold, title)) | |
| 170 | } else { | |
| 171 | fmt.Fprintln(w, title) | |
| 172 | } | |
| 173 | if len(rows) == 0 { | |
| 174 | fmt.Fprintln(w, " none") | |
| 175 | return | |
| 176 | } | |
| 177 | if c.Term.Cols == 0 { | |
| 178 | for _, r := range rows { | |
| 179 | parts := make([]string, len(r)) | |
| 180 | for i, cl := range r { | |
| 181 | parts[i] = cl.s | |
| 182 | if cl.kind == kindAge { | |
| 183 | parts[i] = stamp(cl.s) | |
| 184 | } | |
| 185 | } | |
| 186 | fmt.Fprintf(w, " %s\n", strings.Join(parts, "\t")) | |
| 187 | } | |
| 188 | return | |
| 189 | } | |
| 190 | tb := c.table(w, header...) | |
| 191 | for _, r := range rows { | |
| 192 | tb.row(r...) | |
| 193 | } | |
| 194 | tb.flush() | |
| 195 | } | |
| 196 | itemRows := func(items []DashboardItem, marker string) [][]cell { | |
| 197 | rows := make([][]cell, len(items)) | |
| 198 | for i, item := range items { | |
| 199 | rows[i] = []cell{cRef(fmt.Sprintf("%s%s%d", item.Repo, marker, item.Number)), cFlex(item.Title), cText(item.Author)} | |
| 200 | } | |
| 201 | return rows | |
| 202 | } | |
| 203 | ||
| 166 | 204 | if d.Unread > 0 { |
| 167 | 205 | fmt.Fprintf(w, "unread notifications: %d\n", d.Unread) |
| 168 | 206 | } |
| 169 | fmt.Fprintln(w, "waiting on your review:") | |
| 170 | printDashboardItems(w, d.Reviews, "!") | |
| 171 | fmt.Fprintln(w, "assigned to you:") | |
| 172 | printDashboardItems(w, d.Assigned, "#") | |
| 173 | fmt.Fprintln(w, "open merge requests:") | |
| 174 | printDashboardItems(w, d.MRs, "!") | |
| 175 | fmt.Fprintln(w, "open issues:") | |
| 176 | printDashboardItems(w, d.Issues, "#") | |
| 177 | fmt.Fprintln(w, "pinned:") | |
| 178 | if len(d.Pinned) == 0 { | |
| 179 | fmt.Fprintln(w, " none") | |
| 180 | } | |
| 181 | for _, p := range d.Pinned { | |
| 182 | mark := "" | |
| 207 | itemHeader := []string{"REF", "TITLE", "AUTHOR"} | |
| 208 | section("waiting on your review:", itemHeader, itemRows(d.Reviews, "!")) | |
| 209 | section("assigned to you:", itemHeader, itemRows(d.Assigned, "#")) | |
| 210 | section("open merge requests:", itemHeader, itemRows(d.MRs, "!")) | |
| 211 | section("open issues:", itemHeader, itemRows(d.Issues, "#")) | |
| 212 | ||
| 213 | pinnedRows := make([][]cell, len(d.Pinned)) | |
| 214 | for i, p := range d.Pinned { | |
| 215 | cells := []cell{cRef(p.Path), cState(p.Visibility), cFlex(p.Description)} | |
| 183 | 216 | if p.Archived { |
| 184 | mark = "\t[archived]" | |
| 217 | cells = append(cells, cText("[archived]")) | |
| 185 | 218 | } |
| 186 | fmt.Fprintf(w, " %s\t%s\t%s%s\n", p.Path, p.Visibility, p.Description, mark) | |
| 219 | pinnedRows[i] = cells | |
| 187 | 220 | } |
| 188 | fmt.Fprintln(w, "recent activity:") | |
| 189 | if len(d.Activity) == 0 { | |
| 190 | fmt.Fprintln(w, " none") | |
| 191 | } | |
| 192 | for _, e := range d.Activity { | |
| 193 | fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%s\n", e.CreatedAt, e.Actor, e.Kind, e.Repo, string(e.Data)) | |
| 194 | } | |
| 195 | fmt.Fprintln(w, "builds:") | |
| 196 | if len(d.Builds) == 0 { | |
| 197 | fmt.Fprintln(w, " none") | |
| 221 | section("pinned:", []string{"PATH", "VISIBILITY", "DESCRIPTION"}, pinnedRows) | |
| 222 | ||
| 223 | activityRows := make([][]cell, len(d.Activity)) | |
| 224 | for i, e := range d.Activity { | |
| 225 | activityRows[i] = []cell{cAge(e.CreatedAt), cText(e.Actor), cText(e.Kind), cRef(e.Repo), cFlex(string(e.Data))} | |
| 198 | 226 | } |
| 199 | for _, b := range d.Builds { | |
| 200 | fmt.Fprintf(w, " %s\t%d\t%s\t%s\t%.10s\t%s\n", b.Repo, b.Number, b.Job, b.Status, b.SHA, b.Ref) | |
| 227 | section("recent activity:", []string{"WHEN", "ACTOR", "KIND", "REPO", "DATA"}, activityRows) | |
| 228 | ||
| 229 | buildRows := make([][]cell, len(d.Builds)) | |
| 230 | for i, b := range d.Builds { | |
| 231 | buildRows[i] = []cell{cRef(b.Repo), cNum(b.Number), cText(b.Job), cState(b.Status), cRef(fmt.Sprintf("%.10s", b.SHA)), cText(b.Ref)} | |
| 201 | 232 | } |
| 233 | section("builds:", []string{"REPO", "#", "JOB", "STATUS", "SHA", "REF"}, buildRows) | |
| 234 | ||
| 202 | 235 | if d.Server != nil { |
| 203 | 236 | fmt.Fprintf(w, "server:\n build %s\n", d.Server.Commit) |
| 204 | 237 | } |
| 205 | 238 | if q := d.Queues; q != nil { |
| 206 | 239 | fmt.Fprintln(w, "queues:") |
| 240 | ||
| 207 | 241 | fmt.Fprintf(w, " webhooks\tpending %d\tretrying %d\tfailed %d\n", q.Webhooks.Pending, q.Webhooks.Retrying, q.Webhooks.Failed) |
| 242 | twh := c.table(w, "REPO", "URL", "ATTEMPTS", "ERROR") | |
| 208 | 243 | for _, it := range q.Webhooks.Items { |
| 209 | fmt.Fprintf(w, " %s\t%s\tattempts %d\t%s\n", it.Repo, it.URL, it.Attempts, it.LastError) | |
| 244 | twh.row(cRef(" "+it.Repo), cText(it.URL), cText(fmt.Sprintf("attempts %d", it.Attempts)), cText(it.LastError)) | |
| 210 | 245 | } |
| 246 | twh.flush() | |
| 247 | ||
| 211 | 248 | fmt.Fprintf(w, " mail\tpending %d\tretrying %d\tfailed %d\n", q.Mail.Pending, q.Mail.Retrying, q.Mail.Failed) |
| 249 | tma := c.table(w, "RECIPIENT", "SUBJECT", "ATTEMPTS", "ERROR") | |
| 212 | 250 | for _, it := range q.Mail.Items { |
| 213 | fmt.Fprintf(w, " %s\t%s\tattempts %d\t%s\n", it.Recipient, it.Subject, it.Attempts, it.LastError) | |
| 251 | tma.row(cRef(" "+it.Recipient), cText(it.Subject), cText(fmt.Sprintf("attempts %d", it.Attempts)), cText(it.LastError)) | |
| 214 | 252 | } |
| 253 | tma.flush() | |
| 254 | ||
| 215 | 255 | // The device id, not the token: a token is never echoed. |
| 216 | 256 | fmt.Fprintf(w, " push\tpending %d\tretrying %d\tfailed %d\n", q.Push.Pending, q.Push.Retrying, q.Push.Failed) |
| 257 | tpu := c.table(w, "DEVICE", "TITLE", "ATTEMPTS", "ERROR") | |
| 217 | 258 | for _, it := range q.Push.Items { |
| 218 | fmt.Fprintf(w, " device %d\t%s\tattempts %d\t%s\n", it.DeviceID, it.Title, it.Attempts, it.LastError) | |
| 259 | tpu.row(cRef(fmt.Sprintf(" device %d", it.DeviceID)), cText(it.Title), cText(fmt.Sprintf("attempts %d", it.Attempts)), cText(it.LastError)) | |
| 219 | 260 | } |
| 261 | tpu.flush() | |
| 262 | ||
| 220 | 263 | fmt.Fprintf(w, " mirrors\tdirty %d\terrors %d\n", q.Mirrors.Dirty, q.Mirrors.Errors) |
| 264 | tmi := c.table(w, "REPO", "DIRECTION", "URL", "ERROR") | |
| 221 | 265 | for _, it := range q.Mirrors.Items { |
| 222 | fmt.Fprintf(w, " %s\t%s\t%s\t%s\n", it.Repo, it.Direction, it.URL, it.LastError) | |
| 266 | tmi.row(cRef(" "+it.Repo), cText(it.Direction), cText(it.URL), cText(it.LastError)) | |
| 223 | 267 | } |
| 268 | tmi.flush() | |
| 269 | ||
| 224 | 270 | fmt.Fprintf(w, " builds\tpending %d\trunning %d\n", q.Builds.Pending, q.Builds.Running) |
| 271 | tbq := c.table(w, "REPO", "#", "JOB", "STATUS") | |
| 225 | 272 | for _, it := range q.Builds.Items { |
| 226 | 273 | since := it.StartedAt |
| 227 | 274 | if it.Status == "pending" { |
| 228 | 275 | since = it.CreatedAt |
| 229 | 276 | } |
| 230 | fmt.Fprintf(w, " %s\t%d\t%s\t%s since %s\n", it.Repo, it.Number, it.Job, it.Status, since) | |
| 277 | if c.Term.Cols == 0 { | |
| 278 | since = stamp(since) | |
| 279 | } else { | |
| 280 | since = relAge(since, termNow()) | |
| 281 | } | |
| 282 | tbq.row(cRef(" "+it.Repo), cNum(it.Number), cText(it.Job), cText(fmt.Sprintf("%s since %s", it.Status, since))) | |
| 231 | 283 | } |
| 284 | tbq.flush() | |
| 285 | ||
| 232 | 286 | fmt.Fprintf(w, " deps\terrors %d\n", q.Deps.Errors) |
| 287 | tde := c.table(w, "REPO", "ERROR") | |
| 233 | 288 | for _, it := range q.Deps.Items { |
| 234 | fmt.Fprintf(w, " %s\t%s\n", it.Repo, it.LastError) | |
| 289 | tde.row(cRef(" "+it.Repo), cText(it.LastError)) | |
| 235 | 290 | } |
| 291 | tde.flush() | |
| 236 | 292 | } |
| 237 | 293 | }) |
| 238 | 294 | } |
| 239 | 295 | |
| 240 | func printDashboardItems(w io.Writer, items []DashboardItem, marker string) { | |
| 241 | if len(items) == 0 { | |
| 242 | fmt.Fprintln(w, " none") | |
| 243 | return | |
| 244 | } | |
| 245 | for _, item := range items { | |
| 246 | fmt.Fprintf(w, " %s%s%d\t%s\t%s\n", item.Repo, marker, item.Number, item.Title, item.Author) | |
| 247 | } | |
| 248 | } | |
| 249 | ||
| 250 | 296 | // feedDefaultLimit caps a bare `feed` call; pagination reaches further |
| 251 | 297 | // back. |
| 252 | 298 | const feedDefaultLimit = 50 |
| @@ -292,8 +338,10 @@ func runFeed(c *Ctx, args []string) int { | ||
| 292 | 338 | }) |
| 293 | 339 | ds := feedOutputs(events) |
| 294 | 340 | return c.emitPage(p, ds, next, func(w io.Writer) { |
| 341 | tb := c.table(w, "WHEN", "ACTOR", "KIND", "REPO", "DATA") | |
| 295 | 342 | for _, d := range ds { |
| 296 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", d.CreatedAt, d.Actor, d.Kind, d.Repo, string(d.Data)) | |
| 343 | tb.row(cAge(d.CreatedAt), cText(d.Actor), cText(d.Kind), cRef(d.Repo), cFlex(string(d.Data))) | |
| 297 | 344 | } |
| 345 | tb.flush() | |
| 298 | 346 | }) |
| 299 | 347 | } |
internal/control/deploykey.go +3 −1
| @@ -98,9 +98,11 @@ func runDeployKeyList(c *Ctx, args []string) int { | ||
| 98 | 98 | ds = append(ds, out{k.Fingerprint, k.Algo, mode, k.Label}) |
| 99 | 99 | } |
| 100 | 100 | return c.emit(ds, func(w io.Writer) { |
| 101 | tb := c.table(w, "FINGERPRINT", "ALGO", "MODE", "LABEL") | |
| 101 | 102 | for _, d := range ds { |
| 102 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", d.Fingerprint, d.Algo, d.Mode, d.Label) | |
| 103 | tb.row(cRef(d.Fingerprint), cText(d.Algo), cState(d.Mode), cText(d.Label)) | |
| 103 | 104 | } |
| 105 | tb.flush() | |
| 104 | 106 | }) |
| 105 | 107 | } |
| 106 | 108 | |
internal/control/identity.go +3 −1
| @@ -83,9 +83,11 @@ func runKeysList(c *Ctx, args []string) int { | ||
| 83 | 83 | ds = append(ds, out{k.Fingerprint, k.Algo, k.Scope, k.Label}) |
| 84 | 84 | } |
| 85 | 85 | return c.emit(ds, func(w io.Writer) { |
| 86 | tb := c.table(w, "FINGERPRINT", "ALGO", "SCOPE", "LABEL") | |
| 86 | 87 | for _, d := range ds { |
| 87 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", d.Fingerprint, d.Algo, d.Scope, d.Label) | |
| 88 | tb.row(cRef(d.Fingerprint), cText(d.Algo), cState(d.Scope), cText(d.Label)) | |
| 88 | 89 | } |
| 90 | tb.flush() | |
| 89 | 91 | }) |
| 90 | 92 | } |
| 91 | 93 | |
internal/control/notifications.go +7 −3
| @@ -303,9 +303,11 @@ func runNotificationsDeviceList(c *Ctx, args []string) int { | ||
| 303 | 303 | Token: ShortToken(d.Token), Added: d.CreatedAt}) |
| 304 | 304 | } |
| 305 | 305 | return c.emit(rows, func(w io.Writer) { |
| 306 | tb := c.table(w, "ID", "LABEL", "TOKEN", "ADDED") | |
| 306 | 307 | for _, r := range rows { |
| 307 | fmt.Fprintf(w, "%d\t%s\t%s\t%s\n", r.ID, r.Label, r.Token, r.Added) | |
| 308 | tb.row(cRef(fmt.Sprintf("%d", r.ID)), cText(r.Label), cText(r.Token), cAge(r.Added)) | |
| 308 | 309 | } |
| 310 | tb.flush() | |
| 309 | 311 | }) |
| 310 | 312 | } |
| 311 | 313 | |
| @@ -381,14 +383,16 @@ func runNotificationsList(c *Ctx, args []string) int { | ||
| 381 | 383 | ds = append(ds, out{n.ID, n.RepoPath, n.Kind, n.Actor, n.Summary, n.Path, n.CreatedAt, n.ReadAt}) |
| 382 | 384 | } |
| 383 | 385 | return c.emitPage(p, ds, next, func(w io.Writer) { |
| 386 | tb := c.table(w, "ID", "WHEN", "REPO", "EVENT", "PATH") | |
| 384 | 387 | for _, d := range ds { |
| 385 | 388 | mark := "*" |
| 386 | 389 | if d.ReadAt != "" { |
| 387 | 390 | mark = " " |
| 388 | 391 | } |
| 389 | fmt.Fprintf(w, "%s %d\t%s\t%s\t%s %s\t%s\n", | |
| 390 | mark, d.ID, d.CreatedAt, d.Repo, d.Actor, d.Summary, d.Path) | |
| 392 | tb.row(cRef(fmt.Sprintf("%s %d", mark, d.ID)), cAge(d.CreatedAt), cRef(d.Repo), | |
| 393 | cFlex(fmt.Sprintf("%s %s", d.Actor, d.Summary)), cText(d.Path)) | |
| 391 | 394 | } |
| 395 | tb.flush() | |
| 392 | 396 | }) |
| 393 | 397 | } |
| 394 | 398 | |
internal/control/org.go +6 −2
| @@ -87,9 +87,11 @@ func runOrgList(c *Ctx, args []string) int { | ||
| 87 | 87 | ds = append(ds, out{o.Username, o.Role}) |
| 88 | 88 | } |
| 89 | 89 | return c.emit(ds, func(w io.Writer) { |
| 90 | tb := c.table(w, "ORG", "ROLE") | |
| 90 | 91 | for _, d := range ds { |
| 91 | fmt.Fprintf(w, "%s\t%s\n", d.Org, d.Role) | |
| 92 | tb.row(cRef(d.Org), cState(d.Role)) | |
| 92 | 93 | } |
| 94 | tb.flush() | |
| 93 | 95 | }) |
| 94 | 96 | } |
| 95 | 97 | |
| @@ -122,9 +124,11 @@ func runOrgShow(c *Ctx, args []string) int { | ||
| 122 | 124 | }{org.Name, ms} |
| 123 | 125 | return c.emit(d, func(w io.Writer) { |
| 124 | 126 | fmt.Fprintf(w, "%s\n", d.Org) |
| 127 | tb := c.table(w, "USER", "ROLE") | |
| 125 | 128 | for _, m := range ms { |
| 126 | fmt.Fprintf(w, " %s\t%s\n", m.User, m.Role) | |
| 129 | tb.row(cRef(" "+m.User), cState(m.Role)) | |
| 127 | 130 | } |
| 131 | tb.flush() | |
| 128 | 132 | }) |
| 129 | 133 | } |
| 130 | 134 | |
internal/control/register.go +5 −2
| @@ -62,16 +62,19 @@ func runEmailList(c *Ctx, args []string) int { | ||
| 62 | 62 | ds = append(ds, out{e.Address, e.Verified, e.VerifiedBy, e.Primary}) |
| 63 | 63 | } |
| 64 | 64 | return c.emit(ds, func(w io.Writer) { |
| 65 | tb := c.table(w, "ADDRESS", "STATE") | |
| 65 | 66 | for _, d := range ds { |
| 66 | 67 | state := "unverified" |
| 67 | 68 | if d.Verified { |
| 68 | 69 | state = "verified" |
| 69 | 70 | } |
| 71 | cells := []cell{cRef(d.Address), cState(state)} | |
| 70 | 72 | if d.Primary { |
| 71 | state += "\tprimary" | |
| 73 | cells = append(cells, cText("primary")) | |
| 72 | 74 | } |
| 73 | fmt.Fprintf(w, "%s\t%s\n", d.Address, state) | |
| 75 | tb.row(cells...) | |
| 74 | 76 | } |
| 77 | tb.flush() | |
| 75 | 78 | }) |
| 76 | 79 | } |
| 77 | 80 | |
internal/control/snippet.go +6 −2
| @@ -202,9 +202,11 @@ func runSnippetShow(c *Ctx, args []string) int { | ||
| 202 | 202 | fmt.Fprintf(w, "%s\n", sn.Description) |
| 203 | 203 | } |
| 204 | 204 | fmt.Fprintf(w, "%s\nupdated %s\n", snippetURL(c, sn), sn.UpdatedAt) |
| 205 | tb := c.table(w, "NAME", "SIZE") | |
| 205 | 206 | for _, f := range files { |
| 206 | fmt.Fprintf(w, " %s\t%d bytes\n", f.Name, f.Size) | |
| 207 | tb.row(cRef(" "+f.Name), cText(fmt.Sprintf("%d bytes", f.Size))) | |
| 207 | 208 | } |
| 209 | tb.flush() | |
| 208 | 210 | }) |
| 209 | 211 | } |
| 210 | 212 | |
| @@ -238,6 +240,7 @@ func runSnippetList(c *Ctx, args []string) int { | ||
| 238 | 240 | items = append(items, snippetOut(c, sn)) |
| 239 | 241 | } |
| 240 | 242 | return c.emitPage(p, items, next, func(w io.Writer) { |
| 243 | tb := c.table(w, "ID", "VISIBILITY", "FILES", "DESCRIPTION") | |
| 241 | 244 | for _, sn := range rows { |
| 242 | 245 | names := "" |
| 243 | 246 | for i, f := range sn.Files { |
| @@ -246,8 +249,9 @@ func runSnippetList(c *Ctx, args []string) int { | ||
| 246 | 249 | } |
| 247 | 250 | names += f.Name |
| 248 | 251 | } |
| 249 | fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", sn.PublicID, sn.Visibility, names, sn.Description) | |
| 252 | tb.row(cRef(sn.PublicID), cState(sn.Visibility), cText(names), cFlex(sn.Description)) | |
| 250 | 253 | } |
| 254 | tb.flush() | |
| 251 | 255 | }) |
| 252 | 256 | } |
| 253 | 257 | |
internal/control/teams.go +6 −2
| @@ -143,9 +143,11 @@ func runTeamList(c *Ctx, args []string) int { | ||
| 143 | 143 | names = append(names, t.Name) |
| 144 | 144 | } |
| 145 | 145 | return c.emit(names, func(w io.Writer) { |
| 146 | tb := c.table(w, "TEAM") | |
| 146 | 147 | for _, n := range names { |
| 147 | fmt.Fprintln(w, n) | |
| 148 | tb.row(cRef(n)) | |
| 148 | 149 | } |
| 150 | tb.flush() | |
| 149 | 151 | }) |
| 150 | 152 | } |
| 151 | 153 | |
| @@ -176,9 +178,11 @@ func runTeamShow(c *Ctx, args []string) int { | ||
| 176 | 178 | }{team.Name, members, grants} |
| 177 | 179 | return c.emit(d, func(w io.Writer) { |
| 178 | 180 | fmt.Fprintf(w, "%s/%s\nmembers: %s\n", org.Name, team.Name, strings.Join(members, ", ")) |
| 181 | tb := c.table(w, "REPO", "ROLE") | |
| 179 | 182 | for _, g := range grants { |
| 180 | fmt.Fprintf(w, "%s\t%s\n", g.RepoPath, g.Role) | |
| 183 | tb.row(cRef(g.RepoPath), cState(g.Role)) | |
| 181 | 184 | } |
| 185 | tb.flush() | |
| 182 | 186 | }) |
| 183 | 187 | } |
| 184 | 188 | |
internal/control/token.go +9 −2
| @@ -96,13 +96,20 @@ func runTokenList(c *Ctx, args []string) int { | ||
| 96 | 96 | ds = append(ds, out{t.Name, t.Scope, t.CreatedAt, t.ExpiresAt, t.LastUsedAt}) |
| 97 | 97 | } |
| 98 | 98 | return c.emit(ds, func(w io.Writer) { |
| 99 | tb := c.table(w, "NAME", "SCOPE", "EXPIRES") | |
| 99 | 100 | for _, d := range ds { |
| 100 | 101 | exp := "never expires" |
| 101 | 102 | if d.ExpiresAt != nil { |
| 102 | exp = "expires " + d.ExpiresAt.UTC().Format(time.RFC3339) | |
| 103 | ts := d.ExpiresAt.UTC().Format(time.RFC3339Nano) | |
| 104 | if c.Term.Cols == 0 { | |
| 105 | exp = "expires " + stamp(ts) | |
| 106 | } else { | |
| 107 | exp = "expires " + relAge(ts, termNow()) | |
| 108 | } | |
| 103 | 109 | } |
| 104 | fmt.Fprintf(w, "%s\t%s\t%s\n", d.Name, d.Scope, exp) | |
| 110 | tb.row(cRef(d.Name), cState(d.Scope), cText(exp)) | |
| 105 | 111 | } |
| 112 | tb.flush() | |
| 106 | 113 | }) |
| 107 | 114 | } |
| 108 | 115 | |
internal/control/web.go +9 −1
| @@ -33,9 +33,17 @@ func runWebSessionsList(c *Ctx, args []string) int { | ||
| 33 | 33 | return c.fail(protocol.ExitFailure, "%v", err) |
| 34 | 34 | } |
| 35 | 35 | return c.emit(sessions, func(w io.Writer) { |
| 36 | tb := c.table(w, "ID", "SINCE", "UNTIL") | |
| 36 | 37 | for _, s := range sessions { |
| 37 | fmt.Fprintf(w, "%s\tsince %s\tuntil %s\n", s.ID, s.CreatedAt, s.ExpiresAt) | |
| 38 | since, until := s.CreatedAt, s.ExpiresAt | |
| 39 | if c.Term.Cols == 0 { | |
| 40 | since, until = stamp(since), stamp(until) | |
| 41 | } else { | |
| 42 | since, until = relAge(since, termNow()), relAge(until, termNow()) | |
| 43 | } | |
| 44 | tb.row(cRef(s.ID), cText("since "+since), cText("until "+until)) | |
| 38 | 45 | } |
| 46 | tb.flush() | |
| 39 | 47 | }) |
| 40 | 48 | } |
| 41 | 49 | |