From e26a5812b1ae50720ac94a83584d89b538803ab6 Mon Sep 17 00:00:00 2001 From: aman Date: Fri, 17 Jul 2026 16:05:59 +0530 Subject: [PATCH] fix(cmd): fail fast when marking an unknown flag required MarkFlagRequired errors only when the named flag does not exist, and every call site discarded that error, silently making the flag optional. Route all calls through a helper that panics on an unknown name. Co-Authored-By: Claude Fable 5 --- cmd/flags.go | 18 ++++++++++++++++++ cmd/flags_test.go | 24 ++++++++++++++++++++++++ cmd/group.go | 12 ++++++------ cmd/namespace.go | 4 ++-- cmd/organization.go | 14 +++++++------- cmd/permission.go | 12 ++++++------ cmd/policy.go | 10 +++++----- cmd/preferences.go | 6 +++--- cmd/project.go | 12 ++++++------ cmd/reconcile.go | 2 +- cmd/role.go | 12 ++++++------ cmd/seed.go | 2 +- cmd/user.go | 12 ++++++------ 13 files changed, 91 insertions(+), 49 deletions(-) create mode 100644 cmd/flags.go create mode 100644 cmd/flags_test.go diff --git a/cmd/flags.go b/cmd/flags.go new file mode 100644 index 000000000..2ec101e1a --- /dev/null +++ b/cmd/flags.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "fmt" + + cli "github.com/spf13/cobra" +) + +// mustMarkRequired marks each named flag as required. It panics when a flag +// is not defined on the command, so a mistyped or renamed flag name fails on +// the first run instead of silently making the flag optional. +func mustMarkRequired(cmd *cli.Command, names ...string) { + for _, name := range names { + if err := cmd.MarkFlagRequired(name); err != nil { + panic(fmt.Sprintf("marking --%s required on %q: %v", name, cmd.Name(), err)) + } + } +} diff --git a/cmd/flags_test.go b/cmd/flags_test.go new file mode 100644 index 000000000..cf88b32b0 --- /dev/null +++ b/cmd/flags_test.go @@ -0,0 +1,24 @@ +package cmd + +import ( + "testing" + + cli "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func TestMustMarkRequired(t *testing.T) { + t.Run("should mark an existing flag as required", func(t *testing.T) { + cmd := &cli.Command{Use: "test"} + cmd.Flags().String("file", "", "") + + assert.NotPanics(t, func() { mustMarkRequired(cmd, "file") }) + assert.Error(t, cmd.ValidateRequiredFlags()) + }) + + t.Run("should panic on an unknown flag name", func(t *testing.T) { + cmd := &cli.Command{Use: "test"} + + assert.Panics(t, func() { mustMarkRequired(cmd, "no-such-flag") }) + }) +} diff --git a/cmd/group.go b/cmd/group.go index 86d32dad6..aaa6ff736 100644 --- a/cmd/group.go +++ b/cmd/group.go @@ -86,9 +86,9 @@ func createGroupCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the group body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -140,9 +140,9 @@ func editGroupCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the group body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -219,7 +219,7 @@ func viewGroupCommand(cliConfig *Config) *cli.Command { cmd.Flags().BoolVarP(&metadata, "metadata", "m", false, "Set this flag to see metadata") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -283,7 +283,7 @@ func listGroupCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/namespace.go b/cmd/namespace.go index 7a8c8e32a..c259b122f 100644 --- a/cmd/namespace.go +++ b/cmd/namespace.go @@ -89,7 +89,7 @@ func viewNamespaceCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -147,7 +147,7 @@ func listNamespaceCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/organization.go b/cmd/organization.go index 0b20c3f30..2d2d68ea8 100644 --- a/cmd/organization.go +++ b/cmd/organization.go @@ -87,9 +87,9 @@ func createOrganizationCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the organization body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -141,9 +141,9 @@ func editOrganizationCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the organization body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -219,7 +219,7 @@ func viewOrganizationCommand(cliConfig *Config) *cli.Command { cmd.Flags().BoolVarP(&metadata, "metadata", "m", false, "Set this flag to see metadata") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -280,7 +280,7 @@ func listOrganizationCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -340,7 +340,7 @@ func admlistOrganizationCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/permission.go b/cmd/permission.go index 90c7ac6a6..238a714c7 100644 --- a/cmd/permission.go +++ b/cmd/permission.go @@ -86,9 +86,9 @@ func createPermissionCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the permission body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -140,9 +140,9 @@ func editPermissionCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the permission body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -199,7 +199,7 @@ func viewPermissionCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -261,7 +261,7 @@ func listPermissionCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/policy.go b/cmd/policy.go index 998f415d4..2fb2281dc 100644 --- a/cmd/policy.go +++ b/cmd/policy.go @@ -84,9 +84,9 @@ func createPolicyCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the policy body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -138,9 +138,9 @@ func editPolicyCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the policy body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -198,7 +198,7 @@ func viewPolicyCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/preferences.go b/cmd/preferences.go index 04a0abaa5..44cc6fe7f 100644 --- a/cmd/preferences.go +++ b/cmd/preferences.go @@ -83,7 +83,7 @@ func preferencesListCommand(cliConfig *Config) *cobra.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -140,7 +140,7 @@ func preferencesSetCommand(cliConfig *Config) *cobra.Command { cmd.Flags().StringVarP(&name, "name", "n", "", "Name of the preference") cmd.Flags().StringVarP(&value, "value", "v", "", "Value of the preference") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -182,7 +182,7 @@ func preferencesGetCommand(cliConfig *Config) *cobra.Command { }, } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/project.go b/cmd/project.go index 8fe52429f..5f4d4f1a0 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -86,9 +86,9 @@ func createProjectCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the project body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -140,9 +140,9 @@ func editProjectCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the project body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -219,7 +219,7 @@ func viewProjectCommand(cliConfig *Config) *cli.Command { cmd.Flags().BoolVarP(&metadata, "metadata", "m", false, "Set this flag to see metadata") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -283,7 +283,7 @@ func listProjectCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/reconcile.go b/cmd/reconcile.go index 8be765855..eee6f8fe6 100644 --- a/cmd/reconcile.go +++ b/cmd/reconcile.go @@ -57,7 +57,7 @@ func ReconcileCommand(cliConfig *Config) *cli.Command { }, } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the desired-state YAML file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Print the plan without applying changes") cmd.Flags().StringVarP(&header, "header", "H", "", "Header : for auth, e.g. 'Authorization:Basic '") bindFlagsFromClientConfig(cmd) diff --git a/cmd/role.go b/cmd/role.go index a7cc4c094..5d8e6c012 100644 --- a/cmd/role.go +++ b/cmd/role.go @@ -87,9 +87,9 @@ func createRoleCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the role body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -141,9 +141,9 @@ func editRoleCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the role body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -221,7 +221,7 @@ func viewRoleCommand(cliConfig *Config) *cli.Command { cmd.Flags().BoolVarP(&metadata, "metadata", "m", false, "Set this flag to see metadata") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -284,7 +284,7 @@ func listRoleCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } diff --git a/cmd/seed.go b/cmd/seed.go index 042f5614e..f5e44d1f2 100644 --- a/cmd/seed.go +++ b/cmd/seed.go @@ -89,7 +89,7 @@ func SeedCommand(cliConfig *Config) *cli.Command { bindFlagsFromClientConfig(cmd) cmd.Flags().StringVarP(&header, "header", "H", "", "Header ") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") cmd.Flags().StringVarP(&configFile, "config", "c", "", "config file path") return cmd } diff --git a/cmd/user.go b/cmd/user.go index fb54c2697..aaaa80e7a 100644 --- a/cmd/user.go +++ b/cmd/user.go @@ -91,9 +91,9 @@ func createUserCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the user body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -146,9 +146,9 @@ func editUserCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&filePath, "file", "f", "", "Path to the user body file") - cmd.MarkFlagRequired("file") + mustMarkRequired(cmd, "file") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -222,7 +222,7 @@ func viewUserCommand(cliConfig *Config) *cli.Command { cmd.Flags().BoolVarP(&metadata, "metadata", "m", false, "Set this flag to see metadata") cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd } @@ -280,7 +280,7 @@ func listUserCommand(cliConfig *Config) *cli.Command { } cmd.Flags().StringVarP(&header, "header", "H", "", "Header :") - cmd.MarkFlagRequired("header") + mustMarkRequired(cmd, "header") return cmd }