detached a8338c0

refactor(cli): deduplicate deploy failure and remote helpers

commit
a8338c05c00bd374a38d162d052501e768ddfd0a
author
brice · · 2026-04-08 14:57 UTC
parent
4162c86
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +42 -31
M internal/adapters/in/cli/auth.go +24 -15
@@ -232,6 +232,15 @@ Examples:
232232 return cmd
233233 }
234234
235+func resolveRequiredRemote(flagToken string) (*remote.ResolvedRemote, error) {
236+ resolved, isRemote := remote.Resolve(remoteFlag, flagToken, insecureTLSFlag)
237+ if !isRemote {
238+ return nil, fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
239+ }
240+
241+ return resolved, nil
242+}
243+
235244 func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) error {
236245 token = strings.TrimSpace(token)
237246 if token == "" {
@@ -239,9 +248,9 @@ func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) err
239248 }
240249
241250 // Resolve remote — but use the new token (not the stored one) for verification.
242- resolved, isRemote := remote.Resolve(remoteFlag, "", insecureTLSFlag)
243- if !isRemote {
244- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
251+ resolved, err := resolveRequiredRemote("")
252+ if err != nil {
253+ return err
245254 }
246255
247256 client := remote.NewClient(resolved.URL, remoteClientOptions(token, resolved.InsecureTLS)...)
@@ -271,9 +280,9 @@ func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) err
271280 }
272281
273282 func runAuthLoginVerify(ctx context.Context, out io.Writer) error {
274- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)
275- if !isRemote {
276- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
283+ resolved, err := resolveRequiredRemote(tokenFlag)
284+ if err != nil {
285+ return err
277286 }
278287 if resolved.Token == "" {
279288 return fmt.Errorf("no token stored for remote '%s'; use: gordon auth login --token <token>", resolved.DisplayName())
@@ -317,9 +326,9 @@ Examples:
317326 }
318327
319328 func runAuthShowToken(out io.Writer) error {
320- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)
321- if !isRemote {
322- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
329+ resolved, err := resolveRequiredRemote(tokenFlag)
330+ if err != nil {
331+ return err
323332 }
324333 if resolved.Token == "" {
325334 return fmt.Errorf("no token found for remote '%s'", resolved.DisplayName())
@@ -355,9 +364,9 @@ Examples:
355364 }
356365
357366 func runAuthLogout(out io.Writer) error {
358- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)
359- if !isRemote {
360- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
367+ resolved, err := resolveRequiredRemote(tokenFlag)
368+ if err != nil {
369+ return err
361370 }
362371 if resolved.Name == "" {
363372 return fmt.Errorf("cannot logout from ad-hoc URL. Use 'gordon remotes add' to save this remote first")
@@ -700,9 +709,9 @@ Examples:
700709
701710 // runAuthStatus checks authentication status for the active remote.
702711 func runAuthStatus(ctx context.Context) error {
703- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)
704- if !isRemote {
705- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
712+ resolved, err := resolveRequiredRemote(tokenFlag)
713+ if err != nil {
714+ return err
706715 }
707716
708717 client := remote.NewClient(resolved.URL, remoteClientOptions(resolved.Token, resolved.InsecureTLS)...)
@@ -232,6 +232,15 @@ Examples:
232 return cmd232 return cmd
233 }233 }
234 234
235+func resolveRequiredRemote(flagToken string) (*remote.ResolvedRemote, error) {
236+ resolved, isRemote := remote.Resolve(remoteFlag, flagToken, insecureTLSFlag)
237+ if !isRemote {
238+ return nil, fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")
239+ }
240+
241+ return resolved, nil
242+}
243+
235 func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) error {244 func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) error {
236 token = strings.TrimSpace(token)245 token = strings.TrimSpace(token)
237 if token == "" {246 if token == "" {
@@ -239,9 +248,9 @@ func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) err
239 }248 }
240 249
241 // Resolve remote — but use the new token (not the stored one) for verification.250 // Resolve remote — but use the new token (not the stored one) for verification.
242- resolved, isRemote := remote.Resolve(remoteFlag, "", insecureTLSFlag)251+ resolved, err := resolveRequiredRemote("")
243- if !isRemote {252+ if err != nil {
244- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")253+ return err
245 }254 }
246 255
247 client := remote.NewClient(resolved.URL, remoteClientOptions(token, resolved.InsecureTLS)...)256 client := remote.NewClient(resolved.URL, remoteClientOptions(token, resolved.InsecureTLS)...)
@@ -271,9 +280,9 @@ func runAuthLoginWithToken(ctx context.Context, token string, out io.Writer) err
271 }280 }
272 281
273 func runAuthLoginVerify(ctx context.Context, out io.Writer) error {282 func runAuthLoginVerify(ctx context.Context, out io.Writer) error {
274- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)283+ resolved, err := resolveRequiredRemote(tokenFlag)
275- if !isRemote {284+ if err != nil {
276- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")285+ return err
277 }286 }
278 if resolved.Token == "" {287 if resolved.Token == "" {
279 return fmt.Errorf("no token stored for remote '%s'; use: gordon auth login --token <token>", resolved.DisplayName())288 return fmt.Errorf("no token stored for remote '%s'; use: gordon auth login --token <token>", resolved.DisplayName())
@@ -317,9 +326,9 @@ Examples:
317 }326 }
318 327
319 func runAuthShowToken(out io.Writer) error {328 func runAuthShowToken(out io.Writer) error {
320- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)329+ resolved, err := resolveRequiredRemote(tokenFlag)
321- if !isRemote {330+ if err != nil {
322- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")331+ return err
323 }332 }
324 if resolved.Token == "" {333 if resolved.Token == "" {
325 return fmt.Errorf("no token found for remote '%s'", resolved.DisplayName())334 return fmt.Errorf("no token found for remote '%s'", resolved.DisplayName())
@@ -355,9 +364,9 @@ Examples:
355 }364 }
356 365
357 func runAuthLogout(out io.Writer) error {366 func runAuthLogout(out io.Writer) error {
358- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)367+ resolved, err := resolveRequiredRemote(tokenFlag)
359- if !isRemote {368+ if err != nil {
360- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")369+ return err
361 }370 }
362 if resolved.Name == "" {371 if resolved.Name == "" {
363 return fmt.Errorf("cannot logout from ad-hoc URL. Use 'gordon remotes add' to save this remote first")372 return fmt.Errorf("cannot logout from ad-hoc URL. Use 'gordon remotes add' to save this remote first")
@@ -700,9 +709,9 @@ Examples:
700 709
701 // runAuthStatus checks authentication status for the active remote.710 // runAuthStatus checks authentication status for the active remote.
702 func runAuthStatus(ctx context.Context) error {711 func runAuthStatus(ctx context.Context) error {
703- resolved, isRemote := remote.Resolve(remoteFlag, tokenFlag, insecureTLSFlag)712+ resolved, err := resolveRequiredRemote(tokenFlag)
704- if !isRemote {713+ if err != nil {
705- return fmt.Errorf("no remote specified. Use --remote or 'gordon remotes use <name>'")714+ return err
706 }715 }
707 716
708 client := remote.NewClient(resolved.URL, remoteClientOptions(resolved.Token, resolved.InsecureTLS)...)717 client := remote.NewClient(resolved.URL, remoteClientOptions(resolved.Token, resolved.InsecureTLS)...)
M internal/adapters/in/cli/deploy.go +1 -4
@@ -71,10 +71,7 @@ func runDeploy(ctx context.Context, deployer deployer, isRemote bool, deployDoma
7171 messageDomain = result.Domain
7272 }
7373
74- containerID := result.ContainerID
75- if containerID != "" && len(containerID) > 12 {
76- containerID = containerID[:12]
77- }
74+ containerID := shortContainerID(result.ContainerID)
7875
7976 switch result.Status {
8077 case "deployed":
@@ -71,10 +71,7 @@ func runDeploy(ctx context.Context, deployer deployer, isRemote bool, deployDoma
71 messageDomain = result.Domain71 messageDomain = result.Domain
72 }72 }
73 73
74- containerID := result.ContainerID74+ containerID := shortContainerID(result.ContainerID)
75- if containerID != "" && len(containerID) > 12 {
76- containerID = containerID[:12]
77- }
78 75
79 switch result.Status {76 switch result.Status {
80 case "deployed":77 case "deployed":
M internal/adapters/in/cli/deploy_error.go +4 -3
@@ -56,14 +56,15 @@ func structuredDeployFailure(err error) (error, bool) {
5656
5757 func renderDeployFailure(summary, cause, hint string, logs []string) string {
5858 if summary == "" {
59- summary = "failed to deploy"
59+ summary = domain.DefaultDeployFailureSummary
6060 }
6161
6262 var b strings.Builder
63- if summary == "failed to deploy" {
63+ if summary == domain.DefaultDeployFailureSummary {
6464 b.WriteString(summary)
6565 } else {
66- b.WriteString("failed to deploy: ")
66+ b.WriteString(domain.DefaultDeployFailureSummary)
67+ b.WriteString(": ")
6768 b.WriteString(summary)
6869 }
6970
@@ -56,14 +56,15 @@ func structuredDeployFailure(err error) (error, bool) {
56 56
57 func renderDeployFailure(summary, cause, hint string, logs []string) string {57 func renderDeployFailure(summary, cause, hint string, logs []string) string {
58 if summary == "" {58 if summary == "" {
59- summary = "failed to deploy"59+ summary = domain.DefaultDeployFailureSummary
60 }60 }
61 61
62 var b strings.Builder62 var b strings.Builder
63- if summary == "failed to deploy" {63+ if summary == domain.DefaultDeployFailureSummary {
64 b.WriteString(summary)64 b.WriteString(summary)
65 } else {65 } else {
66- b.WriteString("failed to deploy: ")66+ b.WriteString(domain.DefaultDeployFailureSummary)
67+ b.WriteString(": ")
67 b.WriteString(summary)68 b.WriteString(summary)
68 }69 }
69 70
M internal/adapters/in/cli/presentation.go +8 -0
@@ -63,3 +63,11 @@ func cliRenderWarning(msg string) string {
6363 func cliRenderInfo(msg string) string {
6464 return styles.RenderInfo(msg)
6565 }
66+
67+func shortContainerID(id string) string {
68+ if len(id) > 12 {
69+ return id[:12]
70+ }
71+
72+ return id
73+}
@@ -63,3 +63,11 @@ func cliRenderWarning(msg string) string {
63 func cliRenderInfo(msg string) string {63 func cliRenderInfo(msg string) string {
64 return styles.RenderInfo(msg)64 return styles.RenderInfo(msg)
65 }65 }
66+
67+func shortContainerID(id string) string {
68+ if len(id) > 12 {
69+ return id[:12]
70+ }
71+
72+ return id
73+}
M internal/adapters/in/cli/push.go +1 -4
@@ -681,10 +681,7 @@ func deployAfterPush(ctx context.Context, cp ControlPlane, pushDomain string, no
681681 if err != nil {
682682 return formatDeployFailure(err)
683683 }
684- containerID := result.ContainerID
685- if len(containerID) > 12 {
686- containerID = containerID[:12]
687- }
684+ containerID := shortContainerID(result.ContainerID)
688685 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Deployed %s (container: %s)", pushDomain, containerID)))
689686 return nil
690687 }
@@ -681,10 +681,7 @@ func deployAfterPush(ctx context.Context, cp ControlPlane, pushDomain string, no
681 if err != nil {681 if err != nil {
682 return formatDeployFailure(err)682 return formatDeployFailure(err)
683 }683 }
684- containerID := result.ContainerID684+ containerID := shortContainerID(result.ContainerID)
685- if len(containerID) > 12 {
686- containerID = containerID[:12]
687- }
688 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Deployed %s (container: %s)", pushDomain, containerID)))685 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Deployed %s (container: %s)", pushDomain, containerID)))
689 return nil686 return nil
690 }687 }
M internal/adapters/in/cli/rollback.go +1 -4
@@ -244,10 +244,7 @@ func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route
244244 }
245245 return fmt.Errorf("failed to deploy (route reverted): %w", err)
246246 }
247- containerID := result.ContainerID
248- if len(containerID) > 12 {
249- containerID = containerID[:12]
250- }
247+ containerID := shortContainerID(result.ContainerID)
251248 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Rolled back %s to %s (container: %s)", rollbackDomain, selectedTag, containerID)))
252249 return nil
253250 }
@@ -244,10 +244,7 @@ func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route
244 }244 }
245 return fmt.Errorf("failed to deploy (route reverted): %w", err)245 return fmt.Errorf("failed to deploy (route reverted): %w", err)
246 }246 }
247- containerID := result.ContainerID247+ containerID := shortContainerID(result.ContainerID)
248- if len(containerID) > 12 {
249- containerID = containerID[:12]
250- }
251 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Rolled back %s to %s (container: %s)", rollbackDomain, selectedTag, containerID)))248 fmt.Println(styles.RenderSuccess(fmt.Sprintf("Rolled back %s to %s (container: %s)", rollbackDomain, selectedTag, containerID)))
252 return nil249 return nil
253 }250 }
M internal/domain/deploy_failure.go +3 -1
@@ -1,5 +1,7 @@
11 package domain
22
3+const DefaultDeployFailureSummary = "failed to deploy"
4+
35 // DeployFailureError describes a surfaced deployment failure.
46 type DeployFailureError struct {
57 Summary string
@@ -14,7 +16,7 @@ type DeployFailureError struct {
1416 // Error returns the deploy failure summary.
1517 func (e *DeployFailureError) Error() string {
1618 if e == nil || e.Summary == "" {
17- return "failed to deploy"
19+ return DefaultDeployFailureSummary
1820 }
1921
2022 return e.Summary
@@ -1,5 +1,7 @@
1 package domain1 package domain
2 2
3+const DefaultDeployFailureSummary = "failed to deploy"
4+
3 // DeployFailureError describes a surfaced deployment failure.5 // DeployFailureError describes a surfaced deployment failure.
4 type DeployFailureError struct {6 type DeployFailureError struct {
5 Summary string7 Summary string
@@ -14,7 +16,7 @@ type DeployFailureError struct {
14 // Error returns the deploy failure summary.16 // Error returns the deploy failure summary.
15 func (e *DeployFailureError) Error() string {17 func (e *DeployFailureError) Error() string {
16 if e == nil || e.Summary == "" {18 if e == nil || e.Summary == "" {
17- return "failed to deploy"19+ return DefaultDeployFailureSummary
18 }20 }
19 21
20 return e.Summary22 return e.Summary

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 7

Tags 106

↑↓ moveenter openesc close