detached af6e90a

fix(cli): improve backward compatibility and add validation guards

commit
af6e90afb423c872c50765381b7cb46cc70c2c27
author
bnema · · 2026-01-28 21:05 UTC
parent
39076ab
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +17 -3
M internal/adapters/in/cli/routes.go +16 -2
@@ -257,12 +257,26 @@ func newRoutesAddCmd() *cobra.Command {
257257 Examples:
258258 gordon routes add app.mydomain.com myapp:latest
259259 gordon --remote https://gordon.mydomain.com routes add api.mydomain.com api:v2`,
260- Args: cobra.ExactArgs(2),
260+ Args: cobra.RangeArgs(1, 2),
261261 RunE: func(cmd *cobra.Command, args []string) error {
262262 ctx := context.Background()
263263 routeDomain := args[0]
264264
265- if image == "" {
265+ // Resolve image from positional argument or flag
266+ hasPositionalImage := len(args) > 1
267+ hasFlagImage := image != ""
268+
269+ // Check for conflicts: both positional and flag provided
270+ if hasPositionalImage && hasFlagImage {
271+ return fmt.Errorf("error: cannot use both positional image argument and --image flag")
272+ }
273+
274+ // Check that image is provided via either method
275+ if !hasPositionalImage && !hasFlagImage {
276+ return fmt.Errorf("error: image is required (use positional argument or --image flag)")
277+ }
278+
279+ if hasPositionalImage {
266280 image = args[1]
267281 }
268282
@@ -257,12 +257,26 @@ func newRoutesAddCmd() *cobra.Command {
257 Examples:257 Examples:
258 gordon routes add app.mydomain.com myapp:latest258 gordon routes add app.mydomain.com myapp:latest
259 gordon --remote https://gordon.mydomain.com routes add api.mydomain.com api:v2`,259 gordon --remote https://gordon.mydomain.com routes add api.mydomain.com api:v2`,
260- Args: cobra.ExactArgs(2),260+ Args: cobra.RangeArgs(1, 2),
261 RunE: func(cmd *cobra.Command, args []string) error {261 RunE: func(cmd *cobra.Command, args []string) error {
262 ctx := context.Background()262 ctx := context.Background()
263 routeDomain := args[0]263 routeDomain := args[0]
264 264
265- if image == "" {265+ // Resolve image from positional argument or flag
266+ hasPositionalImage := len(args) > 1
267+ hasFlagImage := image != ""
268+
269+ // Check for conflicts: both positional and flag provided
270+ if hasPositionalImage && hasFlagImage {
271+ return fmt.Errorf("error: cannot use both positional image argument and --image flag")
272+ }
273+
274+ // Check that image is provided via either method
275+ if !hasPositionalImage && !hasFlagImage {
276+ return fmt.Errorf("error: image is required (use positional argument or --image flag)")
277+ }
278+
279+ if hasPositionalImage {
266 image = args[1]280 image = args[1]
267 }281 }
268 282
M internal/adapters/in/http/admin/handler.go +1 -1
@@ -279,7 +279,7 @@ func (h *Handler) handleRoutesPost(w http.ResponseWriter, r *http.Request) {
279279 }
280280
281281 // Validate image exists in registry before adding route
282- if h.registrySvc != nil {
282+ if h.registrySvc != nil && route.Image != "" {
283283 imageName, imageRef := validation.ParseImageReference(route.Image)
284284 if _, err := h.registrySvc.GetManifest(ctx, imageName, imageRef); err != nil {
285285 if errors.Is(err, domain.ErrManifestNotFound) {
@@ -279,7 +279,7 @@ func (h *Handler) handleRoutesPost(w http.ResponseWriter, r *http.Request) {
279 }279 }
280 280
281 // Validate image exists in registry before adding route281 // Validate image exists in registry before adding route
282- if h.registrySvc != nil {282+ if h.registrySvc != nil && route.Image != "" {
283 imageName, imageRef := validation.ParseImageReference(route.Image)283 imageName, imageRef := validation.ParseImageReference(route.Image)
284 if _, err := h.registrySvc.GetManifest(ctx, imageName, imageRef); err != nil {284 if _, err := h.registrySvc.GetManifest(ctx, imageName, imageRef); err != nil {
285 if errors.Is(err, domain.ErrManifestNotFound) {285 if errors.Is(err, domain.ErrManifestNotFound) {

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