detached 1929353

refactor: introduce parameter structs for push command functions

Replace long parameter lists in buildAndPush, tagAndPush, runPush, and
performAttachmentPush with buildConfig, imagePush, pushRequest, and
attachmentPushRequest structs. Reduces parameter counts from 8-11 to 2-3.
commit
1929353ecb283d37fdad04d8c7b335478b871dbd
author
brice · · 2026-03-14 17:22 UTC
parent
8fce1c3
signature
unsigned
3 files · +125 -50
M internal/adapters/in/cli/attachments_push.go +36 -13
@@ -18,6 +18,13 @@ var (
1818 determineVersionFn = determineVersion
1919 )
2020
21+// attachmentPushRequest holds all inputs for the attachments push command.
22+type attachmentPushRequest struct {
23+ ImageArg string
24+ Tag string
25+ Build buildConfig
26+}
27+
2128 func newAttachmentsPushCmd() *cobra.Command {
2229 var (
2330 build bool
@@ -32,7 +39,11 @@ func newAttachmentsPushCmd() *cobra.Command {
3239 Short: "Build, tag, and push an attachment image",
3340 Args: cobra.ExactArgs(1),
3441 RunE: func(cmd *cobra.Command, args []string) error {
35- return runAttachmentsPush(cmd.Context(), args[0], tag, build, platform, dockerfile, buildArgs, cmd.OutOrStdout())
42+ return runAttachmentsPush(cmd.Context(), attachmentPushRequest{
43+ ImageArg: args[0],
44+ Tag: tag,
45+ Build: buildConfig{Enabled: build, Platform: platform, Dockerfile: dockerfile, BuildArgs: buildArgs},
46+ }, cmd.OutOrStdout())
3647 },
3748 }
3849
@@ -45,46 +56,58 @@ func newAttachmentsPushCmd() *cobra.Command {
4556 return cmd
4657 }
4758
48-func runAttachmentsPush(ctx context.Context, imageArg, tag string, build bool, platform string, dockerfile string, buildArgs []string, out io.Writer) error {
59+func runAttachmentsPush(ctx context.Context, req attachmentPushRequest, out io.Writer) error {
4960 handle, err := resolveControlPlaneFn(configPath)
5061 if err != nil {
5162 return err
5263 }
5364 defer handle.close()
5465
55- dockerfile, err = resolveDockerfile(dockerfile, build)
66+ dockerfile, err := resolveDockerfile(req.Build.Dockerfile, req.Build.Enabled)
5667 if err != nil {
5768 return err
5869 }
5970
60- for _, ba := range buildArgs {
71+ for _, ba := range req.Build.BuildArgs {
6172 if err := validateBuildArg(ba); err != nil {
6273 return err
6374 }
6475 }
6576
66- registry, imageName, targets, err := resolveAttachmentImage(ctx, handle.plane, imageArg)
77+ registry, imageName, targets, err := resolveAttachmentImage(ctx, handle.plane, req.ImageArg)
6778 if err != nil {
6879 return err
6980 }
7081
71- version, err := resolveVersionWithFn(ctx, tag)
82+ version, err := resolveVersionWithFn(ctx, req.Tag)
7283 if err != nil {
7384 return err
7485 }
7586
76- versionRef, latestRef := resolveImageRefs(registry, imageName, version)
87+ img := imagePush{
88+ Registry: registry,
89+ ImageName: imageName,
90+ Version: version,
91+ }
92+ img.VersionRef, img.LatestRef = resolveImageRefs(registry, imageName, version)
7793
7894 imageOps, err := newImageOpsFn()
7995 if err != nil {
8096 return err
8197 }
8298
83- if err := printAttachmentPushInfo(out, imageArg, targets, versionRef, latestRef, version); err != nil {
99+ if err := printAttachmentPushInfo(out, req.ImageArg, targets, img.VersionRef, img.LatestRef, version); err != nil {
84100 return err
85101 }
86102
87- if err := performAttachmentPush(ctx, imageOps, build, version, platform, dockerfile, buildArgs, registry, imageName, versionRef, latestRef); err != nil {
103+ build := buildConfig{
104+ Enabled: req.Build.Enabled,
105+ Platform: req.Build.Platform,
106+ Dockerfile: dockerfile,
107+ BuildArgs: req.Build.BuildArgs,
108+ }
109+
110+ if err := performAttachmentPush(ctx, imageOps, build, img); err != nil {
88111 return err
89112 }
90113
@@ -109,11 +132,11 @@ func printAttachmentPushInfo(out io.Writer, imageArg string, targets []string, v
109132 return nil
110133 }
111134
112-func performAttachmentPush(ctx context.Context, ops pushImageOps, build bool, version, platform, dockerfile string, buildArgs []string, registry, imageName, versionRef, latestRef string) error {
113- if build {
114- return buildAndPush(ctx, ops, version, platform, dockerfile, buildArgs, versionRef, latestRef)
135+func performAttachmentPush(ctx context.Context, ops pushImageOps, build buildConfig, img imagePush) error {
136+ if build.Enabled {
137+ return buildAndPush(ctx, ops, build, img)
115138 }
116- return tagAndPush(ctx, ops, registry, imageName, version, versionRef, latestRef)
139+ return tagAndPush(ctx, ops, img)
117140 }
118141
119142 func resolveAttachmentImage(ctx context.Context, cp ControlPlane, imageArg string) (registry, imageName string, targets []string, err error) {
@@ -18,6 +18,13 @@ var (
18 determineVersionFn = determineVersion18 determineVersionFn = determineVersion
19 )19 )
20 20
21+// attachmentPushRequest holds all inputs for the attachments push command.
22+type attachmentPushRequest struct {
23+ ImageArg string
24+ Tag string
25+ Build buildConfig
26+}
27+
21 func newAttachmentsPushCmd() *cobra.Command {28 func newAttachmentsPushCmd() *cobra.Command {
22 var (29 var (
23 build bool30 build bool
@@ -32,7 +39,11 @@ func newAttachmentsPushCmd() *cobra.Command {
32 Short: "Build, tag, and push an attachment image",39 Short: "Build, tag, and push an attachment image",
33 Args: cobra.ExactArgs(1),40 Args: cobra.ExactArgs(1),
34 RunE: func(cmd *cobra.Command, args []string) error {41 RunE: func(cmd *cobra.Command, args []string) error {
35- return runAttachmentsPush(cmd.Context(), args[0], tag, build, platform, dockerfile, buildArgs, cmd.OutOrStdout())42+ return runAttachmentsPush(cmd.Context(), attachmentPushRequest{
43+ ImageArg: args[0],
44+ Tag: tag,
45+ Build: buildConfig{Enabled: build, Platform: platform, Dockerfile: dockerfile, BuildArgs: buildArgs},
46+ }, cmd.OutOrStdout())
36 },47 },
37 }48 }
38 49
@@ -45,46 +56,58 @@ func newAttachmentsPushCmd() *cobra.Command {
45 return cmd56 return cmd
46 }57 }
47 58
48-func runAttachmentsPush(ctx context.Context, imageArg, tag string, build bool, platform string, dockerfile string, buildArgs []string, out io.Writer) error {59+func runAttachmentsPush(ctx context.Context, req attachmentPushRequest, out io.Writer) error {
49 handle, err := resolveControlPlaneFn(configPath)60 handle, err := resolveControlPlaneFn(configPath)
50 if err != nil {61 if err != nil {
51 return err62 return err
52 }63 }
53 defer handle.close()64 defer handle.close()
54 65
55- dockerfile, err = resolveDockerfile(dockerfile, build)66+ dockerfile, err := resolveDockerfile(req.Build.Dockerfile, req.Build.Enabled)
56 if err != nil {67 if err != nil {
57 return err68 return err
58 }69 }
59 70
60- for _, ba := range buildArgs {71+ for _, ba := range req.Build.BuildArgs {
61 if err := validateBuildArg(ba); err != nil {72 if err := validateBuildArg(ba); err != nil {
62 return err73 return err
63 }74 }
64 }75 }
65 76
66- registry, imageName, targets, err := resolveAttachmentImage(ctx, handle.plane, imageArg)77+ registry, imageName, targets, err := resolveAttachmentImage(ctx, handle.plane, req.ImageArg)
67 if err != nil {78 if err != nil {
68 return err79 return err
69 }80 }
70 81
71- version, err := resolveVersionWithFn(ctx, tag)82+ version, err := resolveVersionWithFn(ctx, req.Tag)
72 if err != nil {83 if err != nil {
73 return err84 return err
74 }85 }
75 86
76- versionRef, latestRef := resolveImageRefs(registry, imageName, version)87+ img := imagePush{
88+ Registry: registry,
89+ ImageName: imageName,
90+ Version: version,
91+ }
92+ img.VersionRef, img.LatestRef = resolveImageRefs(registry, imageName, version)
77 93
78 imageOps, err := newImageOpsFn()94 imageOps, err := newImageOpsFn()
79 if err != nil {95 if err != nil {
80 return err96 return err
81 }97 }
82 98
83- if err := printAttachmentPushInfo(out, imageArg, targets, versionRef, latestRef, version); err != nil {99+ if err := printAttachmentPushInfo(out, req.ImageArg, targets, img.VersionRef, img.LatestRef, version); err != nil {
84 return err100 return err
85 }101 }
86 102
87- if err := performAttachmentPush(ctx, imageOps, build, version, platform, dockerfile, buildArgs, registry, imageName, versionRef, latestRef); err != nil {103+ build := buildConfig{
104+ Enabled: req.Build.Enabled,
105+ Platform: req.Build.Platform,
106+ Dockerfile: dockerfile,
107+ BuildArgs: req.Build.BuildArgs,
108+ }
109+
110+ if err := performAttachmentPush(ctx, imageOps, build, img); err != nil {
88 return err111 return err
89 }112 }
90 113
@@ -109,11 +132,11 @@ func printAttachmentPushInfo(out io.Writer, imageArg string, targets []string, v
109 return nil132 return nil
110 }133 }
111 134
112-func performAttachmentPush(ctx context.Context, ops pushImageOps, build bool, version, platform, dockerfile string, buildArgs []string, registry, imageName, versionRef, latestRef string) error {135+func performAttachmentPush(ctx context.Context, ops pushImageOps, build buildConfig, img imagePush) error {
113- if build {136+ if build.Enabled {
114- return buildAndPush(ctx, ops, version, platform, dockerfile, buildArgs, versionRef, latestRef)137+ return buildAndPush(ctx, ops, build, img)
115 }138 }
116- return tagAndPush(ctx, ops, registry, imageName, version, versionRef, latestRef)139+ return tagAndPush(ctx, ops, img)
117 }140 }
118 141
119 func resolveAttachmentImage(ctx context.Context, cp ControlPlane, imageArg string) (registry, imageName string, targets []string, err error) {142 func resolveAttachmentImage(ctx context.Context, cp ControlPlane, imageArg string) (registry, imageName string, targets []string, err error) {
M internal/adapters/in/cli/push.go +82 -36
@@ -23,6 +23,33 @@ import (
2323
2424 var newImageOpsFn = newImageOpsFromFlags
2525
26+// buildConfig holds image build settings for push commands.
27+type buildConfig struct {
28+ Enabled bool
29+ Platform string
30+ Dockerfile string
31+ BuildArgs []string
32+}
33+
34+// imagePush holds resolved image identity for push operations.
35+type imagePush struct {
36+ Registry string
37+ ImageName string
38+ Version string
39+ VersionRef string
40+ LatestRef string
41+}
42+
43+// pushRequest holds all inputs for the push command.
44+type pushRequest struct {
45+ ImageArg string
46+ Domain string
47+ Tag string
48+ Build buildConfig
49+ NoDeploy bool
50+ NoConfirm bool
51+}
52+
2653 func newPushCmd() *cobra.Command {
2754 var (
2855 noDeploy bool
@@ -60,7 +87,14 @@ Examples:
6087 if len(args) > 0 {
6188 imageArg = args[0]
6289 }
63- return runPush(cmd.Context(), imageArg, domainFlag, tag, build, platform, dockerfile, buildArgs, noDeploy, noConfirm)
90+ return runPush(cmd.Context(), pushRequest{
91+ ImageArg: imageArg,
92+ Domain: domainFlag,
93+ Tag: tag,
94+ Build: buildConfig{Enabled: build, Platform: platform, Dockerfile: dockerfile, BuildArgs: buildArgs},
95+ NoDeploy: noDeploy,
96+ NoConfirm: noConfirm,
97+ })
6498 },
6599 }
66100
@@ -120,70 +154,82 @@ func resolveVersion(ctx context.Context, tag string) (string, error) {
120154 return version, nil
121155 }
122156
123-func runPush(ctx context.Context, imageArg, domainFlag, tag string, build bool, platform string, dockerfile string, buildArgs []string, noDeploy bool, noConfirm bool) error {
157+func runPush(ctx context.Context, req pushRequest) error {
124158 handle, err := resolveControlPlane(configPath)
125159 if err != nil {
126160 return err
127161 }
128162 defer handle.close()
129163
130- dockerfile, err = resolveDockerfile(dockerfile, build)
164+ dockerfile, err := resolveDockerfile(req.Build.Dockerfile, req.Build.Enabled)
131165 if err != nil {
132166 return err
133167 }
134168
135- registry, imageName, pushDomain, err := resolveRoute(ctx, handle.plane, imageArg, domainFlag, dockerfile)
169+ registry, imageName, pushDomain, err := resolveRoute(ctx, handle.plane, req.ImageArg, req.Domain, dockerfile)
136170 if err != nil {
137171 return err
138172 }
139173
140- version, err := resolveVersion(ctx, tag)
174+ version, err := resolveVersion(ctx, req.Tag)
141175 if err != nil {
142176 return err
143177 }
144178
145- for _, ba := range buildArgs {
179+ for _, ba := range req.Build.BuildArgs {
146180 if err := validateBuildArg(ba); err != nil {
147181 return err
148182 }
149183 }
150184
151- versionRef, latestRef := resolveImageRefs(registry, imageName, version)
185+ img := imagePush{
186+ Registry: registry,
187+ ImageName: imageName,
188+ Version: version,
189+ }
190+ img.VersionRef, img.LatestRef = resolveImageRefs(registry, imageName, version)
152191
153192 imageOps, err := newImageOpsFn()
154193 if err != nil {
155194 return err
156195 }
157196
158- fmt.Printf("Image: %s\n", styles.Theme.Bold.Render(versionRef))
197+ fmt.Printf("Image: %s\n", styles.Theme.Bold.Render(img.VersionRef))
159198 if version != "latest" {
160- fmt.Printf("Also: %s\n", styles.Theme.Bold.Render(latestRef))
199+ fmt.Printf("Also: %s\n", styles.Theme.Bold.Render(img.LatestRef))
161200 }
162201 fmt.Printf("Domain: %s\n", styles.Theme.Bold.Render(pushDomain))
163202
164203 // Signal the server to suppress event-based deploys for this image.
165204 // The CLI will trigger an explicit deploy after push completes.
166- if !noDeploy {
205+ if !req.NoDeploy {
167206 if err := handle.plane.DeployIntent(ctx, imageName); err != nil {
168207 // Non-fatal: worst case we get a redundant deploy via event
169208 fmt.Fprintf(os.Stderr, "warning: failed to register deploy intent: %v\n", err)
170209 }
171210 }
172211
173- if build {
174- if err := buildAndPush(ctx, imageOps, version, platform, dockerfile, buildArgs, versionRef, latestRef); err != nil {
212+ build := buildConfig{
213+ Enabled: req.Build.Enabled,
214+ Platform: req.Build.Platform,
215+ Dockerfile: dockerfile,
216+ BuildArgs: req.Build.BuildArgs,
217+ }
218+
219+ if build.Enabled {
220+ if err := buildAndPush(ctx, imageOps, build, img); err != nil {
175221 return err
176222 }
177223 } else {
178- if err := tagAndPush(ctx, imageOps, registry, imageName, version, versionRef, latestRef); err != nil {
224+ if err := tagAndPush(ctx, imageOps, img); err != nil {
179225 return err
180226 }
181227 }
182228
183229 fmt.Println(styles.RenderSuccess("Push complete"))
184230
185- if !noDeploy {
186- return deployAfterPush(ctx, handle.plane, pushDomain, noConfirm)
231+ if !req.NoDeploy {
232+ return deployAfterPush(ctx, handle.plane, pushDomain, req.NoConfirm)
187233 }
188234
189235 return nil
@@ -474,26 +520,26 @@ func parseTagRef(ref string) string {
474520 return tag
475521 }
476522
477-func buildAndPush(ctx context.Context, ops pushImageOps, version, platform, dockerfile string, buildArgs []string, versionRef, latestRef string) error {
478- if _, err := os.Stat(dockerfile); os.IsNotExist(err) {
479- return fmt.Errorf("dockerfile not found: %s", dockerfile)
523+func buildAndPush(ctx context.Context, ops pushImageOps, build buildConfig, img imagePush) error {
524+ if _, err := os.Stat(build.Dockerfile); os.IsNotExist(err) {
525+ return fmt.Errorf("dockerfile not found: %s", build.Dockerfile)
480526 }
481527
482528 // Build and load into local daemon (NOT --push).
483529 // The native registry push client handles chunked uploads to stay
484530 // within Cloudflare's 100MB per-request limit.
485531 fmt.Println("\nBuilding image...")
486- if err := ops.Build(ctx, buildImageArgs(ctx, version, platform, dockerfile, buildArgs, versionRef, latestRef)); err != nil {
532+ if err := ops.Build(ctx, buildImageArgs(ctx, img.Version, build.Platform, build.Dockerfile, build.BuildArgs, img.VersionRef, img.LatestRef)); err != nil {
487533 return err
488534 }
489535
490536 fmt.Println("Pushing...")
491- if err := ops.Push(ctx, latestRef); err != nil {
492- return fmt.Errorf("failed to push %s: %w", latestRef, err)
537+ if err := ops.Push(ctx, img.LatestRef); err != nil {
538+ return fmt.Errorf("failed to push %s: %w", img.LatestRef, err)
493539 }
494- if version != "latest" {
495- if err := ops.Push(ctx, versionRef); err != nil {
496- return fmt.Errorf("failed to push %s: %w", versionRef, err)
540+ if img.Version != "latest" {
541+ if err := ops.Push(ctx, img.VersionRef); err != nil {
542+ return fmt.Errorf("failed to push %s: %w", img.VersionRef, err)
497543 }
498544 }
499545
@@ -550,8 +596,8 @@ func buildImageArgs(ctx context.Context, version, platform, dockerfile string, b
550596 return args
551597 }
552598
553-func tagAndPush(ctx context.Context, ops pushImageOps, registry, imageName, version, versionRef, latestRef string) error {
554- localImage := fmt.Sprintf("%s/%s", registry, imageName)
599+func tagAndPush(ctx context.Context, ops pushImageOps, img imagePush) error {
600+ localImage := fmt.Sprintf("%s/%s", img.Registry, img.ImageName)
555601
556602 fmt.Println("\nChecking local image...")
557603 exists, err := ops.Exists(ctx, localImage)
@@ -563,22 +609,22 @@ func tagAndPush(ctx context.Context, ops pushImageOps, registry, imageName, vers
563609 }
564610
565611 fmt.Println("Tagging...")
566- if err := ops.Tag(ctx, localImage, versionRef); err != nil {
567- return fmt.Errorf("failed to tag %s: %w", versionRef, err)
612+ if err := ops.Tag(ctx, localImage, img.VersionRef); err != nil {
613+ return fmt.Errorf("failed to tag %s: %w", img.VersionRef, err)
568614 }
569- if version != "latest" {
570- if err := ops.Tag(ctx, localImage, latestRef); err != nil {
571- return fmt.Errorf("failed to tag %s: %w", latestRef, err)
615+ if img.Version != "latest" {
616+ if err := ops.Tag(ctx, localImage, img.LatestRef); err != nil {
617+ return fmt.Errorf("failed to tag %s: %w", img.LatestRef, err)
572618 }
573619 }
574620
575621 fmt.Println("Pushing...")
576- if err := ops.Push(ctx, versionRef); err != nil {
577- return fmt.Errorf("failed to push %s: %w", versionRef, err)
622+ if err := ops.Push(ctx, img.VersionRef); err != nil {
623+ return fmt.Errorf("failed to push %s: %w", img.VersionRef, err)
578624 }
579- if version != "latest" {
580- if err := ops.Push(ctx, latestRef); err != nil {
581- return fmt.Errorf("failed to push %s: %w", latestRef, err)
625+ if img.Version != "latest" {
626+ if err := ops.Push(ctx, img.LatestRef); err != nil {
627+ return fmt.Errorf("failed to push %s: %w", img.LatestRef, err)
582628 }
583629 }
584630 return nil
@@ -23,6 +23,33 @@ import (
23 23
24 var newImageOpsFn = newImageOpsFromFlags24 var newImageOpsFn = newImageOpsFromFlags
25 25
26+// buildConfig holds image build settings for push commands.
27+type buildConfig struct {
28+ Enabled bool
29+ Platform string
30+ Dockerfile string
31+ BuildArgs []string
32+}
33+
34+// imagePush holds resolved image identity for push operations.
35+type imagePush struct {
36+ Registry string
37+ ImageName string
38+ Version string
39+ VersionRef string
40+ LatestRef string
41+}
42+
43+// pushRequest holds all inputs for the push command.
44+type pushRequest struct {
45+ ImageArg string
46+ Domain string
47+ Tag string
48+ Build buildConfig
49+ NoDeploy bool
50+ NoConfirm bool
51+}
52+
26 func newPushCmd() *cobra.Command {53 func newPushCmd() *cobra.Command {
27 var (54 var (
28 noDeploy bool55 noDeploy bool
@@ -60,7 +87,14 @@ Examples:
60 if len(args) > 0 {87 if len(args) > 0 {
61 imageArg = args[0]88 imageArg = args[0]
62 }89 }
63- return runPush(cmd.Context(), imageArg, domainFlag, tag, build, platform, dockerfile, buildArgs, noDeploy, noConfirm)90+ return runPush(cmd.Context(), pushRequest{
91+ ImageArg: imageArg,
92+ Domain: domainFlag,
93+ Tag: tag,
94+ Build: buildConfig{Enabled: build, Platform: platform, Dockerfile: dockerfile, BuildArgs: buildArgs},
95+ NoDeploy: noDeploy,
96+ NoConfirm: noConfirm,
97+ })
64 },98 },
65 }99 }
66 100
@@ -120,70 +154,82 @@ func resolveVersion(ctx context.Context, tag string) (string, error) {
120 return version, nil154 return version, nil
121 }155 }
122 156
123-func runPush(ctx context.Context, imageArg, domainFlag, tag string, build bool, platform string, dockerfile string, buildArgs []string, noDeploy bool, noConfirm bool) error {157+func runPush(ctx context.Context, req pushRequest) error {
124 handle, err := resolveControlPlane(configPath)158 handle, err := resolveControlPlane(configPath)
125 if err != nil {159 if err != nil {
126 return err160 return err
127 }161 }
128 defer handle.close()162 defer handle.close()
129 163
130- dockerfile, err = resolveDockerfile(dockerfile, build)164+ dockerfile, err := resolveDockerfile(req.Build.Dockerfile, req.Build.Enabled)
131 if err != nil {165 if err != nil {
132 return err166 return err
133 }167 }
134 168
135- registry, imageName, pushDomain, err := resolveRoute(ctx, handle.plane, imageArg, domainFlag, dockerfile)169+ registry, imageName, pushDomain, err := resolveRoute(ctx, handle.plane, req.ImageArg, req.Domain, dockerfile)
136 if err != nil {170 if err != nil {
137 return err171 return err
138 }172 }
139 173
140- version, err := resolveVersion(ctx, tag)174+ version, err := resolveVersion(ctx, req.Tag)
141 if err != nil {175 if err != nil {
142 return err176 return err
143 }177 }
144 178
145- for _, ba := range buildArgs {179+ for _, ba := range req.Build.BuildArgs {
146 if err := validateBuildArg(ba); err != nil {180 if err := validateBuildArg(ba); err != nil {
147 return err181 return err
148 }182 }
149 }183 }
150 184
151- versionRef, latestRef := resolveImageRefs(registry, imageName, version)185+ img := imagePush{
186+ Registry: registry,
187+ ImageName: imageName,
188+ Version: version,
189+ }
190+ img.VersionRef, img.LatestRef = resolveImageRefs(registry, imageName, version)
152 191
153 imageOps, err := newImageOpsFn()192 imageOps, err := newImageOpsFn()
154 if err != nil {193 if err != nil {
155 return err194 return err
156 }195 }
157 196
158- fmt.Printf("Image: %s\n", styles.Theme.Bold.Render(versionRef))197+ fmt.Printf("Image: %s\n", styles.Theme.Bold.Render(img.VersionRef))
159 if version != "latest" {198 if version != "latest" {
160- fmt.Printf("Also: %s\n", styles.Theme.Bold.Render(latestRef))199+ fmt.Printf("Also: %s\n", styles.Theme.Bold.Render(img.LatestRef))
161 }200 }
162 fmt.Printf("Domain: %s\n", styles.Theme.Bold.Render(pushDomain))201 fmt.Printf("Domain: %s\n", styles.Theme.Bold.Render(pushDomain))
163 202
164 // Signal the server to suppress event-based deploys for this image.203 // Signal the server to suppress event-based deploys for this image.
165 // The CLI will trigger an explicit deploy after push completes.204 // The CLI will trigger an explicit deploy after push completes.
166- if !noDeploy {205+ if !req.NoDeploy {
167 if err := handle.plane.DeployIntent(ctx, imageName); err != nil {206 if err := handle.plane.DeployIntent(ctx, imageName); err != nil {
168 // Non-fatal: worst case we get a redundant deploy via event207 // Non-fatal: worst case we get a redundant deploy via event
169 fmt.Fprintf(os.Stderr, "warning: failed to register deploy intent: %v\n", err)208 fmt.Fprintf(os.Stderr, "warning: failed to register deploy intent: %v\n", err)
170 }209 }
171 }210 }
172 211
173- if build {212+ build := buildConfig{
174- if err := buildAndPush(ctx, imageOps, version, platform, dockerfile, buildArgs, versionRef, latestRef); err != nil {213+ Enabled: req.Build.Enabled,
214+ Platform: req.Build.Platform,
215+ Dockerfile: dockerfile,
216+ BuildArgs: req.Build.BuildArgs,
217+ }
218+
219+ if build.Enabled {
220+ if err := buildAndPush(ctx, imageOps, build, img); err != nil {
175 return err221 return err
176 }222 }
177 } else {223 } else {
178- if err := tagAndPush(ctx, imageOps, registry, imageName, version, versionRef, latestRef); err != nil {224+ if err := tagAndPush(ctx, imageOps, img); err != nil {
179 return err225 return err
180 }226 }
181 }227 }
182 228
183 fmt.Println(styles.RenderSuccess("Push complete"))229 fmt.Println(styles.RenderSuccess("Push complete"))
184 230
185- if !noDeploy {231+ if !req.NoDeploy {
186- return deployAfterPush(ctx, handle.plane, pushDomain, noConfirm)232+ return deployAfterPush(ctx, handle.plane, pushDomain, req.NoConfirm)
187 }233 }
188 234
189 return nil235 return nil
@@ -474,26 +520,26 @@ func parseTagRef(ref string) string {
474 return tag520 return tag
475 }521 }
476 522
477-func buildAndPush(ctx context.Context, ops pushImageOps, version, platform, dockerfile string, buildArgs []string, versionRef, latestRef string) error {523+func buildAndPush(ctx context.Context, ops pushImageOps, build buildConfig, img imagePush) error {
478- if _, err := os.Stat(dockerfile); os.IsNotExist(err) {524+ if _, err := os.Stat(build.Dockerfile); os.IsNotExist(err) {
479- return fmt.Errorf("dockerfile not found: %s", dockerfile)525+ return fmt.Errorf("dockerfile not found: %s", build.Dockerfile)
480 }526 }
481 527
482 // Build and load into local daemon (NOT --push).528 // Build and load into local daemon (NOT --push).
483 // The native registry push client handles chunked uploads to stay529 // The native registry push client handles chunked uploads to stay
484 // within Cloudflare's 100MB per-request limit.530 // within Cloudflare's 100MB per-request limit.
485 fmt.Println("\nBuilding image...")531 fmt.Println("\nBuilding image...")
486- if err := ops.Build(ctx, buildImageArgs(ctx, version, platform, dockerfile, buildArgs, versionRef, latestRef)); err != nil {532+ if err := ops.Build(ctx, buildImageArgs(ctx, img.Version, build.Platform, build.Dockerfile, build.BuildArgs, img.VersionRef, img.LatestRef)); err != nil {
487 return err533 return err
488 }534 }
489 535
490 fmt.Println("Pushing...")536 fmt.Println("Pushing...")
491- if err := ops.Push(ctx, latestRef); err != nil {537+ if err := ops.Push(ctx, img.LatestRef); err != nil {
492- return fmt.Errorf("failed to push %s: %w", latestRef, err)538+ return fmt.Errorf("failed to push %s: %w", img.LatestRef, err)
493 }539 }
494- if version != "latest" {540+ if img.Version != "latest" {
495- if err := ops.Push(ctx, versionRef); err != nil {541+ if err := ops.Push(ctx, img.VersionRef); err != nil {
496- return fmt.Errorf("failed to push %s: %w", versionRef, err)542+ return fmt.Errorf("failed to push %s: %w", img.VersionRef, err)
497 }543 }
498 }544 }
499 545
@@ -550,8 +596,8 @@ func buildImageArgs(ctx context.Context, version, platform, dockerfile string, b
550 return args596 return args
551 }597 }
552 598
553-func tagAndPush(ctx context.Context, ops pushImageOps, registry, imageName, version, versionRef, latestRef string) error {599+func tagAndPush(ctx context.Context, ops pushImageOps, img imagePush) error {
554- localImage := fmt.Sprintf("%s/%s", registry, imageName)600+ localImage := fmt.Sprintf("%s/%s", img.Registry, img.ImageName)
555 601
556 fmt.Println("\nChecking local image...")602 fmt.Println("\nChecking local image...")
557 exists, err := ops.Exists(ctx, localImage)603 exists, err := ops.Exists(ctx, localImage)
@@ -563,22 +609,22 @@ func tagAndPush(ctx context.Context, ops pushImageOps, registry, imageName, vers
563 }609 }
564 610
565 fmt.Println("Tagging...")611 fmt.Println("Tagging...")
566- if err := ops.Tag(ctx, localImage, versionRef); err != nil {612+ if err := ops.Tag(ctx, localImage, img.VersionRef); err != nil {
567- return fmt.Errorf("failed to tag %s: %w", versionRef, err)613+ return fmt.Errorf("failed to tag %s: %w", img.VersionRef, err)
568 }614 }
569- if version != "latest" {615+ if img.Version != "latest" {
570- if err := ops.Tag(ctx, localImage, latestRef); err != nil {616+ if err := ops.Tag(ctx, localImage, img.LatestRef); err != nil {
571- return fmt.Errorf("failed to tag %s: %w", latestRef, err)617+ return fmt.Errorf("failed to tag %s: %w", img.LatestRef, err)
572 }618 }
573 }619 }
574 620
575 fmt.Println("Pushing...")621 fmt.Println("Pushing...")
576- if err := ops.Push(ctx, versionRef); err != nil {622+ if err := ops.Push(ctx, img.VersionRef); err != nil {
577- return fmt.Errorf("failed to push %s: %w", versionRef, err)623+ return fmt.Errorf("failed to push %s: %w", img.VersionRef, err)
578 }624 }
579- if version != "latest" {625+ if img.Version != "latest" {
580- if err := ops.Push(ctx, latestRef); err != nil {626+ if err := ops.Push(ctx, img.LatestRef); err != nil {
581- return fmt.Errorf("failed to push %s: %w", latestRef, err)627+ return fmt.Errorf("failed to push %s: %w", img.LatestRef, err)
582 }628 }
583 }629 }
584 return nil630 return nil
M internal/adapters/in/cli/push_integration_test.go +7 -1
@@ -51,7 +51,13 @@ func TestIntegration_TagAndPush_NativeRegistry(t *testing.T) {
5151 versionRef := registry + "/testapp:v1.0.0"
5252 latestRef := registry + "/testapp:latest"
5353
54- err = tagAndPush(ctx, ops, registry, "testapp", "v1.0.0", versionRef, latestRef)
54+ err = tagAndPush(ctx, ops, imagePush{
55+ Registry: registry,
56+ ImageName: "testapp",
57+ Version: "v1.0.0",
58+ VersionRef: versionRef,
59+ LatestRef: latestRef,
60+ })
5561 require.NoError(t, err)
5662
5763 harness.RequireManifest(t, "v1.0.0")
@@ -51,7 +51,13 @@ func TestIntegration_TagAndPush_NativeRegistry(t *testing.T) {
51 versionRef := registry + "/testapp:v1.0.0"51 versionRef := registry + "/testapp:v1.0.0"
52 latestRef := registry + "/testapp:latest"52 latestRef := registry + "/testapp:latest"
53 53
54- err = tagAndPush(ctx, ops, registry, "testapp", "v1.0.0", versionRef, latestRef)54+ err = tagAndPush(ctx, ops, imagePush{
55+ Registry: registry,
56+ ImageName: "testapp",
57+ Version: "v1.0.0",
58+ VersionRef: versionRef,
59+ LatestRef: latestRef,
60+ })
55 require.NoError(t, err)61 require.NoError(t, err)
56 62
57 harness.RequireManifest(t, "v1.0.0")63 harness.RequireManifest(t, "v1.0.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 7

Tags 106

↑↓ moveenter openesc close