From 42f0e05511f652f8b4797b0333085ac3c7146964 Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Fri, 30 Oct 2026 23:29:58 -0400 Subject: [PATCH] fix(cmd): reject unsupported --output values Commands with a json-only --output flag silently fell back to the human-readable output for any other value (e.g. yaml) and exited 0. Validate the value when the flag is parsed and name the supported one. Co-Authored-By: Claude Sonnet 5.5 Signed-off-by: Alex Castillo --- pkg/cmd/attempt_get.go | 2 +- pkg/cmd/attempt_list.go | 2 +- pkg/cmd/connection_create.go | 2 +- pkg/cmd/connection_get.go | 2 +- pkg/cmd/connection_list.go | 2 +- pkg/cmd/connection_update.go | 2 +- pkg/cmd/connection_upsert.go | 2 +- pkg/cmd/destination_create.go | 2 +- pkg/cmd/destination_get.go | 2 +- pkg/cmd/destination_list.go | 2 +- pkg/cmd/destination_update.go | 2 +- pkg/cmd/destination_upsert.go | 2 +- pkg/cmd/event_get.go | 2 +- pkg/cmd/event_list.go | 2 +- pkg/cmd/issue_dismiss.go | 2 +- pkg/cmd/issue_get.go | 2 +- pkg/cmd/issue_list.go | 2 +- pkg/cmd/issue_update.go | 2 +- pkg/cmd/metrics.go | 2 +- pkg/cmd/outpost_attempt.go | 4 +-- pkg/cmd/outpost_config.go | 4 +-- pkg/cmd/outpost_custom_domain.go | 4 +-- pkg/cmd/outpost_destination_create.go | 2 +- pkg/cmd/outpost_destination_disable.go | 2 +- pkg/cmd/outpost_destination_enable.go | 2 +- pkg/cmd/outpost_destination_get.go | 2 +- pkg/cmd/outpost_destination_list.go | 2 +- pkg/cmd/outpost_destination_type.go | 4 +-- pkg/cmd/outpost_destination_update.go | 2 +- pkg/cmd/outpost_event_get.go | 2 +- pkg/cmd/outpost_event_list.go | 2 +- pkg/cmd/outpost_event_retry.go | 2 +- pkg/cmd/outpost_metrics.go | 2 +- pkg/cmd/outpost_publish.go | 2 +- pkg/cmd/outpost_status.go | 2 +- pkg/cmd/outpost_tenant_get.go | 2 +- pkg/cmd/outpost_tenant_list.go | 2 +- pkg/cmd/outpost_tenant_portal.go | 2 +- pkg/cmd/outpost_tenant_token.go | 2 +- pkg/cmd/outpost_tenant_upsert.go | 2 +- pkg/cmd/outpost_topic.go | 2 +- pkg/cmd/output_flag.go | 37 ++++++++++++++++++++ pkg/cmd/output_flag_test.go | 48 ++++++++++++++++++++++++++ pkg/cmd/project_list.go | 2 +- pkg/cmd/request_events.go | 2 +- pkg/cmd/request_get.go | 2 +- pkg/cmd/request_ignored_events.go | 2 +- pkg/cmd/request_list.go | 2 +- pkg/cmd/source_create.go | 2 +- pkg/cmd/source_get.go | 2 +- pkg/cmd/source_list.go | 2 +- pkg/cmd/source_update.go | 2 +- pkg/cmd/source_upsert.go | 2 +- pkg/cmd/transformation_count.go | 2 +- pkg/cmd/transformation_create.go | 2 +- pkg/cmd/transformation_executions.go | 4 +-- pkg/cmd/transformation_get.go | 2 +- pkg/cmd/transformation_list.go | 2 +- pkg/cmd/transformation_run.go | 2 +- pkg/cmd/transformation_update.go | 2 +- pkg/cmd/transformation_upsert.go | 2 +- 61 files changed, 149 insertions(+), 64 deletions(-) create mode 100644 pkg/cmd/output_flag.go create mode 100644 pkg/cmd/output_flag_test.go diff --git a/pkg/cmd/attempt_get.go b/pkg/cmd/attempt_get.go index e87b557c..add800da 100644 --- a/pkg/cmd/attempt_get.go +++ b/pkg/cmd/attempt_get.go @@ -31,7 +31,7 @@ Examples: RunE: ac.runAttemptGetCmd, } - ac.cmd.Flags().StringVar(&ac.output, "output", "", "Output format (json)") + ac.cmd.Flags().Var(newOutputFormatValue(&ac.output), "output", "Output format (json)") return ac } diff --git a/pkg/cmd/attempt_list.go b/pkg/cmd/attempt_list.go index b078b23d..bfd224aa 100644 --- a/pkg/cmd/attempt_list.go +++ b/pkg/cmd/attempt_list.go @@ -43,7 +43,7 @@ Examples: ac.cmd.Flags().IntVar(&ac.limit, "limit", 100, "Limit number of results") ac.cmd.Flags().StringVar(&ac.next, "next", "", "Pagination cursor for next page") ac.cmd.Flags().StringVar(&ac.prev, "prev", "", "Pagination cursor for previous page") - ac.cmd.Flags().StringVar(&ac.output, "output", "", "Output format (json)") + ac.cmd.Flags().Var(newOutputFormatValue(&ac.output), "output", "Output format (json)") return ac } diff --git a/pkg/cmd/connection_create.go b/pkg/cmd/connection_create.go index a20bb7f0..df4668a1 100644 --- a/pkg/cmd/connection_create.go +++ b/pkg/cmd/connection_create.go @@ -237,7 +237,7 @@ func newConnectionCreateCmd() *connectionCreateCmd { cc.cmd.Flags().StringVar(&cc.destinationID, "destination-id", "", "Use existing destination by ID") // Output flags - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") cc.cmd.MarkFlagRequired("name") diff --git a/pkg/cmd/connection_get.go b/pkg/cmd/connection_get.go index 2b580fdc..16b7885e 100644 --- a/pkg/cmd/connection_get.go +++ b/pkg/cmd/connection_get.go @@ -43,7 +43,7 @@ Examples: "cli.arguments": `[{"name":"connection-id-or-name","type":"string","description":"Connection ID or name","required":true}]`, } - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") addIncludeSourceAuthFlagForConnection(cc.cmd, &cc.includeSourceAuth) addIncludeDestinationAuthFlag(cc.cmd, &cc.includeDestinationAuth) diff --git a/pkg/cmd/connection_list.go b/pkg/cmd/connection_list.go index 5cfb2ff1..144c556e 100644 --- a/pkg/cmd/connection_list.go +++ b/pkg/cmd/connection_list.go @@ -58,7 +58,7 @@ Examples: cc.cmd.Flags().StringVar(&cc.destinationID, "destination-id", "", "Filter by destination ID") cc.cmd.Flags().BoolVar(&cc.disabled, "disabled", false, "Include disabled connections") cc.cmd.Flags().IntVar(&cc.limit, "limit", 100, "Limit number of results") - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") return cc } diff --git a/pkg/cmd/connection_update.go b/pkg/cmd/connection_update.go index 8728d460..cd07bae6 100644 --- a/pkg/cmd/connection_update.go +++ b/pkg/cmd/connection_update.go @@ -73,7 +73,7 @@ Examples: addConnectionRuleFlags(cu.cmd, &cu.connectionRuleFlags) // Output - cu.cmd.Flags().StringVar(&cu.output, "output", "", "Output format (json)") + cu.cmd.Flags().Var(newOutputFormatValue(&cu.output), "output", "Output format (json)") return cu } diff --git a/pkg/cmd/connection_upsert.go b/pkg/cmd/connection_upsert.go index 84e3c388..36c1c90e 100644 --- a/pkg/cmd/connection_upsert.go +++ b/pkg/cmd/connection_upsert.go @@ -167,7 +167,7 @@ func newConnectionUpsertCmd() *connectionUpsertCmd { cu.cmd.Flags().StringVar(&cu.destinationID, "destination-id", "", "Use existing destination by ID") // Output flags - cu.cmd.Flags().StringVar(&cu.output, "output", "", "Output format (json)") + cu.cmd.Flags().Var(newOutputFormatValue(&cu.output), "output", "Output format (json)") // Upsert-specific flags cu.cmd.Flags().BoolVar(&cu.dryRun, "dry-run", false, "Preview changes without applying them") diff --git a/pkg/cmd/destination_create.go b/pkg/cmd/destination_create.go index d0028a3f..79a87a6b 100644 --- a/pkg/cmd/destination_create.go +++ b/pkg/cmd/destination_create.go @@ -64,7 +64,7 @@ Examples: dc.cmd.Flags().StringVar(&dc.CustomSignatureKey, "custom-signature-key", "", "Key/header name for custom signature") addDestinationDeliveryPolicyFlags(dc.cmd, &dc.destinationConfigFlags) dc.cmd.Flags().StringVar(&dc.HTTPMethod, "http-method", "", "HTTP method for HTTP destinations (GET, POST, PUT, PATCH, DELETE)") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") dc.cmd.MarkFlagRequired("name") dc.cmd.MarkFlagRequired("type") diff --git a/pkg/cmd/destination_get.go b/pkg/cmd/destination_get.go index f47cd819..b59dab69 100644 --- a/pkg/cmd/destination_get.go +++ b/pkg/cmd/destination_get.go @@ -36,7 +36,7 @@ Examples: RunE: dc.runDestinationGetCmd, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") addIncludeAuthFlagForDestination(dc.cmd, &dc.includeDestAuth) return dc diff --git a/pkg/cmd/destination_list.go b/pkg/cmd/destination_list.go index f6449fc7..3dd1e008 100644 --- a/pkg/cmd/destination_list.go +++ b/pkg/cmd/destination_list.go @@ -44,7 +44,7 @@ Examples: dc.cmd.Flags().StringVar(&dc.destType, "type", "", "Filter by destination type (HTTP, CLI, MOCK_API)") dc.cmd.Flags().BoolVar(&dc.disabled, "disabled", false, "Include disabled destinations") dc.cmd.Flags().IntVar(&dc.limit, "limit", 100, "Limit number of results") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/destination_update.go b/pkg/cmd/destination_update.go index 4312c54b..608bd355 100644 --- a/pkg/cmd/destination_update.go +++ b/pkg/cmd/destination_update.go @@ -62,7 +62,7 @@ Examples: dc.cmd.Flags().StringVar(&dc.CustomSignatureKey, "custom-signature-key", "", "Key/header name for custom signature") addDestinationDeliveryPolicyFlags(dc.cmd, &dc.destinationConfigFlags) dc.cmd.Flags().StringVar(&dc.HTTPMethod, "http-method", "", "HTTP method for HTTP destinations") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/destination_upsert.go b/pkg/cmd/destination_upsert.go index cb6c9dbf..80729717 100644 --- a/pkg/cmd/destination_upsert.go +++ b/pkg/cmd/destination_upsert.go @@ -63,7 +63,7 @@ Examples: addDestinationDeliveryPolicyFlags(dc.cmd, &dc.destinationConfigFlags) dc.cmd.Flags().StringVar(&dc.HTTPMethod, "http-method", "", "HTTP method for HTTP destinations") dc.cmd.Flags().BoolVar(&dc.dryRun, "dry-run", false, "Preview changes without applying") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/event_get.go b/pkg/cmd/event_get.go index dc1f927c..d41bd00c 100644 --- a/pkg/cmd/event_get.go +++ b/pkg/cmd/event_get.go @@ -31,7 +31,7 @@ Examples: RunE: ec.runEventGetCmd, } - ec.cmd.Flags().StringVar(&ec.output, "output", "", "Output format (json)") + ec.cmd.Flags().Var(newOutputFormatValue(&ec.output), "output", "Output format (json)") return ec } diff --git a/pkg/cmd/event_list.go b/pkg/cmd/event_list.go index aaadd5e8..b3aac978 100644 --- a/pkg/cmd/event_list.go +++ b/pkg/cmd/event_list.go @@ -97,7 +97,7 @@ Examples: ec.cmd.Flags().IntVar(&ec.limit, "limit", 100, "Limit number of results") ec.cmd.Flags().StringVar(&ec.next, "next", "", "Pagination cursor for next page") ec.cmd.Flags().StringVar(&ec.prev, "prev", "", "Pagination cursor for previous page") - ec.cmd.Flags().StringVar(&ec.output, "output", "", "Output format (json)") + ec.cmd.Flags().Var(newOutputFormatValue(&ec.output), "output", "Output format (json)") return ec } diff --git a/pkg/cmd/issue_dismiss.go b/pkg/cmd/issue_dismiss.go index 26785395..8ea4b9df 100644 --- a/pkg/cmd/issue_dismiss.go +++ b/pkg/cmd/issue_dismiss.go @@ -34,7 +34,7 @@ Examples: } ic.cmd.Flags().BoolVar(&ic.force, "force", false, "Dismiss without confirmation") - ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)") + ic.cmd.Flags().Var(newOutputFormatValue(&ic.output), "output", "Output format (json)") return ic } diff --git a/pkg/cmd/issue_get.go b/pkg/cmd/issue_get.go index 6f76db8f..2d5ad70a 100644 --- a/pkg/cmd/issue_get.go +++ b/pkg/cmd/issue_get.go @@ -32,7 +32,7 @@ Examples: RunE: ic.runIssueGetCmd, } - ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)") + ic.cmd.Flags().Var(newOutputFormatValue(&ic.output), "output", "Output format (json)") return ic } diff --git a/pkg/cmd/issue_list.go b/pkg/cmd/issue_list.go index b059268a..97560c86 100644 --- a/pkg/cmd/issue_list.go +++ b/pkg/cmd/issue_list.go @@ -53,7 +53,7 @@ Examples: ic.cmd.Flags().IntVar(&ic.limit, "limit", 100, "Limit number of results (max 250)") ic.cmd.Flags().StringVar(&ic.next, "next", "", "Pagination cursor for next page") ic.cmd.Flags().StringVar(&ic.prev, "prev", "", "Pagination cursor for previous page") - ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)") + ic.cmd.Flags().Var(newOutputFormatValue(&ic.output), "output", "Output format (json)") return ic } diff --git a/pkg/cmd/issue_update.go b/pkg/cmd/issue_update.go index 6d1231ea..c4eaf1ae 100644 --- a/pkg/cmd/issue_update.go +++ b/pkg/cmd/issue_update.go @@ -39,7 +39,7 @@ Examples: ic.cmd.Flags().StringVar(&ic.status, "status", "", "New issue status (OPENED, IGNORED, ACKNOWLEDGED, RESOLVED) [required]") ic.cmd.MarkFlagRequired("status") - ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)") + ic.cmd.Flags().Var(newOutputFormatValue(&ic.output), "output", "Output format (json)") return ic } diff --git a/pkg/cmd/metrics.go b/pkg/cmd/metrics.go index c02d463b..880f8990 100644 --- a/pkg/cmd/metrics.go +++ b/pkg/cmd/metrics.go @@ -100,7 +100,7 @@ func addMetricsCommonFlags(cmd *cobra.Command, f *metricsCommonFlags, filters ho if filters.IssueID { cmd.Flags().StringVar(&f.issueID, "issue-id", "", "Filter by issue ID (required for per-issue metrics, e.g. when using --dimensions issue_id)") } - cmd.Flags().StringVar(&f.output, "output", "", "Output format (json)") + cmd.Flags().Var(newOutputFormatValue(&f.output), "output", "Output format (json)") _ = cmd.MarkFlagRequired("start") _ = cmd.MarkFlagRequired("end") // Every metrics endpoint rejects a request without measures, so catch it diff --git a/pkg/cmd/outpost_attempt.go b/pkg/cmd/outpost_attempt.go index 7b7fd0d9..d22d3fa2 100644 --- a/pkg/cmd/outpost_attempt.go +++ b/pkg/cmd/outpost_attempt.go @@ -117,7 +117,7 @@ specifically; the filters and results are otherwise the same.`, ac.cmd.Flags().StringVar(&ac.dir, "dir", "", "Sort direction (asc, desc)") ac.cmd.Flags().StringVar(&ac.next, "next", "", "Next page cursor") ac.cmd.Flags().StringVar(&ac.prev, "prev", "", "Previous page cursor") - ac.cmd.Flags().StringVar(&ac.output, "output", "", "Output format (json)") + ac.cmd.Flags().Var(newOutputFormatValue(&ac.output), "output", "Output format (json)") return ac } @@ -215,7 +215,7 @@ func newOutpostAttemptGetCmd() *outpostAttemptGetCmd { ac.cmd.Flags().StringVar(&ac.tenantID, "tenant-id", "", "Tenant the attempt belongs to") ac.cmd.Flags().StringVar(&ac.destinationID, "destination-id", "", "Destination the attempt targeted") ac.cmd.Flags().StringVar(&ac.include, "include", "", "Include related data, comma-separated (event, event.data, response_data, destination)") - ac.cmd.Flags().StringVar(&ac.output, "output", "", "Output format (json)") + ac.cmd.Flags().Var(newOutputFormatValue(&ac.output), "output", "Output format (json)") return ac } diff --git a/pkg/cmd/outpost_config.go b/pkg/cmd/outpost_config.go index 08a7bc0e..f0a14402 100644 --- a/pkg/cmd/outpost_config.go +++ b/pkg/cmd/outpost_config.go @@ -71,7 +71,7 @@ are omitted unless you ask for one by name.`, }, } - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") return cc } @@ -169,7 +169,7 @@ which when that happens.`, cc.cmd.Flags().StringArrayVar(&cc.unset, "unset", nil, "Return a key to its default (repeatable)") cc.cmd.Flags().StringVar(&cc.configFile, "config-file", "", "Path to a JSON file of configuration values") cc.cmd.Flags().BoolVar(&cc.dryRun, "dry-run", false, "Show what would change without applying it") - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") return cc } diff --git a/pkg/cmd/outpost_custom_domain.go b/pkg/cmd/outpost_custom_domain.go index 6d708926..6553419b 100644 --- a/pkg/cmd/outpost_custom_domain.go +++ b/pkg/cmd/outpost_custom_domain.go @@ -53,7 +53,7 @@ func newOutpostCustomDomainGetCmd() *outpostCustomDomainGetCmd { hookdeck outpost config custom-domain get`, } - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") return cc } @@ -113,7 +113,7 @@ they have propagated and been verified.`, }, } - cc.cmd.Flags().StringVar(&cc.output, "output", "", "Output format (json)") + cc.cmd.Flags().Var(newOutputFormatValue(&cc.output), "output", "Output format (json)") return cc } diff --git a/pkg/cmd/outpost_destination_create.go b/pkg/cmd/outpost_destination_create.go index 9e66907f..6ebb1137 100644 --- a/pkg/cmd/outpost_destination_create.go +++ b/pkg/cmd/outpost_destination_create.go @@ -56,7 +56,7 @@ Topics default to all ("*") when --topics is omitted.`, } dc.cmd.Flags().StringVar(&dc.destType, "type", "", "Destination type (required)") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") addOutpostDestinationFieldFlags(dc.cmd, &dc.fields) dc.cmd.MarkFlagRequired("type") diff --git a/pkg/cmd/outpost_destination_disable.go b/pkg/cmd/outpost_destination_disable.go index 4f13a343..18a5e913 100644 --- a/pkg/cmd/outpost_destination_disable.go +++ b/pkg/cmd/outpost_destination_disable.go @@ -37,7 +37,7 @@ The destination and its configuration are kept, so 'enable' resumes delivery.`, }, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/outpost_destination_enable.go b/pkg/cmd/outpost_destination_enable.go index 720326b5..b9c102fb 100644 --- a/pkg/cmd/outpost_destination_enable.go +++ b/pkg/cmd/outpost_destination_enable.go @@ -36,7 +36,7 @@ func newOutpostDestinationEnableCmd(parent *outpostDestinationCmd) *outpostDesti }, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/outpost_destination_get.go b/pkg/cmd/outpost_destination_get.go index b5225c8e..7a937b8c 100644 --- a/pkg/cmd/outpost_destination_get.go +++ b/pkg/cmd/outpost_destination_get.go @@ -35,7 +35,7 @@ func newOutpostDestinationGetCmd(parent *outpostDestinationCmd) *outpostDestinat }, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/outpost_destination_list.go b/pkg/cmd/outpost_destination_list.go index 5295661b..39455329 100644 --- a/pkg/cmd/outpost_destination_list.go +++ b/pkg/cmd/outpost_destination_list.go @@ -45,7 +45,7 @@ This endpoint is not paginated: every destination for the tenant is returned.`, dc.cmd.Flags().StringVar(&dc.destType, "type", "", "Filter by destination type(s), comma-separated") dc.cmd.Flags().StringVar(&dc.topics, "topics", "", "Filter by topic(s), comma-separated") - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/outpost_destination_type.go b/pkg/cmd/outpost_destination_type.go index 4eeaf08a..130448f1 100644 --- a/pkg/cmd/outpost_destination_type.go +++ b/pkg/cmd/outpost_destination_type.go @@ -56,7 +56,7 @@ func newOutpostDestinationTypeListCmd() *outpostDestinationTypeListCmd { hookdeck outpost destination-type list`, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } @@ -117,7 +117,7 @@ or format the schema constrains it to.`, }, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") return dc } diff --git a/pkg/cmd/outpost_destination_update.go b/pkg/cmd/outpost_destination_update.go index d6aefd54..19d2578a 100644 --- a/pkg/cmd/outpost_destination_update.go +++ b/pkg/cmd/outpost_destination_update.go @@ -53,7 +53,7 @@ so metadata cannot be cleared here.`, }, } - dc.cmd.Flags().StringVar(&dc.output, "output", "", "Output format (json)") + dc.cmd.Flags().Var(newOutputFormatValue(&dc.output), "output", "Output format (json)") addOutpostDestinationFieldFlags(dc.cmd, &dc.fields) return dc diff --git a/pkg/cmd/outpost_event_get.go b/pkg/cmd/outpost_event_get.go index 6c370483..ca549c4c 100644 --- a/pkg/cmd/outpost_event_get.go +++ b/pkg/cmd/outpost_event_get.go @@ -43,7 +43,7 @@ func newOutpostEventGetCmd() *outpostEventGetCmd { } ec.cmd.Flags().StringVar(&ec.tenantID, "tenant-id", "", "Tenant the event belongs to") - ec.cmd.Flags().StringVar(&ec.output, "output", "", "Output format (json)") + ec.cmd.Flags().Var(newOutputFormatValue(&ec.output), "output", "Output format (json)") return ec } diff --git a/pkg/cmd/outpost_event_list.go b/pkg/cmd/outpost_event_list.go index 89d19b44..7de8ac5e 100644 --- a/pkg/cmd/outpost_event_list.go +++ b/pkg/cmd/outpost_event_list.go @@ -63,7 +63,7 @@ Filters are combined with AND. Time bounds are ISO 8601 datetimes.`, ec.cmd.Flags().StringVar(&ec.dir, "dir", "", "Sort direction (asc, desc)") ec.cmd.Flags().StringVar(&ec.next, "next", "", "Next page cursor") ec.cmd.Flags().StringVar(&ec.prev, "prev", "", "Previous page cursor") - ec.cmd.Flags().StringVar(&ec.output, "output", "", "Output format (json)") + ec.cmd.Flags().Var(newOutputFormatValue(&ec.output), "output", "Output format (json)") return ec } diff --git a/pkg/cmd/outpost_event_retry.go b/pkg/cmd/outpost_event_retry.go index d2ced9bb..86361405 100644 --- a/pkg/cmd/outpost_event_retry.go +++ b/pkg/cmd/outpost_event_retry.go @@ -40,7 +40,7 @@ The destination must be enabled and must subscribe to the event's topic.`, ec.cmd.Flags().StringVar(&ec.eventID, "event-id", "", "The event to retry (required)") ec.cmd.Flags().StringVar(&ec.destinationID, "destination-id", "", "The destination to deliver to (required)") - ec.cmd.Flags().StringVar(&ec.output, "output", "", "Output format (json)") + ec.cmd.Flags().Var(newOutputFormatValue(&ec.output), "output", "Output format (json)") ec.cmd.MarkFlagRequired("event-id") ec.cmd.MarkFlagRequired("destination-id") diff --git a/pkg/cmd/outpost_metrics.go b/pkg/cmd/outpost_metrics.go index 69ce47eb..0130d7fa 100644 --- a/pkg/cmd/outpost_metrics.go +++ b/pkg/cmd/outpost_metrics.go @@ -86,7 +86,7 @@ to bucket the results over time.`, summary, measures, dimensions), mc.cmd.Flags().StringVar(&mc.measures, "measures", "", "Measures to compute, comma-separated (required)") mc.cmd.Flags().StringVar(&mc.dimensions, "dimensions", "", "Dimensions to group by, comma-separated") mc.cmd.Flags().StringArrayVar(&mc.filters, "filter", nil, "Filter as dimension=value (repeatable)") - mc.cmd.Flags().StringVar(&mc.output, "output", "", "Output format (json)") + mc.cmd.Flags().Var(newOutputFormatValue(&mc.output), "output", "Output format (json)") mc.cmd.MarkFlagRequired("start") mc.cmd.MarkFlagRequired("end") diff --git a/pkg/cmd/outpost_publish.go b/pkg/cmd/outpost_publish.go index d5d1f388..6eb59d86 100644 --- a/pkg/cmd/outpost_publish.go +++ b/pkg/cmd/outpost_publish.go @@ -72,7 +72,7 @@ create a Project API key in the Hookdeck dashboard under project settings.`, pc.cmd.Flags().StringVar(&pc.dataFile, "data-file", "", "Path to a JSON file containing the event payload") pc.cmd.Flags().StringArrayVar(&pc.metadata, "metadata", nil, "Metadata as key=value (repeatable)") pc.cmd.Flags().BoolVar(&pc.eligibleForRetry, "eligible-for-retry", true, "Whether failed deliveries should be retried") - pc.cmd.Flags().StringVar(&pc.output, "output", "", "Output format (json)") + pc.cmd.Flags().Var(newOutputFormatValue(&pc.output), "output", "Output format (json)") pc.cmd.MarkFlagRequired("tenant-id") pc.cmd.MarkFlagRequired("topic") diff --git a/pkg/cmd/outpost_status.go b/pkg/cmd/outpost_status.go index 7050d08d..9b7dcc73 100644 --- a/pkg/cmd/outpost_status.go +++ b/pkg/cmd/outpost_status.go @@ -34,7 +34,7 @@ being applied.`, hookdeck outpost status`, } - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") return sc } diff --git a/pkg/cmd/outpost_tenant_get.go b/pkg/cmd/outpost_tenant_get.go index 06a68c2b..c4770096 100644 --- a/pkg/cmd/outpost_tenant_get.go +++ b/pkg/cmd/outpost_tenant_get.go @@ -39,7 +39,7 @@ func newOutpostTenantGetCmd() *outpostTenantGetCmd { }, } - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/outpost_tenant_list.go b/pkg/cmd/outpost_tenant_list.go index e49de038..37f339da 100644 --- a/pkg/cmd/outpost_tenant_list.go +++ b/pkg/cmd/outpost_tenant_list.go @@ -50,7 +50,7 @@ func newOutpostTenantListCmd() *outpostTenantListCmd { tc.cmd.Flags().StringVar(&tc.dir, "dir", "", "Sort direction (asc, desc)") tc.cmd.Flags().StringVar(&tc.next, "next", "", "Next page cursor") tc.cmd.Flags().StringVar(&tc.prev, "prev", "", "Previous page cursor") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/outpost_tenant_portal.go b/pkg/cmd/outpost_tenant_portal.go index 432972f0..3cb27016 100644 --- a/pkg/cmd/outpost_tenant_portal.go +++ b/pkg/cmd/outpost_tenant_portal.go @@ -53,7 +53,7 @@ This requires a portal custom domain to be configured for the project; see tc.cmd.Flags().StringVar(&tc.theme, "theme", "", "Portal theme ("+strings.Join(hookdeck.OutpostPortalThemes, ", ")+")") tc.cmd.Flags().BoolVar(&tc.open, "open", false, "Open the portal URL in your browser") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/outpost_tenant_token.go b/pkg/cmd/outpost_tenant_token.go index 6f7fe092..d0246413 100644 --- a/pkg/cmd/outpost_tenant_token.go +++ b/pkg/cmd/outpost_tenant_token.go @@ -40,7 +40,7 @@ not to be pasted into a shell history or shared.`, }, } - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/outpost_tenant_upsert.go b/pkg/cmd/outpost_tenant_upsert.go index eb087666..54223506 100644 --- a/pkg/cmd/outpost_tenant_upsert.go +++ b/pkg/cmd/outpost_tenant_upsert.go @@ -52,7 +52,7 @@ tenant exists will remove metadata it already had.`, tc.cmd.Flags().StringArrayVar(&tc.metadata, "metadata", nil, "Metadata as key=value (repeatable)") tc.cmd.Flags().StringVar(&tc.metadataFile, "metadata-file", "", "Path to a JSON file of metadata key/value pairs") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/outpost_topic.go b/pkg/cmd/outpost_topic.go index 107cb14c..2ab30fe0 100644 --- a/pkg/cmd/outpost_topic.go +++ b/pkg/cmd/outpost_topic.go @@ -53,7 +53,7 @@ func newOutpostTopicListCmd() *outpostTopicListCmd { hookdeck outpost topic list`, } - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/output_flag.go b/pkg/cmd/output_flag.go new file mode 100644 index 00000000..ea79bb59 --- /dev/null +++ b/pkg/cmd/output_flag.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/pflag" +) + +// outputFormatValue is a pflag.Value for --output flags that only support +// "json". Unknown values are rejected at parse time instead of silently +// falling back to the human-readable output. +type outputFormatValue struct { + target *string +} + +func newOutputFormatValue(target *string) *outputFormatValue { + return &outputFormatValue{target: target} +} + +func (v *outputFormatValue) String() string { + if v.target == nil { + return "" + } + return *v.target +} + +func (v *outputFormatValue) Set(s string) error { + if s != "" && s != "json" { + return fmt.Errorf("unsupported output format %q (supported: json)", s) + } + *v.target = s + return nil +} + +func (v *outputFormatValue) Type() string { return "string" } + +var _ pflag.Value = (*outputFormatValue)(nil) diff --git a/pkg/cmd/output_flag_test.go b/pkg/cmd/output_flag_test.go new file mode 100644 index 00000000..d14ec16d --- /dev/null +++ b/pkg/cmd/output_flag_test.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOutputFormatValue(t *testing.T) { + var out string + v := newOutputFormatValue(&out) + + require.NoError(t, v.Set("")) + require.NoError(t, v.Set("json")) + assert.Equal(t, "json", out) + + err := v.Set("yaml") + require.Error(t, err) + assert.Contains(t, err.Error(), `unsupported output format "yaml"`) + assert.Contains(t, err.Error(), "supported: json") + assert.Equal(t, "json", out, "a rejected value must not overwrite the previous one") +} + +// TestJSONOutputFlagsRejectUnknownFormat checks every command that exposes a +// json-only --output flag, so a newly added command cannot silently ignore an +// unsupported format. +func TestJSONOutputFlagsRejectUnknownFormat(t *testing.T) { + var checked int + var walk func(c *cobra.Command) + walk = func(c *cobra.Command) { + if f := c.Flags().Lookup("output"); f != nil { + if _, ok := f.Value.(*outputFormatValue); ok { + checked++ + assert.Error(t, c.Flags().Set("output", "yaml"), + "command %q accepted --output yaml", c.CommandPath()) + assert.NoError(t, c.Flags().Set("output", "json"), + "command %q rejected --output json", c.CommandPath()) + } + } + for _, child := range c.Commands() { + walk(child) + } + } + walk(RootCmd()) + assert.Greater(t, checked, 10, "expected many commands with a json-only --output flag") +} diff --git a/pkg/cmd/project_list.go b/pkg/cmd/project_list.go index 2621e8fd..685ea0c6 100644 --- a/pkg/cmd/project_list.go +++ b/pkg/cmd/project_list.go @@ -36,7 +36,7 @@ $ hookdeck project list --output json $ hookdeck project list --type gateway`, } - lc.cmd.Flags().StringVar(&lc.output, "output", "", "Output format: json") + lc.cmd.Flags().Var(newOutputFormatValue(&lc.output), "output", "Output format (json)") lc.cmd.Flags().StringVar(&lc.typeFilter, "type", "", "Filter by project type: gateway, outpost, console") return lc diff --git a/pkg/cmd/request_events.go b/pkg/cmd/request_events.go index 678e7b4d..49e720d9 100644 --- a/pkg/cmd/request_events.go +++ b/pkg/cmd/request_events.go @@ -102,7 +102,7 @@ Examples: rc.cmd.Flags().IntVar(&rc.limit, "limit", 100, "Limit number of results") rc.cmd.Flags().StringVar(&rc.next, "next", "", "Pagination cursor for next page") rc.cmd.Flags().StringVar(&rc.prev, "prev", "", "Pagination cursor for previous page") - rc.cmd.Flags().StringVar(&rc.output, "output", "", "Output format (json)") + rc.cmd.Flags().Var(newOutputFormatValue(&rc.output), "output", "Output format (json)") return rc } diff --git a/pkg/cmd/request_get.go b/pkg/cmd/request_get.go index edf463b5..32a169a5 100644 --- a/pkg/cmd/request_get.go +++ b/pkg/cmd/request_get.go @@ -31,7 +31,7 @@ Examples: RunE: rc.runRequestGetCmd, } - rc.cmd.Flags().StringVar(&rc.output, "output", "", "Output format (json)") + rc.cmd.Flags().Var(newOutputFormatValue(&rc.output), "output", "Output format (json)") return rc } diff --git a/pkg/cmd/request_ignored_events.go b/pkg/cmd/request_ignored_events.go index 8343f150..9a86914a 100644 --- a/pkg/cmd/request_ignored_events.go +++ b/pkg/cmd/request_ignored_events.go @@ -38,7 +38,7 @@ Examples: rc.cmd.Flags().IntVar(&rc.limit, "limit", 100, "Limit number of results") rc.cmd.Flags().StringVar(&rc.next, "next", "", "Pagination cursor for next page") rc.cmd.Flags().StringVar(&rc.prev, "prev", "", "Pagination cursor for previous page") - rc.cmd.Flags().StringVar(&rc.output, "output", "", "Output format (json)") + rc.cmd.Flags().Var(newOutputFormatValue(&rc.output), "output", "Output format (json)") return rc } diff --git a/pkg/cmd/request_list.go b/pkg/cmd/request_list.go index 0820b6f4..354bd8ca 100644 --- a/pkg/cmd/request_list.go +++ b/pkg/cmd/request_list.go @@ -83,7 +83,7 @@ Examples: rc.cmd.Flags().IntVar(&rc.limit, "limit", 100, "Limit number of results") rc.cmd.Flags().StringVar(&rc.next, "next", "", "Pagination cursor for next page") rc.cmd.Flags().StringVar(&rc.prev, "prev", "", "Pagination cursor for previous page") - rc.cmd.Flags().StringVar(&rc.output, "output", "", "Output format (json)") + rc.cmd.Flags().Var(newOutputFormatValue(&rc.output), "output", "Output format (json)") return rc } diff --git a/pkg/cmd/source_create.go b/pkg/cmd/source_create.go index edb86dbd..fa1dd70e 100644 --- a/pkg/cmd/source_create.go +++ b/pkg/cmd/source_create.go @@ -57,7 +57,7 @@ Examples: sc.cmd.Flags().StringVar(&sc.AllowedHTTPMethods, "allowed-http-methods", "", "Comma-separated allowed HTTP methods (GET, POST, PUT, PATCH, DELETE)") sc.cmd.Flags().StringVar(&sc.CustomResponseBody, "custom-response-body", "", "Custom response body (max 1000 chars)") sc.cmd.Flags().StringVar(&sc.CustomResponseType, "custom-response-content-type", "", "Custom response content type (json, text, xml)") - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") sc.cmd.MarkFlagRequired("name") sc.cmd.MarkFlagRequired("type") diff --git a/pkg/cmd/source_get.go b/pkg/cmd/source_get.go index 5b9131d5..a40126a4 100644 --- a/pkg/cmd/source_get.go +++ b/pkg/cmd/source_get.go @@ -35,7 +35,7 @@ Examples: RunE: sc.runSourceGetCmd, } - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") addIncludeSourceAuthFlag(sc.cmd, &sc.includeAuth) return sc diff --git a/pkg/cmd/source_list.go b/pkg/cmd/source_list.go index a288d9b2..a5af75ca 100644 --- a/pkg/cmd/source_list.go +++ b/pkg/cmd/source_list.go @@ -44,7 +44,7 @@ Examples: sc.cmd.Flags().StringVar(&sc.sourceType, "type", "", "Filter by source type (e.g. WEBHOOK, STRIPE)") sc.cmd.Flags().BoolVar(&sc.disabled, "disabled", false, "Include disabled sources") sc.cmd.Flags().IntVar(&sc.limit, "limit", 100, "Limit number of results") - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") return sc } diff --git a/pkg/cmd/source_update.go b/pkg/cmd/source_update.go index 9c8d19ed..c0b23e33 100644 --- a/pkg/cmd/source_update.go +++ b/pkg/cmd/source_update.go @@ -56,7 +56,7 @@ Examples: sc.cmd.Flags().StringVar(&sc.AllowedHTTPMethods, "allowed-http-methods", "", "Comma-separated allowed HTTP methods (GET, POST, PUT, PATCH, DELETE)") sc.cmd.Flags().StringVar(&sc.CustomResponseBody, "custom-response-body", "", "Custom response body (max 1000 chars)") sc.cmd.Flags().StringVar(&sc.CustomResponseType, "custom-response-content-type", "", "Custom response content type (json, text, xml)") - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") return sc } diff --git a/pkg/cmd/source_upsert.go b/pkg/cmd/source_upsert.go index 22faeaba..38648d51 100644 --- a/pkg/cmd/source_upsert.go +++ b/pkg/cmd/source_upsert.go @@ -57,7 +57,7 @@ Examples: sc.cmd.Flags().StringVar(&sc.CustomResponseBody, "custom-response-body", "", "Custom response body (max 1000 chars)") sc.cmd.Flags().StringVar(&sc.CustomResponseType, "custom-response-content-type", "", "Custom response content type (json, text, xml)") sc.cmd.Flags().BoolVar(&sc.dryRun, "dry-run", false, "Preview changes without applying") - sc.cmd.Flags().StringVar(&sc.output, "output", "", "Output format (json)") + sc.cmd.Flags().Var(newOutputFormatValue(&sc.output), "output", "Output format (json)") return sc } diff --git a/pkg/cmd/transformation_count.go b/pkg/cmd/transformation_count.go index 44dca8e2..3bb3cf8a 100644 --- a/pkg/cmd/transformation_count.go +++ b/pkg/cmd/transformation_count.go @@ -32,7 +32,7 @@ Examples: } tc.cmd.Flags().StringVar(&tc.name, "name", "", "Filter by transformation name") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_create.go b/pkg/cmd/transformation_create.go index 1cc4959a..69b5542f 100644 --- a/pkg/cmd/transformation_create.go +++ b/pkg/cmd/transformation_create.go @@ -44,7 +44,7 @@ Examples: tc.cmd.Flags().StringVar(&tc.code, "code", "", "JavaScript code string (required if --code-file not set)") tc.cmd.Flags().StringVar(&tc.codeFile, "code-file", "", "Path to JavaScript file (required if --code not set)") tc.cmd.Flags().StringVar(&tc.env, "env", "", "Environment variables as KEY=value,KEY2=value2") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_executions.go b/pkg/cmd/transformation_executions.go index 9b73405c..2965d12c 100644 --- a/pkg/cmd/transformation_executions.go +++ b/pkg/cmd/transformation_executions.go @@ -60,7 +60,7 @@ func newTransformationExecutionsListCmd() *transformationExecutionsListCmd { tc.cmd.Flags().IntVar(&tc.limit, "limit", 100, "Limit number of results") tc.cmd.Flags().StringVar(&tc.next, "next", "", "Pagination cursor for next page") tc.cmd.Flags().StringVar(&tc.prev, "prev", "", "Pagination cursor for previous page") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } @@ -153,7 +153,7 @@ func newTransformationExecutionsGetCmd() *transformationExecutionsGetCmd { RunE: tc.run, } - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_get.go b/pkg/cmd/transformation_get.go index a6114ad4..f78def4f 100644 --- a/pkg/cmd/transformation_get.go +++ b/pkg/cmd/transformation_get.go @@ -34,7 +34,7 @@ Examples: RunE: tc.runTransformationGetCmd, } - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_list.go b/pkg/cmd/transformation_list.go index 89288c2a..d591dcbd 100644 --- a/pkg/cmd/transformation_list.go +++ b/pkg/cmd/transformation_list.go @@ -49,7 +49,7 @@ Examples: tc.cmd.Flags().IntVar(&tc.limit, "limit", 100, "Limit number of results") tc.cmd.Flags().StringVar(&tc.next, "next", "", "Pagination cursor for next page") tc.cmd.Flags().StringVar(&tc.prev, "prev", "", "Pagination cursor for previous page") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_run.go b/pkg/cmd/transformation_run.go index 7ae39aca..584cbbbe 100644 --- a/pkg/cmd/transformation_run.go +++ b/pkg/cmd/transformation_run.go @@ -51,7 +51,7 @@ Examples: tc.cmd.Flags().StringVar(&tc.requestFile, "request-file", "", "Path to request JSON file") tc.cmd.Flags().StringVar(&tc.connectionID, "connection-id", "", "Connection ID for execution context") tc.cmd.Flags().StringVar(&tc.env, "env", "", "Environment variables as KEY=value,KEY2=value2") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_update.go b/pkg/cmd/transformation_update.go index a549b1ac..6ae8c507 100644 --- a/pkg/cmd/transformation_update.go +++ b/pkg/cmd/transformation_update.go @@ -42,7 +42,7 @@ Examples: tc.cmd.Flags().StringVar(&tc.code, "code", "", "New JavaScript code string") tc.cmd.Flags().StringVar(&tc.codeFile, "code-file", "", "Path to JavaScript file") tc.cmd.Flags().StringVar(&tc.env, "env", "", "Environment variables as KEY=value,KEY2=value2") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc } diff --git a/pkg/cmd/transformation_upsert.go b/pkg/cmd/transformation_upsert.go index f69d5b3b..27e9c659 100644 --- a/pkg/cmd/transformation_upsert.go +++ b/pkg/cmd/transformation_upsert.go @@ -43,7 +43,7 @@ Examples: tc.cmd.Flags().StringVar(&tc.codeFile, "code-file", "", "Path to JavaScript file") tc.cmd.Flags().StringVar(&tc.env, "env", "", "Environment variables as KEY=value,KEY2=value2") tc.cmd.Flags().BoolVar(&tc.dryRun, "dry-run", false, "Preview changes without applying") - tc.cmd.Flags().StringVar(&tc.output, "output", "", "Output format (json)") + tc.cmd.Flags().Var(newOutputFormatValue(&tc.output), "output", "Output format (json)") return tc }