refactor(cli): rename rollback command to pin
- commit
- b1fd1ced929b67b039def2f484faf91cf0b2f498
- author
- brice · · 2026-04-28 02:52 UTC
- parent
- 062dcae
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +28 -28
R internal/adapters/in/cli/rollback.go → internal/adapters/in/cli/pin.go +25 -25 renamed, 76% similar
| @@ -16,40 +16,40 @@ import ( | |||
| 16 | 16 | "github.com/bnema/gordon/internal/domain" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | - | func newRollbackCmd() *cobra.Command { | |
| 19 | + | func newPinCmd() *cobra.Command { | |
| 20 | 20 | var targetTag string | |
| 21 | 21 | ||
| 22 | 22 | cmd := &cobra.Command{ | |
| 23 | - | Use: "rollback <domain>", | |
| 24 | - | Short: "Roll back to a previous image version", | |
| 23 | + | Use: "pin <domain>", | |
| 24 | + | Short: "Pin a route to a specific image tag", | |
| 25 | 25 | Long: `Lists available image tags for a domain and deploys the selected version. | |
| 26 | 26 | Tags are read from the Gordon registry. | |
| 27 | 27 | ||
| 28 | 28 | Examples: | |
| 29 | - | gordon rollback myapp.example.com --remote ... | |
| 30 | - | gordon rollback myapp.example.com --tag v1.0.0 --remote ... | |
| 31 | - | gordon rollback list myapp.example.com --remote ...`, | |
| 29 | + | gordon pin myapp.example.com --remote ... | |
| 30 | + | gordon pin myapp.example.com --tag v1.0.0 --remote ... | |
| 31 | + | gordon pin list myapp.example.com --remote ...`, | |
| 32 | 32 | Args: cobra.ExactArgs(1), | |
| 33 | 33 | RunE: func(cmd *cobra.Command, args []string) error { | |
| 34 | - | return runRollback(cmd.Context(), args[0], targetTag) | |
| 34 | + | return runPin(cmd.Context(), args[0], targetTag) | |
| 35 | 35 | }, | |
| 36 | 36 | } | |
| 37 | 37 | ||
| 38 | 38 | cmd.Flags().StringVar(&targetTag, "tag", "", "Target tag (skips interactive selection)") | |
| 39 | - | cmd.AddCommand(newRollbackListCmd()) | |
| 39 | + | cmd.AddCommand(newPinListCmd()) | |
| 40 | 40 | ||
| 41 | 41 | return cmd | |
| 42 | 42 | } | |
| 43 | 43 | ||
| 44 | - | func newRollbackListCmd() *cobra.Command { | |
| 44 | + | func newPinListCmd() *cobra.Command { | |
| 45 | 45 | var jsonOut bool | |
| 46 | 46 | ||
| 47 | 47 | cmd := &cobra.Command{ | |
| 48 | 48 | Use: "list <domain>", | |
| 49 | - | Short: "List available rollback tags", | |
| 49 | + | Short: "List available tags", | |
| 50 | 50 | Args: cobra.ExactArgs(1), | |
| 51 | 51 | RunE: func(cmd *cobra.Command, args []string) error { | |
| 52 | - | return runRollbackList(cmd.Context(), cmd.OutOrStdout(), args[0], jsonOut) | |
| 52 | + | return runPinList(cmd.Context(), cmd.OutOrStdout(), args[0], jsonOut) | |
| 53 | 53 | }, | |
| 54 | 54 | } | |
| 55 | 55 | ||
| @@ -58,14 +58,14 @@ func newRollbackListCmd() *cobra.Command { | |||
| 58 | 58 | return cmd | |
| 59 | 59 | } | |
| 60 | 60 | ||
| 61 | - | func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | |
| 61 | + | func runPin(ctx context.Context, pinDomain, targetTag string) error { | |
| 62 | 62 | handle, err := resolveControlPlane(configPath) | |
| 63 | 63 | if err != nil { | |
| 64 | 64 | return err | |
| 65 | 65 | } | |
| 66 | 66 | defer handle.close() | |
| 67 | 67 | ||
| 68 | - | route, err := handle.plane.GetRoute(ctx, rollbackDomain) | |
| 68 | + | route, err := handle.plane.GetRoute(ctx, pinDomain) | |
| 69 | 69 | if err != nil { | |
| 70 | 70 | return fmt.Errorf("failed to get route: %w", err) | |
| 71 | 71 | } | |
| @@ -80,7 +80,7 @@ func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | |||
| 80 | 80 | return err | |
| 81 | 81 | } | |
| 82 | 82 | ||
| 83 | - | selectedTag, err := selectTag(targetTag, tags, currentTag, rollbackDomain) | |
| 83 | + | selectedTag, err := selectTag(targetTag, tags, currentTag, pinDomain) | |
| 84 | 84 | if err != nil || selectedTag == "" { | |
| 85 | 85 | return err | |
| 86 | 86 | } | |
| @@ -90,17 +90,17 @@ func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | |||
| 90 | 90 | return nil | |
| 91 | 91 | } | |
| 92 | 92 | ||
| 93 | - | return deploySelectedTag(ctx, handle.plane, route, rollbackDomain, imageName, selectedTag) | |
| 93 | + | return deploySelectedTag(ctx, handle.plane, route, pinDomain, imageName, selectedTag) | |
| 94 | 94 | } | |
| 95 | 95 | ||
| 96 | - | func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string, jsonOut bool) error { | |
| 96 | + | func runPinList(ctx context.Context, w io.Writer, pinDomain string, jsonOut bool) error { | |
| 97 | 97 | handle, err := resolveControlPlane(configPath) | |
| 98 | 98 | if err != nil { | |
| 99 | 99 | return err | |
| 100 | 100 | } | |
| 101 | 101 | defer handle.close() | |
| 102 | 102 | ||
| 103 | - | route, err := handle.plane.GetRoute(ctx, rollbackDomain) | |
| 103 | + | route, err := handle.plane.GetRoute(ctx, pinDomain) | |
| 104 | 104 | if err != nil { | |
| 105 | 105 | return fmt.Errorf("failed to get route: %w", err) | |
| 106 | 106 | } | |
| @@ -115,19 +115,19 @@ func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string, js | |||
| 115 | 115 | return err | |
| 116 | 116 | } | |
| 117 | 117 | ||
| 118 | - | return printRollbackTags(w, rollbackDomain, currentTag, tags, jsonOut) | |
| 118 | + | return printPinTags(w, pinDomain, currentTag, tags, jsonOut) | |
| 119 | 119 | } | |
| 120 | 120 | ||
| 121 | - | func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string, jsonOut bool) error { | |
| 121 | + | func printPinTags(w io.Writer, pinDomain, currentTag string, tags []string, jsonOut bool) error { | |
| 122 | 122 | if jsonOut { | |
| 123 | 123 | return writeJSON(w, map[string]any{ | |
| 124 | - | "domain": rollbackDomain, | |
| 124 | + | "domain": pinDomain, | |
| 125 | 125 | "current_tag": currentTag, | |
| 126 | 126 | "tags": tags, | |
| 127 | 127 | }) | |
| 128 | 128 | } | |
| 129 | 129 | ||
| 130 | - | if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain)); err != nil { | |
| 130 | + | if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(pinDomain)); err != nil { | |
| 131 | 131 | return err | |
| 132 | 132 | } | |
| 133 | 133 | for _, tag := range tags { | |
| @@ -223,18 +223,18 @@ func validateTagExists(targetTag string, tags []string) bool { | |||
| 223 | 223 | return false | |
| 224 | 224 | } | |
| 225 | 225 | ||
| 226 | - | func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route, rollbackDomain, imageName, selectedTag string) error { | |
| 226 | + | func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route, pinDomain, imageName, selectedTag string) error { | |
| 227 | 227 | registry, _, _ := parseImageRef(route.Image) | |
| 228 | 228 | oldImage := route.Image | |
| 229 | 229 | route.Image = fmt.Sprintf("%s/%s:%s", registry, imageName, selectedTag) | |
| 230 | 230 | ||
| 231 | - | fmt.Printf("Rolling back to %s...\n", styles.Theme.Bold.Render(selectedTag)) | |
| 231 | + | fmt.Printf("Pinning to %s...\n", styles.Theme.Bold.Render(selectedTag)) | |
| 232 | 232 | ||
| 233 | 233 | if err := cp.UpdateRoute(ctx, *route); err != nil { | |
| 234 | 234 | return fmt.Errorf("failed to update route: %w", err) | |
| 235 | 235 | } | |
| 236 | 236 | ||
| 237 | - | result, err := cp.Deploy(ctx, rollbackDomain) | |
| 237 | + | result, err := cp.Deploy(ctx, pinDomain) | |
| 238 | 238 | if err != nil { | |
| 239 | 239 | // Attempt to revert route to previous image | |
| 240 | 240 | route.Image = oldImage | |
| @@ -245,6 +245,6 @@ func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route | |||
| 245 | 245 | return fmt.Errorf("failed to deploy (route reverted): %w", err) | |
| 246 | 246 | } | |
| 247 | 247 | containerID := shortContainerID(result.ContainerID) | |
| 248 | - | fmt.Println(styles.RenderSuccess(fmt.Sprintf("Rolled back %s to %s (container: %s)", rollbackDomain, selectedTag, containerID))) | |
| 248 | + | fmt.Println(styles.RenderSuccess(fmt.Sprintf("Pinned %s to %s (container: %s)", pinDomain, selectedTag, containerID))) | |
| 249 | 249 | return nil | |
| 250 | 250 | } | |
| @@ -16,40 +16,40 @@ import ( | |||||
| 16 | "github.com/bnema/gordon/internal/domain" | 16 | "github.com/bnema/gordon/internal/domain" | ||
| 17 | ) | 17 | ) | ||
| 18 | 18 | ||||
| 19 | - | func newRollbackCmd() *cobra.Command { | 19 | + | func newPinCmd() *cobra.Command { |
| 20 | var targetTag string | 20 | var targetTag string | ||
| 21 | 21 | ||||
| 22 | cmd := &cobra.Command{ | 22 | cmd := &cobra.Command{ | ||
| 23 | - | Use: "rollback <domain>", | 23 | + | Use: "pin <domain>", |
| 24 | - | Short: "Roll back to a previous image version", | 24 | + | Short: "Pin a route to a specific image tag", |
| 25 | Long: `Lists available image tags for a domain and deploys the selected version. | 25 | Long: `Lists available image tags for a domain and deploys the selected version. | ||
| 26 | Tags are read from the Gordon registry. | 26 | Tags are read from the Gordon registry. | ||
| 27 | 27 | ||||
| 28 | Examples: | 28 | Examples: | ||
| 29 | - | gordon rollback myapp.example.com --remote ... | 29 | + | gordon pin myapp.example.com --remote ... |
| 30 | - | gordon rollback myapp.example.com --tag v1.0.0 --remote ... | 30 | + | gordon pin myapp.example.com --tag v1.0.0 --remote ... |
| 31 | - | gordon rollback list myapp.example.com --remote ...`, | 31 | + | gordon pin list myapp.example.com --remote ...`, |
| 32 | Args: cobra.ExactArgs(1), | 32 | Args: cobra.ExactArgs(1), | ||
| 33 | RunE: func(cmd *cobra.Command, args []string) error { | 33 | RunE: func(cmd *cobra.Command, args []string) error { | ||
| 34 | - | return runRollback(cmd.Context(), args[0], targetTag) | 34 | + | return runPin(cmd.Context(), args[0], targetTag) |
| 35 | }, | 35 | }, | ||
| 36 | } | 36 | } | ||
| 37 | 37 | ||||
| 38 | cmd.Flags().StringVar(&targetTag, "tag", "", "Target tag (skips interactive selection)") | 38 | cmd.Flags().StringVar(&targetTag, "tag", "", "Target tag (skips interactive selection)") | ||
| 39 | - | cmd.AddCommand(newRollbackListCmd()) | 39 | + | cmd.AddCommand(newPinListCmd()) |
| 40 | 40 | ||||
| 41 | return cmd | 41 | return cmd | ||
| 42 | } | 42 | } | ||
| 43 | 43 | ||||
| 44 | - | func newRollbackListCmd() *cobra.Command { | 44 | + | func newPinListCmd() *cobra.Command { |
| 45 | var jsonOut bool | 45 | var jsonOut bool | ||
| 46 | 46 | ||||
| 47 | cmd := &cobra.Command{ | 47 | cmd := &cobra.Command{ | ||
| 48 | Use: "list <domain>", | 48 | Use: "list <domain>", | ||
| 49 | - | Short: "List available rollback tags", | 49 | + | Short: "List available tags", |
| 50 | Args: cobra.ExactArgs(1), | 50 | Args: cobra.ExactArgs(1), | ||
| 51 | RunE: func(cmd *cobra.Command, args []string) error { | 51 | RunE: func(cmd *cobra.Command, args []string) error { | ||
| 52 | - | return runRollbackList(cmd.Context(), cmd.OutOrStdout(), args[0], jsonOut) | 52 | + | return runPinList(cmd.Context(), cmd.OutOrStdout(), args[0], jsonOut) |
| 53 | }, | 53 | }, | ||
| 54 | } | 54 | } | ||
| 55 | 55 | ||||
| @@ -58,14 +58,14 @@ func newRollbackListCmd() *cobra.Command { | |||||
| 58 | return cmd | 58 | return cmd | ||
| 59 | } | 59 | } | ||
| 60 | 60 | ||||
| 61 | - | func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | 61 | + | func runPin(ctx context.Context, pinDomain, targetTag string) error { |
| 62 | handle, err := resolveControlPlane(configPath) | 62 | handle, err := resolveControlPlane(configPath) | ||
| 63 | if err != nil { | 63 | if err != nil { | ||
| 64 | return err | 64 | return err | ||
| 65 | } | 65 | } | ||
| 66 | defer handle.close() | 66 | defer handle.close() | ||
| 67 | 67 | ||||
| 68 | - | route, err := handle.plane.GetRoute(ctx, rollbackDomain) | 68 | + | route, err := handle.plane.GetRoute(ctx, pinDomain) |
| 69 | if err != nil { | 69 | if err != nil { | ||
| 70 | return fmt.Errorf("failed to get route: %w", err) | 70 | return fmt.Errorf("failed to get route: %w", err) | ||
| 71 | } | 71 | } | ||
| @@ -80,7 +80,7 @@ func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | |||||
| 80 | return err | 80 | return err | ||
| 81 | } | 81 | } | ||
| 82 | 82 | ||||
| 83 | - | selectedTag, err := selectTag(targetTag, tags, currentTag, rollbackDomain) | 83 | + | selectedTag, err := selectTag(targetTag, tags, currentTag, pinDomain) |
| 84 | if err != nil || selectedTag == "" { | 84 | if err != nil || selectedTag == "" { | ||
| 85 | return err | 85 | return err | ||
| 86 | } | 86 | } | ||
| @@ -90,17 +90,17 @@ func runRollback(ctx context.Context, rollbackDomain, targetTag string) error { | |||||
| 90 | return nil | 90 | return nil | ||
| 91 | } | 91 | } | ||
| 92 | 92 | ||||
| 93 | - | return deploySelectedTag(ctx, handle.plane, route, rollbackDomain, imageName, selectedTag) | 93 | + | return deploySelectedTag(ctx, handle.plane, route, pinDomain, imageName, selectedTag) |
| 94 | } | 94 | } | ||
| 95 | 95 | ||||
| 96 | - | func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string, jsonOut bool) error { | 96 | + | func runPinList(ctx context.Context, w io.Writer, pinDomain string, jsonOut bool) error { |
| 97 | handle, err := resolveControlPlane(configPath) | 97 | handle, err := resolveControlPlane(configPath) | ||
| 98 | if err != nil { | 98 | if err != nil { | ||
| 99 | return err | 99 | return err | ||
| 100 | } | 100 | } | ||
| 101 | defer handle.close() | 101 | defer handle.close() | ||
| 102 | 102 | ||||
| 103 | - | route, err := handle.plane.GetRoute(ctx, rollbackDomain) | 103 | + | route, err := handle.plane.GetRoute(ctx, pinDomain) |
| 104 | if err != nil { | 104 | if err != nil { | ||
| 105 | return fmt.Errorf("failed to get route: %w", err) | 105 | return fmt.Errorf("failed to get route: %w", err) | ||
| 106 | } | 106 | } | ||
| @@ -115,19 +115,19 @@ func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string, js | |||||
| 115 | return err | 115 | return err | ||
| 116 | } | 116 | } | ||
| 117 | 117 | ||||
| 118 | - | return printRollbackTags(w, rollbackDomain, currentTag, tags, jsonOut) | 118 | + | return printPinTags(w, pinDomain, currentTag, tags, jsonOut) |
| 119 | } | 119 | } | ||
| 120 | 120 | ||||
| 121 | - | func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string, jsonOut bool) error { | 121 | + | func printPinTags(w io.Writer, pinDomain, currentTag string, tags []string, jsonOut bool) error { |
| 122 | if jsonOut { | 122 | if jsonOut { | ||
| 123 | return writeJSON(w, map[string]any{ | 123 | return writeJSON(w, map[string]any{ | ||
| 124 | - | "domain": rollbackDomain, | 124 | + | "domain": pinDomain, |
| 125 | "current_tag": currentTag, | 125 | "current_tag": currentTag, | ||
| 126 | "tags": tags, | 126 | "tags": tags, | ||
| 127 | }) | 127 | }) | ||
| 128 | } | 128 | } | ||
| 129 | 129 | ||||
| 130 | - | if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain)); err != nil { | 130 | + | if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(pinDomain)); err != nil { |
| 131 | return err | 131 | return err | ||
| 132 | } | 132 | } | ||
| 133 | for _, tag := range tags { | 133 | for _, tag := range tags { | ||
| @@ -223,18 +223,18 @@ func validateTagExists(targetTag string, tags []string) bool { | |||||
| 223 | return false | 223 | return false | ||
| 224 | } | 224 | } | ||
| 225 | 225 | ||||
| 226 | - | func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route, rollbackDomain, imageName, selectedTag string) error { | 226 | + | func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route, pinDomain, imageName, selectedTag string) error { |
| 227 | registry, _, _ := parseImageRef(route.Image) | 227 | registry, _, _ := parseImageRef(route.Image) | ||
| 228 | oldImage := route.Image | 228 | oldImage := route.Image | ||
| 229 | route.Image = fmt.Sprintf("%s/%s:%s", registry, imageName, selectedTag) | 229 | route.Image = fmt.Sprintf("%s/%s:%s", registry, imageName, selectedTag) | ||
| 230 | 230 | ||||
| 231 | - | fmt.Printf("Rolling back to %s...\n", styles.Theme.Bold.Render(selectedTag)) | 231 | + | fmt.Printf("Pinning to %s...\n", styles.Theme.Bold.Render(selectedTag)) |
| 232 | 232 | ||||
| 233 | if err := cp.UpdateRoute(ctx, *route); err != nil { | 233 | if err := cp.UpdateRoute(ctx, *route); err != nil { | ||
| 234 | return fmt.Errorf("failed to update route: %w", err) | 234 | return fmt.Errorf("failed to update route: %w", err) | ||
| 235 | } | 235 | } | ||
| 236 | 236 | ||||
| 237 | - | result, err := cp.Deploy(ctx, rollbackDomain) | 237 | + | result, err := cp.Deploy(ctx, pinDomain) |
| 238 | if err != nil { | 238 | if err != nil { | ||
| 239 | // Attempt to revert route to previous image | 239 | // Attempt to revert route to previous image | ||
| 240 | route.Image = oldImage | 240 | route.Image = oldImage | ||
| @@ -245,6 +245,6 @@ func deploySelectedTag(ctx context.Context, cp ControlPlane, route *domain.Route | |||||
| 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 := shortContainerID(result.ContainerID) | 247 | containerID := shortContainerID(result.ContainerID) | ||
| 248 | - | fmt.Println(styles.RenderSuccess(fmt.Sprintf("Rolled back %s to %s (container: %s)", rollbackDomain, selectedTag, containerID))) | 248 | + | fmt.Println(styles.RenderSuccess(fmt.Sprintf("Pinned %s to %s (container: %s)", pinDomain, selectedTag, containerID))) |
| 249 | return nil | 249 | return nil | ||
| 250 | } | 250 | } | ||
M internal/adapters/in/cli/root.go +3 -3
| @@ -101,9 +101,9 @@ Commands are organized by where they run: | |||
| 101 | 101 | pushCmd.GroupID = groupManage | |
| 102 | 102 | rootCmd.AddCommand(pushCmd) | |
| 103 | 103 | ||
| 104 | - | rollbackCmd := newRollbackCmd() | |
| 105 | - | rollbackCmd.GroupID = groupManage | |
| 106 | - | rootCmd.AddCommand(rollbackCmd) | |
| 104 | + | pinCmd := newPinCmd() | |
| 105 | + | pinCmd.GroupID = groupManage | |
| 106 | + | rootCmd.AddCommand(pinCmd) | |
| 107 | 107 | ||
| 108 | 108 | reloadCmd := newReloadCmd() | |
| 109 | 109 | reloadCmd.GroupID = groupManage | |
| @@ -101,9 +101,9 @@ Commands are organized by where they run: | |||||
| 101 | pushCmd.GroupID = groupManage | 101 | pushCmd.GroupID = groupManage | ||
| 102 | rootCmd.AddCommand(pushCmd) | 102 | rootCmd.AddCommand(pushCmd) | ||
| 103 | 103 | ||||
| 104 | - | rollbackCmd := newRollbackCmd() | 104 | + | pinCmd := newPinCmd() |
| 105 | - | rollbackCmd.GroupID = groupManage | 105 | + | pinCmd.GroupID = groupManage |
| 106 | - | rootCmd.AddCommand(rollbackCmd) | 106 | + | rootCmd.AddCommand(pinCmd) |
| 107 | 107 | ||||
| 108 | reloadCmd := newReloadCmd() | 108 | reloadCmd := newReloadCmd() | ||
| 109 | reloadCmd.GroupID = groupManage | 109 | reloadCmd.GroupID = groupManage | ||