detached 3387f72

fix(cli): address code review feedback for auth status command

- Add -r shorthand for --remote flag
- Display both remote name and URL for clarity
- Use case-insensitive error string matching
- Fix negative duration display for expired tokens
commit
3387f72e2ddc47288d6b16713aa6dd42c20cb9e1
author
brice · · 2026-01-29 04:35 UTC
parent
546e1a6
signature
unsigned
1 file · +20 -14
M internal/adapters/in/cli/auth.go +20 -14
@@ -671,7 +671,7 @@ Examples:
671671 return runAuthStatus(remoteNameFlag)
672672 },
673673 }
674- cmd.Flags().StringVar(&remoteNameFlag, "remote", "", "Check specific remote")
674+ cmd.Flags().StringVarP(&remoteNameFlag, "remote", "r", "", "Check specific remote")
675675 return cmd
676676 }
677677
@@ -716,24 +716,24 @@ func runAuthStatus(remoteNameArg string) error {
716716
717717 // handleAuthVerifyError handles errors from auth verification.
718718 func handleAuthVerifyError(err error, remoteName, url string) error {
719- errStr := err.Error()
719+ errStrLower := strings.ToLower(err.Error())
720720
721721 // Connection errors
722- if strings.Contains(errStr, "connection refused") ||
723- strings.Contains(errStr, "no such host") ||
724- strings.Contains(errStr, "timeout") {
722+ if strings.Contains(errStrLower, "connection refused") ||
723+ strings.Contains(errStrLower, "no such host") ||
724+ strings.Contains(errStrLower, "timeout") {
725725 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))
726726 fmt.Println()
727727 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Connection Failed"))
728- fmt.Printf("Error: %s\n\n", errStr)
728+ fmt.Printf("Error: %s\n\n", err.Error())
729729 fmt.Println(styles.Theme.Muted.Render("Check that Gordon server is running and accessible."))
730730 return nil
731731 }
732732
733733 // Auth errors
734- if strings.Contains(errStr, "401") ||
735- strings.Contains(errStr, "unauthorized") ||
736- strings.Contains(errStr, "invalid token") {
734+ if strings.Contains(errStrLower, "401") ||
735+ strings.Contains(errStrLower, "unauthorized") ||
736+ strings.Contains(errStrLower, "invalid token") {
737737 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))
738738 fmt.Println()
739739 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Authentication Failed"))
@@ -750,7 +750,7 @@ func handleAuthVerifyError(err error, remoteName, url string) error {
750750
751751 // displayAuthStatus displays authentication status information.
752752 func displayAuthStatus(remoteName, url string, status *dto.AuthVerifyResponse) {
753- fmt.Println(styles.Theme.Bold.Render("Remote: " + url))
753+ fmt.Println(styles.Theme.Bold.Render("Remote: " + remoteName + " | URL: " + url))
754754 fmt.Println()
755755
756756 if status.Valid {
@@ -769,10 +769,16 @@ func displayAuthStatus(remoteName, url string, status *dto.AuthVerifyResponse) {
769769
770770 if status.ExpiresAt > 0 {
771771 expiresAt := time.Unix(status.ExpiresAt, 0)
772- remaining := time.Until(expiresAt)
773- fmt.Printf("%s %s (%s)\n", styles.Theme.Bold.Render("Expires:"),
774- expiresAt.Format("2006-01-02 15:04:05"),
775- styles.Theme.Muted.Render(remaining.Round(time.Second).String()))
772+ if expiresAt.Before(time.Now()) {
773+ fmt.Printf("%s %s (%s ago)\n", styles.Theme.Bold.Render("Expired:"),
774+ expiresAt.Format("2006-01-02 15:04:05"),
775+ styles.Theme.Muted.Render(time.Since(expiresAt).Round(time.Second).String()))
776+ } else {
777+ remaining := time.Until(expiresAt)
778+ fmt.Printf("%s %s (%s)\n", styles.Theme.Bold.Render("Expires:"),
779+ expiresAt.Format("2006-01-02 15:04:05"),
780+ styles.Theme.Muted.Render(remaining.Round(time.Second).String()))
781+ }
776782 }
777783
778784 if len(status.Scopes) > 0 {
@@ -671,7 +671,7 @@ Examples:
671 return runAuthStatus(remoteNameFlag)671 return runAuthStatus(remoteNameFlag)
672 },672 },
673 }673 }
674- cmd.Flags().StringVar(&remoteNameFlag, "remote", "", "Check specific remote")674+ cmd.Flags().StringVarP(&remoteNameFlag, "remote", "r", "", "Check specific remote")
675 return cmd675 return cmd
676 }676 }
677 677
@@ -716,24 +716,24 @@ func runAuthStatus(remoteNameArg string) error {
716 716
717 // handleAuthVerifyError handles errors from auth verification.717 // handleAuthVerifyError handles errors from auth verification.
718 func handleAuthVerifyError(err error, remoteName, url string) error {718 func handleAuthVerifyError(err error, remoteName, url string) error {
719- errStr := err.Error()719+ errStrLower := strings.ToLower(err.Error())
720 720
721 // Connection errors721 // Connection errors
722- if strings.Contains(errStr, "connection refused") ||722+ if strings.Contains(errStrLower, "connection refused") ||
723- strings.Contains(errStr, "no such host") ||723+ strings.Contains(errStrLower, "no such host") ||
724- strings.Contains(errStr, "timeout") {724+ strings.Contains(errStrLower, "timeout") {
725 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))725 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))
726 fmt.Println()726 fmt.Println()
727 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Connection Failed"))727 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Connection Failed"))
728- fmt.Printf("Error: %s\n\n", errStr)728+ fmt.Printf("Error: %s\n\n", err.Error())
729 fmt.Println(styles.Theme.Muted.Render("Check that Gordon server is running and accessible."))729 fmt.Println(styles.Theme.Muted.Render("Check that Gordon server is running and accessible."))
730 return nil730 return nil
731 }731 }
732 732
733 // Auth errors733 // Auth errors
734- if strings.Contains(errStr, "401") ||734+ if strings.Contains(errStrLower, "401") ||
735- strings.Contains(errStr, "unauthorized") ||735+ strings.Contains(errStrLower, "unauthorized") ||
736- strings.Contains(errStr, "invalid token") {736+ strings.Contains(errStrLower, "invalid token") {
737 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))737 fmt.Println(styles.Theme.Bold.Render("Remote: " + url))
738 fmt.Println()738 fmt.Println()
739 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Authentication Failed"))739 fmt.Println(styles.Theme.Error.Render(styles.IconError + " Authentication Failed"))
@@ -750,7 +750,7 @@ func handleAuthVerifyError(err error, remoteName, url string) error {
750 750
751 // displayAuthStatus displays authentication status information.751 // displayAuthStatus displays authentication status information.
752 func displayAuthStatus(remoteName, url string, status *dto.AuthVerifyResponse) {752 func displayAuthStatus(remoteName, url string, status *dto.AuthVerifyResponse) {
753- fmt.Println(styles.Theme.Bold.Render("Remote: " + url))753+ fmt.Println(styles.Theme.Bold.Render("Remote: " + remoteName + " | URL: " + url))
754 fmt.Println()754 fmt.Println()
755 755
756 if status.Valid {756 if status.Valid {
@@ -769,10 +769,16 @@ func displayAuthStatus(remoteName, url string, status *dto.AuthVerifyResponse) {
769 769
770 if status.ExpiresAt > 0 {770 if status.ExpiresAt > 0 {
771 expiresAt := time.Unix(status.ExpiresAt, 0)771 expiresAt := time.Unix(status.ExpiresAt, 0)
772- remaining := time.Until(expiresAt)772+ if expiresAt.Before(time.Now()) {
773- fmt.Printf("%s %s (%s)\n", styles.Theme.Bold.Render("Expires:"),773+ fmt.Printf("%s %s (%s ago)\n", styles.Theme.Bold.Render("Expired:"),
774- expiresAt.Format("2006-01-02 15:04:05"),774+ expiresAt.Format("2006-01-02 15:04:05"),
775- styles.Theme.Muted.Render(remaining.Round(time.Second).String()))775+ styles.Theme.Muted.Render(time.Since(expiresAt).Round(time.Second).String()))
776+ } else {
777+ remaining := time.Until(expiresAt)
778+ fmt.Printf("%s %s (%s)\n", styles.Theme.Bold.Render("Expires:"),
779+ expiresAt.Format("2006-01-02 15:04:05"),
780+ styles.Theme.Muted.Render(remaining.Round(time.Second).String()))
781+ }
776 }782 }
777 783
778 if len(status.Scopes) > 0 {784 if len(status.Scopes) > 0 {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 106

↑↓ moveenter openesc close