From b27c10f9dafa125c7bf2bf2bbf61a505b8958e65 Mon Sep 17 00:00:00 2001 From: Lorenzo Cazzoli Date: Thu, 12 Mar 2026 17:55:34 +0100 Subject: [PATCH 1/7] fix: remove interactive from global flag Remove --interactive from global flags. Ensure it is supported on the command that implement it and update prerun check to require the correct flags --- src/cmd/gateway.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- src/cmd/root.go | 1 - src/cmd/tenant.go | 16 +++++++++++++--- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/cmd/gateway.go b/src/cmd/gateway.go index 5ac3e98..6db0192 100644 --- a/src/cmd/gateway.go +++ b/src/cmd/gateway.go @@ -92,9 +92,13 @@ var installGatewaySubCmd = &cobra.Command{ Use: "install", Short: "installs a gateway for a tenant", PreRun: func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("gateway-id") + interactive, _ := cmd.Flags().GetBool("interactive") + if !interactive { + cmd.MarkFlagRequired("gateway-id") + } }, Run: func(cmd *cobra.Command, args []string) { + interactive, _ := cmd.Flags().GetBool("interactive") if !interactive { if err := action.InstallGateway(cmd, args); err != nil { utils.PrintError(err) @@ -142,8 +146,45 @@ func init() { installGatewaySubCmd.Flags().Bool("no-offloader", false, "Skip offloader setup") installGatewaySubCmd.Flags().Bool("no-s3", false, "Skip S3 setup") installGatewaySubCmd.Flags().Bool("ingress", false, "Install only ingress") + installGatewaySubCmd.Flags().BoolP("interactive", "i", false, "Run in interactive mode") rootCmd.AddCommand(gatewayCmd) gatewayCmd.PersistentFlags().String("tenant-id", "", "ID of the tenant") - gatewayCmd.MarkPersistentFlagRequired("tenant-id") + + // Conditionally require tenant-id for non-interactive commands + createGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("name") + cmd.MarkFlagRequired("location") + cmd.MarkFlagRequired("tenant-id") + } + describeGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") + } + updateGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") + } + listGatewaysSubCmd.PreRun = func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("tenant-id") + allowedSortingKeys := []string{"id, name"} + sort, _ := cmd.Flags().GetString("sort") + + if sort != "" && !utils.Contains(allowedSortingKeys, sort) { + fmt.Println("Error: invalid sort key provided, allowed keys are: id, name") + cmd.Usage() + os.Exit(1) + } + } + removeGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") + } + installGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { + interactive, _ := cmd.Flags().GetBool("interactive") + if !interactive { + cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") + } + } } diff --git a/src/cmd/root.go b/src/cmd/root.go index a27565e..54d016a 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -71,7 +71,6 @@ func Execute(packageJSON []byte) { func init() { // Persistent flags (available to all subcommands) - rootCmd.PersistentFlags().BoolVarP(&interactive, "interactive", "i", false, "Run in interactive mode") rootCmd.PersistentFlags().String("profile", "", "Profile Configuration") rootCmd.PersistentFlags().String("output", "human", "Output format: human (default), json, yaml, xml") rootCmd.PersistentFlags().Bool("no-headers", false, "Suppress table headers in human output (for easier scripting)") diff --git a/src/cmd/tenant.go b/src/cmd/tenant.go index db3e265..2d890f7 100644 --- a/src/cmd/tenant.go +++ b/src/cmd/tenant.go @@ -162,10 +162,14 @@ var configureTenantDNSSubCmd = &cobra.Command{ Short: "configures DNS for a tenant", Long: "This command prints the value of the TXT record that needs to be added with the name '_acme-challenge'", PreRun: func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("tenant-id") - cmd.MarkFlagRequired("domain") + interactive, _ := cmd.Flags().GetBool("interactive") + if !interactive { + cmd.MarkFlagRequired("tenant-id") + cmd.MarkFlagRequired("domain") + } }, Run: func(cmd *cobra.Command, args []string) { + interactive, _ := cmd.Flags().GetBool("interactive") if !interactive { if err := action.ConfigureTenantDNS(cmd, args); err != nil { utils.PrintError(err) @@ -182,9 +186,13 @@ var verifyTenantDNSSubCmd = &cobra.Command{ Use: "verify-dns", Short: "verifies DNS for a tenant", PreRun: func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("tenant-id") + interactive, _ := cmd.Flags().GetBool("interactive") + if !interactive { + cmd.MarkFlagRequired("tenant-id") + } }, Run: func(cmd *cobra.Command, args []string) { + interactive, _ := cmd.Flags().GetBool("interactive") if !interactive { if err := action.VerifyTenantDNS(cmd, args); err != nil { utils.PrintError(err) @@ -234,9 +242,11 @@ func init() { configureTenantDNSSubCmd.Flags().String("tenant-id", "", "ID of the tenant") configureTenantDNSSubCmd.Flags().String("domain", "", "Domain to configure for the tenant") configureTenantDNSSubCmd.Flags().Bool("force", false, "Force the configuration of DNS even if it already exists") + configureTenantDNSSubCmd.Flags().BoolP("interactive", "i", false, "Run in interactive mode") tenantCmd.AddCommand(verifyTenantDNSSubCmd) verifyTenantDNSSubCmd.Flags().String("tenant-id", "", "ID of the tenant") + verifyTenantDNSSubCmd.Flags().BoolP("interactive", "i", false, "Run in interactive mode") rootCmd.AddCommand(tenantCmd) } From 9d1ad56a5bed3e9864126c106808d93447548acb Mon Sep 17 00:00:00 2001 From: Lorenzo Cazzoli Date: Thu, 12 Mar 2026 17:55:56 +0100 Subject: [PATCH 2/7] chore: ignore opencode configuration --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a804171..763bd2b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,7 @@ dist/ *.bazelrc.user +.opencode +opencode/ +opencode.json mimir-key \ No newline at end of file From 1a029a03ca106f77909610d14e1ba2e6607945cd Mon Sep 17 00:00:00 2001 From: Lorenzo Cazzoli Date: Fri, 13 Mar 2026 18:41:34 +0100 Subject: [PATCH 3/7] fix: review flags controls and prerun implementations --- src/cmd/agent.go | 75 ++++++++++------------- src/cmd/gateway.go | 66 ++++++-------------- src/cmd/nexus.go | 6 +- src/cmd/node.go | 90 ++++++++++++---------------- src/cmd/operator.go | 52 ++++++++++------ src/cmd/policy.go | 2 - src/cmd/project.go | 17 +++--- src/cmd/redundancy_class.go | 16 ++++- src/cmd/redundancy_class_recovery.go | 6 +- src/cmd/swarm.go | 13 +--- src/cmd/tenant.go | 27 +++------ src/cmd/user.go | 10 +++- 12 files changed, 171 insertions(+), 209 deletions(-) diff --git a/src/cmd/agent.go b/src/cmd/agent.go index c6c10a5..f23cf19 100644 --- a/src/cmd/agent.go +++ b/src/cmd/agent.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" - "os" "github.com/cubbit/composer-cli/src/action" "github.com/cubbit/composer-cli/utils" @@ -16,39 +15,25 @@ var agentCmd = &cobra.Command{ } var createAgentSubCmd = &cobra.Command{ - Use: "create", - Short: "create a new agent", + Use: "create", + Short: "create a new agent or multiple agents from a batch file", + Example: "cubbit agent create --swarm-id --nexus-id --node-id --batch --file ./batch.json\ncubbit agent create --swarm-id --nexus-id --node-id --agent-port 8080 --agent-disk /dev/sdb --agent-mount-point /mnt/agent", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("nexus-id") cmd.MarkFlagRequired("node-id") - batch, _ := cmd.Flags().GetBool("batch") - if batch { - file, _ := cmd.Flags().GetString("file") - if file == "" { - fmt.Println("Error: --file flag is required when using --batch mode.") - cmd.Usage() - os.Exit(1) - } + cmd.MarkFlagsOneRequired("batch", "agent-port") + cmd.MarkFlagsRequiredTogether("batch", "file") + cmd.MarkFlagsRequiredTogether("agent-port", "agent-disk", "agent-mount-point") - if _, err := os.Stat(file); os.IsNotExist(err) { - fmt.Println("Error: file does not exist:", file) - os.Exit(1) - } - return - } - - cmd.MarkFlagRequired("agent-port") - cmd.MarkFlagRequired("agent-disk") - cmd.MarkFlagRequired("agent-mount-point") - - agentPort := cmd.Flags().Lookup("agent-port") - if agentPort != nil && !agentPort.Changed { - fmt.Println("Error: --agent-port must be explicitly provided.") - cmd.Usage() - os.Exit(1) - } + cmd.MarkFlagsMutuallyExclusive("batch", "agent-port") + cmd.MarkFlagsMutuallyExclusive("batch", "agent-disk") + cmd.MarkFlagsMutuallyExclusive("batch", "agent-mount-point") + cmd.MarkFlagsMutuallyExclusive("file", "agent-port") + cmd.MarkFlagsMutuallyExclusive("file", "agent-disk") + cmd.MarkFlagsMutuallyExclusive("file", "agent-mount-point") }, Run: func(cmd *cobra.Command, args []string) { batch, _ := cmd.Flags().GetBool("batch") @@ -69,6 +54,7 @@ var editAgentSubCmd = &cobra.Command{ Use: "edit", Short: "edit an agent", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("nexus-id") cmd.MarkFlagRequired("node-id") cmd.MarkFlagRequired("agent-id") @@ -84,6 +70,7 @@ var describeAgentSubCmd = &cobra.Command{ Use: "describe", Short: "describe an agent", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("nexus-id") cmd.MarkFlagRequired("node-id") cmd.MarkFlagRequired("agent-id") @@ -96,31 +83,31 @@ var describeAgentSubCmd = &cobra.Command{ } var listAgentsSubCmd = &cobra.Command{ - Use: "list", - Short: "list agents", + Use: "list", + Short: "list agents in a node or redundancy class", + Example: "cubbit list agents --swarm-id --nexus-id --node-id \ncubbit list agents --swarm-id --redundancy-class-id ", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") + cmd.MarkFlagsOneRequired("redundancy-class-id", "node-id") + + cmd.MarkFlagsRequiredTogether("nexus-id", "node-id") cmd.MarkFlagsMutuallyExclusive("nexus-id", "redundancy-class-id") cmd.MarkFlagsMutuallyExclusive("node-id", "redundancy-class-id") - + }, + Run: func(cmd *cobra.Command, args []string) { allowedSortingKeys := []string{"id", "node_id", "port", "created_at"} sort, _ := cmd.Flags().GetString("sort") - if sort != "" && !utils.Contains(allowedSortingKeys, sort) { fmt.Println("Error: invalid sort key provided, allowed keys are: id, node_id", "port", "created_at") - cmd.Usage() - os.Exit(1) + return } filter, _ := cmd.Flags().GetString("filter") - if filter != "" { - if !utils.IsValidFilter(filter) { - fmt.Println("Error: invalid filter provided, allowed format is: key:value key:value ...") - cmd.Usage() - os.Exit(1) - } + if filter != "" && !utils.IsValidFilter(filter) { + fmt.Println("Error: invalid filter provided, allowed format is: key:value key:value ...") + return } - }, - Run: func(cmd *cobra.Command, args []string) { + if err := action.ListAgents(cmd, args); err != nil { utils.PrintError(err) } @@ -131,6 +118,7 @@ var removeAgentSubCmd = &cobra.Command{ Use: "remove", Short: "remove an agent", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("nexus-id") cmd.MarkFlagRequired("node-id") cmd.MarkFlagRequired("agent-id") @@ -146,6 +134,7 @@ var checkAgentStatusSubCmd = &cobra.Command{ Use: "status", Short: "check the status of an agent", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("nexus-id") cmd.MarkFlagRequired("node-id") cmd.MarkFlagRequired("agent-id") @@ -194,8 +183,6 @@ func init() { rootCmd.AddCommand(agentCmd) agentCmd.PersistentFlags().String("swarm-id", "", "ID of the swarm") - agentCmd.MarkPersistentFlagRequired("swarm-id") agentCmd.PersistentFlags().String("nexus-id", "", "ID of the nexus") agentCmd.PersistentFlags().String("node-id", "", "ID of the node") - } diff --git a/src/cmd/gateway.go b/src/cmd/gateway.go index 6db0192..c6a7183 100644 --- a/src/cmd/gateway.go +++ b/src/cmd/gateway.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" - "os" "github.com/cubbit/composer-cli/src/action" "github.com/cubbit/composer-cli/utils" @@ -21,6 +20,7 @@ var createGatewaySubCmd = &cobra.Command{ PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("name") cmd.MarkFlagRequired("location") + cmd.MarkFlagRequired("tenant-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.CreateGateway(cmd, args); err != nil { @@ -34,6 +34,7 @@ var describeGatewaySubCmd = &cobra.Command{ Short: "describes tenant gateways", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.DescribeGateway(cmd, args); err != nil { @@ -47,6 +48,7 @@ var updateGatewaySubCmd = &cobra.Command{ Short: "updates a gateway in a tenant", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.UpdateGateway(cmd, args); err != nil { @@ -59,16 +61,16 @@ var listGatewaysSubCmd = &cobra.Command{ Use: "list", Short: "lists tenant gateways", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("tenant-id") + }, + Run: func(cmd *cobra.Command, args []string) { allowedSortingKeys := []string{"id, name"} sort, _ := cmd.Flags().GetString("sort") - if sort != "" && !utils.Contains(allowedSortingKeys, sort) { fmt.Println("Error: invalid sort key provided, allowed keys are: id, name") - cmd.Usage() - os.Exit(1) + return } - }, - Run: func(cmd *cobra.Command, args []string) { + if err := action.ListGateways(cmd, args); err != nil { utils.PrintError(err) } @@ -80,6 +82,7 @@ var removeGatewaySubCmd = &cobra.Command{ Short: "removes a tenant gateway", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("gateway-id") + cmd.MarkFlagRequired("tenant-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.RemoveGateway(cmd, args); err != nil { @@ -89,13 +92,15 @@ var removeGatewaySubCmd = &cobra.Command{ } var installGatewaySubCmd = &cobra.Command{ - Use: "install", - Short: "installs a gateway for a tenant", + Use: "install", + Short: "installs a gateway for a tenant", + Example: "cubbit gateway install --interactive\ncubbit gateway install --tenant-id --gateway-id ", PreRun: func(cmd *cobra.Command, args []string) { - interactive, _ := cmd.Flags().GetBool("interactive") - if !interactive { - cmd.MarkFlagRequired("gateway-id") - } + cmd.MarkFlagsOneRequired("interactive", "gateway-id") + + cmd.MarkFlagsRequiredTogether("tenant-id", "gateway-id") + cmd.MarkFlagsMutuallyExclusive("interactive", "gateway-id") + cmd.MarkFlagsMutuallyExclusive("interactive", "tenant-id") }, Run: func(cmd *cobra.Command, args []string) { interactive, _ := cmd.Flags().GetBool("interactive") @@ -150,41 +155,4 @@ func init() { rootCmd.AddCommand(gatewayCmd) gatewayCmd.PersistentFlags().String("tenant-id", "", "ID of the tenant") - - // Conditionally require tenant-id for non-interactive commands - createGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("name") - cmd.MarkFlagRequired("location") - cmd.MarkFlagRequired("tenant-id") - } - describeGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("gateway-id") - cmd.MarkFlagRequired("tenant-id") - } - updateGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("gateway-id") - cmd.MarkFlagRequired("tenant-id") - } - listGatewaysSubCmd.PreRun = func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("tenant-id") - allowedSortingKeys := []string{"id, name"} - sort, _ := cmd.Flags().GetString("sort") - - if sort != "" && !utils.Contains(allowedSortingKeys, sort) { - fmt.Println("Error: invalid sort key provided, allowed keys are: id, name") - cmd.Usage() - os.Exit(1) - } - } - removeGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { - cmd.MarkFlagRequired("gateway-id") - cmd.MarkFlagRequired("tenant-id") - } - installGatewaySubCmd.PreRun = func(cmd *cobra.Command, args []string) { - interactive, _ := cmd.Flags().GetBool("interactive") - if !interactive { - cmd.MarkFlagRequired("gateway-id") - cmd.MarkFlagRequired("tenant-id") - } - } } diff --git a/src/cmd/nexus.go b/src/cmd/nexus.go index 793f4de..fc5ad97 100644 --- a/src/cmd/nexus.go +++ b/src/cmd/nexus.go @@ -16,6 +16,7 @@ var createNexusSubCmd = &cobra.Command{ Use: "create", Short: "create a new nexus", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") cmd.MarkFlagRequired("name") cmd.MarkFlagRequired("location") cmd.MarkFlagRequired("provider-id") @@ -32,6 +33,7 @@ var editNexusSubCmd = &cobra.Command{ Short: "edit a nexus", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("nexus-id") + cmd.MarkFlagRequired("swarm-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.EditNexus(cmd, args); err != nil { @@ -45,6 +47,7 @@ var removeNexusSubCmd = &cobra.Command{ Short: "remove a nexus", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("nexus-id") + cmd.MarkFlagRequired("swarm-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.RemoveNexus(cmd, args); err != nil { @@ -57,6 +60,7 @@ var listNexusesSubCmd = &cobra.Command{ Use: "list", Short: "list nexuses", PreRun: func(cmd *cobra.Command, args []string) { + cmd.MarkFlagRequired("swarm-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.ListNexuses(cmd, args); err != nil { @@ -70,6 +74,7 @@ var describeNexusSubCmd = &cobra.Command{ Short: "describe a nexus", PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("nexus-id") + cmd.MarkFlagRequired("swarm-id") }, Run: func(cmd *cobra.Command, args []string) { if err := action.DescribeNexus(cmd, args); err != nil { @@ -121,5 +126,4 @@ func init() { rootCmd.AddCommand(nexusCmd) nexusCmd.PersistentFlags().String("swarm-id", "", "ID of the swarm") - nexusCmd.MarkPersistentFlagRequired("swarm-id") } diff --git a/src/cmd/node.go b/src/cmd/node.go index 9db454b..7bb0b24 100644 --- a/src/cmd/node.go +++ b/src/cmd/node.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" - "os" "github.com/cubbit/composer-cli/src/action" "github.com/cubbit/composer-cli/utils" @@ -16,44 +15,22 @@ var nodeCmd = &cobra.Command{ } var createNodeSubCmd = &cobra.Command{ - Use: "create", - Short: "create a new node", + Use: "create", + Short: "create a new node or a batch of nodes", + Example: "cubbit create --swarm-id --nexus-id --batch --file ./batch.json\n cubbit create --swarm-id --nexus-id --name --private-ip --public-ip --label