From f0a402b78dc5306fea759fd93e89f03093db4ded Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Wed, 8 Apr 2026 12:39:44 +0200 Subject: [PATCH 1/4] update: add positional argument restriction to root command --- cli/cmd/root.go | 1 + cli/cmd/root_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 cli/cmd/root_test.go diff --git a/cli/cmd/root.go b/cli/cmd/root.go index ffb1e8f6..6160e778 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -26,6 +26,7 @@ func GetRootCmd() *cobra.Command { This command can be used to run common tasks related to managing codesphere installations, like downloading new versions.`), + Args: cobra.NoArgs, PersistentPreRun: func(cmd *cobra.Command, args []string) { apiKey := os.Getenv("OMS_PORTAL_API_KEY") diff --git a/cli/cmd/root_test.go b/cli/cmd/root_test.go new file mode 100644 index 00000000..dc326d86 --- /dev/null +++ b/cli/cmd/root_test.go @@ -0,0 +1,72 @@ +// Copyright (c) Codesphere Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd_test + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" + + "github.com/codesphere-cloud/oms/cli/cmd" +) + +func findCommand(root *cobra.Command, path ...string) *cobra.Command { + current := root + for _, p := range path { + next, _, err := current.Find([]string{p}) + if err != nil || next == nil { + return nil + } + current = next + } + + return current +} + +var _ = Describe("RootCmd", func() { + It("rejects positional args for commands configured with no positional args", func() { + rootCmd := cmd.GetRootCmd() + rootCmd.SilenceErrors = true + rootCmd.SilenceUsage = true + + updateAPIKeyCmd := findCommand(rootCmd, "update", "api-key") + Expect(updateAPIKeyCmd).NotTo(BeNil()) + + // Avoid running real command logic; argument validation happens before RunE. + updateAPIKeyCmd.RunE = func(_ *cobra.Command, _ []string) error { return nil } + + rootCmd.SetArgs([]string{"update", "api-key", "--id", "abc123", "--valid-for", "1d", "extra"}) + err := rootCmd.Execute() + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("accepts 0 arg(s), received 1")) + }) + + It("allows positional version for download package despite root cobra.NoArgs", func() { + rootCmd := cmd.GetRootCmd() + rootCmd.SilenceErrors = true + rootCmd.SilenceUsage = true + + downloadPackageCmd := findCommand(rootCmd, "download", "package") + Expect(downloadPackageCmd).NotTo(BeNil()) + + executed := false + downloadPackageCmd.RunE = func(_ *cobra.Command, args []string) error { + executed = true + if len(args) != 1 { + return fmt.Errorf("expected one positional arg, got %d", len(args)) + } + + return nil + } + + rootCmd.SetArgs([]string{"download", "package", "codesphere-v1.55.0"}) + err := rootCmd.Execute() + + Expect(err).NotTo(HaveOccurred()) + Expect(executed).To(BeTrue()) + }) +}) From 100e467a615b782295c96c32d6e95a4b71adf197 Mon Sep 17 00:00:00 2001 From: Simon Herrmann Date: Thu, 9 Apr 2026 16:21:07 +0200 Subject: [PATCH 2/4] update: add helper function for adding commands with inherited positional argument parameter --- cli/cmd/argocd.go | 2 +- cli/cmd/beta.go | 2 +- cli/cmd/beta_install.go | 2 +- cli/cmd/bootstrap_gcp.go | 2 +- cli/cmd/bootstrap_gcp_cleanup.go | 2 +- cli/cmd/bootstrap_gcp_postconfig.go | 2 +- cli/cmd/bootstrap_local.go | 2 +- cli/cmd/build.go | 2 +- cli/cmd/build_image.go | 2 +- cli/cmd/build_images.go | 2 +- cli/cmd/download.go | 2 +- cli/cmd/download_k0s.go | 2 +- cli/cmd/download_package.go | 3 ++- cli/cmd/extend.go | 2 +- cli/cmd/extend_baseimage.go | 2 +- cli/cmd/init.go | 2 +- cli/cmd/init_install_config.go | 2 +- cli/cmd/install.go | 2 +- cli/cmd/install_ceph.go | 2 +- cli/cmd/install_codesphere.go | 2 +- cli/cmd/install_k0s.go | 2 +- cli/cmd/licenses.go | 2 +- cli/cmd/list.go | 2 +- cli/cmd/list_api_keys.go | 2 +- cli/cmd/list_packages.go | 2 +- cli/cmd/register.go | 2 +- cli/cmd/revoke.go | 2 +- cli/cmd/revoke_api_key.go | 2 +- cli/cmd/root.go | 9 +++++++++ cli/cmd/root_test.go | 23 ++++++++--------------- cli/cmd/smoketest.go | 2 +- cli/cmd/smoketest_codesphere.go | 2 +- cli/cmd/update.go | 2 +- cli/cmd/update_api_key.go | 2 +- cli/cmd/update_dockerfile.go | 2 +- cli/cmd/update_install_config.go | 2 +- cli/cmd/update_oms.go | 2 +- cli/cmd/upgrade.go | 2 +- cli/cmd/upgrade_ceph.go | 2 +- cli/cmd/upgrade_codesphere.go | 2 +- cli/cmd/version.go | 2 +- 41 files changed, 57 insertions(+), 54 deletions(-) diff --git a/cli/cmd/argocd.go b/cli/cmd/argocd.go index e70cc949..a5c2e58c 100644 --- a/cli/cmd/argocd.go +++ b/cli/cmd/argocd.go @@ -70,5 +70,5 @@ func AddArgoCDCmd(parentCmd *cobra.Command, opts *GlobalOptions) { argocd.cmd.Flags().BoolVar(&argocd.Opts.FullInstall, "deploy-dc-config", false, "Install Codesphere-managed resources (AppProjects, Repo Creds, ...) after installing the chart") argocd.cmd.RunE = argocd.RunE - parentCmd.AddCommand(argocd.cmd) + AddCmd(parentCmd, argocd.cmd) } diff --git a/cli/cmd/beta.go b/cli/cmd/beta.go index 02dd1792..504a80ad 100644 --- a/cli/cmd/beta.go +++ b/cli/cmd/beta.go @@ -21,7 +21,7 @@ func AddBetaCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Be aware that that usage and behavior may change as the features are developed.`), }, } - rootCmd.AddCommand(beta.cmd) + AddCmd(rootCmd, beta.cmd) AddExtendCmd(beta.cmd, opts) AddBootstrapGcpCmd(beta.cmd, opts) diff --git a/cli/cmd/beta_install.go b/cli/cmd/beta_install.go index 19ba2e1c..6774af70 100644 --- a/cli/cmd/beta_install.go +++ b/cli/cmd/beta_install.go @@ -19,6 +19,6 @@ func AddBetaInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Short: "Install beta components", }, } - rootCmd.AddCommand(install.cmd) + AddCmd(rootCmd, install.cmd) AddArgoCDCmd(install.cmd, opts) } diff --git a/cli/cmd/bootstrap_gcp.go b/cli/cmd/bootstrap_gcp.go index 5a37b801..ec86ba71 100644 --- a/cli/cmd/bootstrap_gcp.go +++ b/cli/cmd/bootstrap_gcp.go @@ -102,7 +102,7 @@ func AddBootstrapGcpCmd(parent *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(bootstrapGcpCmd.cmd, "billing-account") util.MarkFlagRequired(bootstrapGcpCmd.cmd, "base-domain") - parent.AddCommand(bootstrapGcpCmd.cmd) + AddCmd(parent, bootstrapGcpCmd.cmd) AddBootstrapGcpPostconfigCmd(bootstrapGcpCmd.cmd, opts) AddBootstrapGcpCleanupCmd(bootstrapGcpCmd.cmd, opts) } diff --git a/cli/cmd/bootstrap_gcp_cleanup.go b/cli/cmd/bootstrap_gcp_cleanup.go index b67747d9..ee71c0e0 100644 --- a/cli/cmd/bootstrap_gcp_cleanup.go +++ b/cli/cmd/bootstrap_gcp_cleanup.go @@ -116,5 +116,5 @@ func AddBootstrapGcpCleanupCmd(bootstrapGcp *cobra.Command, opts *GlobalOptions) flags.StringVar(&cleanup.Opts.DNSProjectID, "dns-project-id", "", "GCP Project ID for DNS zone (optional, will use infra file if not provided)") cleanup.cmd.RunE = cleanup.RunE - bootstrapGcp.AddCommand(cleanup.cmd) + AddCmd(bootstrapGcp, cleanup.cmd) } diff --git a/cli/cmd/bootstrap_gcp_postconfig.go b/cli/cmd/bootstrap_gcp_postconfig.go index 148ff665..961e2baf 100644 --- a/cli/cmd/bootstrap_gcp_postconfig.go +++ b/cli/cmd/bootstrap_gcp_postconfig.go @@ -70,6 +70,6 @@ func AddBootstrapGcpPostconfigCmd(bootstrapGcp *cobra.Command, opts *GlobalOptio flags.StringVar(&postconfig.Opts.InstallConfigPath, "install-config-path", "config.yaml", "Path to the installation configuration file") flags.StringVar(&postconfig.Opts.PrivateKeyPath, "private-key-path", "", "Path to the GCP service account private key file (optional)") - bootstrapGcp.AddCommand(postconfig.cmd) + AddCmd(bootstrapGcp, postconfig.cmd) postconfig.cmd.RunE = postconfig.RunE } diff --git a/cli/cmd/bootstrap_local.go b/cli/cmd/bootstrap_local.go index e0585357..bbffbd36 100644 --- a/cli/cmd/bootstrap_local.go +++ b/cli/cmd/bootstrap_local.go @@ -84,7 +84,7 @@ func AddBootstrapLocalCmd(parent *cobra.Command) { util.MarkFlagRequired(bootstrapLocalCmd.cmd, "registry-user") - parent.AddCommand(bootstrapLocalCmd.cmd) + AddCmd(parent, bootstrapLocalCmd.cmd) } func (c *BootstrapLocalCmd) BootstrapLocal() error { diff --git a/cli/cmd/build.go b/cli/cmd/build.go index 1bf87ca0..d432d0ed 100644 --- a/cli/cmd/build.go +++ b/cli/cmd/build.go @@ -24,5 +24,5 @@ func AddBuildCmd(rootCmd *cobra.Command, opts *GlobalOptions) { AddBuildImagesCmd(build.cmd, opts) AddBuildImageCmd(build.cmd, opts) - rootCmd.AddCommand(build.cmd) + AddCmd(rootCmd, build.cmd) } diff --git a/cli/cmd/build_image.go b/cli/cmd/build_image.go index 5769293d..04575c6f 100644 --- a/cli/cmd/build_image.go +++ b/cli/cmd/build_image.go @@ -62,7 +62,7 @@ func AddBuildImageCmd(parentCmd *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(imageCmd.cmd, "package") util.MarkFlagRequired(imageCmd.cmd, "registry") - parentCmd.AddCommand(imageCmd.cmd) + AddCmd(parentCmd, imageCmd.cmd) imageCmd.cmd.RunE = imageCmd.RunE } diff --git a/cli/cmd/build_images.go b/cli/cmd/build_images.go index 40a52fa2..120bebd0 100644 --- a/cli/cmd/build_images.go +++ b/cli/cmd/build_images.go @@ -58,7 +58,7 @@ func AddBuildImagesCmd(build *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(buildImages.cmd, "config") - build.AddCommand(buildImages.cmd) + AddCmd(build, buildImages.cmd) buildImages.cmd.RunE = buildImages.RunE } diff --git a/cli/cmd/download.go b/cli/cmd/download.go index 5f0bc368..6cf32141 100644 --- a/cli/cmd/download.go +++ b/cli/cmd/download.go @@ -22,7 +22,7 @@ func AddDownloadCmd(rootCmd *cobra.Command, opts *GlobalOptions) { e.g. available Codesphere packages`), }, } - rootCmd.AddCommand(download.cmd) + AddCmd(rootCmd, download.cmd) AddDownloadPackageCmd(download.cmd, opts) AddDownloadK0sCmd(download.cmd, opts) diff --git a/cli/cmd/download_k0s.go b/cli/cmd/download_k0s.go index 68b4bd10..18c4cba8 100644 --- a/cli/cmd/download_k0s.go +++ b/cli/cmd/download_k0s.go @@ -66,7 +66,7 @@ func AddDownloadK0sCmd(download *cobra.Command, opts *GlobalOptions) { k0s.cmd.Flags().BoolVarP(&k0s.Opts.Force, "force", "f", false, "Force download even if k0s binary exists") k0s.cmd.Flags().BoolVarP(&k0s.Opts.Quiet, "quiet", "q", false, "Suppress progress output during download") - download.AddCommand(k0s.cmd) + AddCmd(download, k0s.cmd) k0s.cmd.RunE = k0s.RunE } diff --git a/cli/cmd/download_package.go b/cli/cmd/download_package.go index 36b090cb..4d0ebf38 100644 --- a/cli/cmd/download_package.go +++ b/cli/cmd/download_package.go @@ -61,6 +61,7 @@ func AddDownloadPackageCmd(download *cobra.Command, opts *GlobalOptions) { Short: "Download a codesphere package", Long: io.Long(`Download a specific version of a Codesphere package To list available packages, run oms list packages.`), + Args: cobra.ArbitraryArgs, Example: formatExamples("download package", []io.Example{ {Cmd: "codesphere-v1.55.0", Desc: "Download Codesphere version 1.55.0"}, {Cmd: "--version codesphere-v1.55.0", Desc: "Download Codesphere version 1.55.0"}, @@ -88,7 +89,7 @@ func AddDownloadPackageCmd(download *cobra.Command, opts *GlobalOptions) { pkg.cmd.Flags().StringVarP(&pkg.Opts.Hash, "hash", "H", "", "Hash of the version to download if multiple builds exist for the same version") pkg.cmd.Flags().StringVarP(&pkg.Opts.Filename, "file", "f", "installer.tar.gz", "Specify artifact to download") pkg.cmd.Flags().BoolVarP(&pkg.Opts.Quiet, "quiet", "q", false, "Suppress progress output during download") - download.AddCommand(pkg.cmd) + AddCmd(download, pkg.cmd) pkg.cmd.RunE = pkg.RunE } diff --git a/cli/cmd/extend.go b/cli/cmd/extend.go index a0b0571c..d227ee71 100644 --- a/cli/cmd/extend.go +++ b/cli/cmd/extend.go @@ -21,7 +21,7 @@ func AddExtendCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Long: io.Long(`Extend Codesphere ressources such as base images to customize them for your needs.`), }, } - rootCmd.AddCommand(extend.cmd) + AddCmd(rootCmd, extend.cmd) AddExtendBaseimageCmd(extend.cmd, opts) } diff --git a/cli/cmd/extend_baseimage.go b/cli/cmd/extend_baseimage.go index fed1ac41..8587608d 100644 --- a/cli/cmd/extend_baseimage.go +++ b/cli/cmd/extend_baseimage.go @@ -69,7 +69,7 @@ func AddExtendBaseimageCmd(extend *cobra.Command, opts *GlobalOptions) { baseimage.cmd.Flags().StringVarP(&baseimage.Opts.Baseimage, "baseimage", "b", "workspace-agent-24.04", "Base image file name inside the package to extend (default: 'workspace-agent-24.04')") baseimage.cmd.Flags().BoolVarP(&baseimage.Opts.Force, "force", "f", false, "Enforce package extraction") - extend.AddCommand(baseimage.cmd) + AddCmd(extend, baseimage.cmd) baseimage.cmd.RunE = baseimage.RunE } diff --git a/cli/cmd/init.go b/cli/cmd/init.go index f4f383c6..ad5e44bd 100644 --- a/cli/cmd/init.go +++ b/cli/cmd/init.go @@ -20,6 +20,6 @@ func AddInitCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Long: io.Long(`Initialize configuration files for Codesphere installation and other components.`), }, } - rootCmd.AddCommand(init.cmd) + AddCmd(rootCmd, init.cmd) AddInitInstallConfigCmd(init.cmd, opts) } diff --git a/cli/cmd/init_install_config.go b/cli/cmd/init_install_config.go index cc1b1383..4652af84 100644 --- a/cli/cmd/init_install_config.go +++ b/cli/cmd/init_install_config.go @@ -168,7 +168,7 @@ func AddInitInstallConfigCmd(init *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(c.cmd, "vault") c.cmd.RunE = c.RunE - init.AddCommand(c.cmd) + AddCmd(init, c.cmd) } func (c *InitInstallConfigCmd) InitInstallConfig(icg installer.InstallConfigManager) error { diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 84ed005e..8e8417c4 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -21,7 +21,7 @@ func AddInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Long: io.Long(`Install Codesphere and other components like Ceph and PostgreSQL.`), }, } - rootCmd.AddCommand(install.cmd) + AddCmd(rootCmd, install.cmd) AddInstallCodesphereCmd(install.cmd, opts) AddInstallK0sCmd(install.cmd, opts) diff --git a/cli/cmd/install_ceph.go b/cli/cmd/install_ceph.go index 320fc772..ab61f884 100644 --- a/cli/cmd/install_ceph.go +++ b/cli/cmd/install_ceph.go @@ -21,5 +21,5 @@ func AddInstallCephCmd(install *cobra.Command) { Long: io.Long(`Coming soon: Install a Ceph cluster`), }, } - install.AddCommand(ceph.cmd) + AddCmd(install, ceph.cmd) } diff --git a/cli/cmd/install_codesphere.go b/cli/cmd/install_codesphere.go index f7b8888b..036a7dd1 100644 --- a/cli/cmd/install_codesphere.go +++ b/cli/cmd/install_codesphere.go @@ -88,7 +88,7 @@ func AddInstallCodesphereCmd(install *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(codesphere.cmd, "config") util.MarkFlagRequired(codesphere.cmd, "priv-key") - install.AddCommand(codesphere.cmd) + AddCmd(install, codesphere.cmd) codesphere.cmd.RunE = codesphere.RunE } diff --git a/cli/cmd/install_k0s.go b/cli/cmd/install_k0s.go index ad38ef5a..726b06d1 100644 --- a/cli/cmd/install_k0s.go +++ b/cli/cmd/install_k0s.go @@ -83,7 +83,7 @@ func AddInstallK0sCmd(install *cobra.Command, opts *GlobalOptions) { _ = k0s.cmd.MarkFlagRequired("install-config") - install.AddCommand(k0s.cmd) + AddCmd(install, k0s.cmd) k0s.cmd.RunE = k0s.RunE } diff --git a/cli/cmd/licenses.go b/cli/cmd/licenses.go index eeb2cd7c..b518d058 100644 --- a/cli/cmd/licenses.go +++ b/cli/cmd/licenses.go @@ -34,6 +34,6 @@ func AddLicensesCmd(rootCmd *cobra.Command) { Long: `Print information about the OMS license and open source licenses of dependencies.`, }, } - rootCmd.AddCommand(licenses.cmd) + AddCmd(rootCmd, licenses.cmd) licenses.cmd.RunE = licenses.RunE } diff --git a/cli/cmd/list.go b/cli/cmd/list.go index 5b406e71..37d0f57c 100644 --- a/cli/cmd/list.go +++ b/cli/cmd/list.go @@ -21,7 +21,7 @@ func AddListCmd(rootCmd *cobra.Command, opts *GlobalOptions) { eg. available Codesphere packages`), }, } - rootCmd.AddCommand(list.cmd) + AddCmd(rootCmd, list.cmd) AddListPackagesCmd(list.cmd, opts) AddListAPIKeysCmd(list.cmd, opts) } diff --git a/cli/cmd/list_api_keys.go b/cli/cmd/list_api_keys.go index 96d318a1..7a5d833d 100644 --- a/cli/cmd/list_api_keys.go +++ b/cli/cmd/list_api_keys.go @@ -42,7 +42,7 @@ func AddListAPIKeysCmd(list *cobra.Command, opts *GlobalOptions) { c.cmd.RunE = c.RunE - list.AddCommand(c.cmd) + AddCmd(list, c.cmd) } func (c *ListAPIKeysCmd) PrintKeysTable(keys []portal.ApiKey) { diff --git a/cli/cmd/list_packages.go b/cli/cmd/list_packages.go index 25bc36cb..dde6228c 100644 --- a/cli/cmd/list_packages.go +++ b/cli/cmd/list_packages.go @@ -57,7 +57,7 @@ func AddListPackagesCmd(list *cobra.Command, opts *GlobalOptions) { _ = builds.cmd.Flags().MarkHidden("list-internal") builds.cmd.Flags().StringVarP(&builds.Opts.Sort, "sort", "s", portal.SortSemver, "Sort order: 'semver' (by semantic version) or 'date' (by build date)") - list.AddCommand(builds.cmd) + AddCmd(list, builds.cmd) } func (c *ListBuildsCmd) PrintPackagesTable(packages portal.Builds) { diff --git a/cli/cmd/register.go b/cli/cmd/register.go index 8ead51c5..99a489bb 100644 --- a/cli/cmd/register.go +++ b/cli/cmd/register.go @@ -63,7 +63,7 @@ func AddRegisterCmd(list *cobra.Command, opts *GlobalOptions) { c.cmd.RunE = c.RunE - list.AddCommand(c.cmd) + AddCmd(list, c.cmd) } func (c *RegisterCmd) Register(p portal.Portal) (*portal.ApiKey, error) { diff --git a/cli/cmd/revoke.go b/cli/cmd/revoke.go index f0ace88c..3332913d 100644 --- a/cli/cmd/revoke.go +++ b/cli/cmd/revoke.go @@ -21,6 +21,6 @@ func AddRevokeCmd(rootCmd *cobra.Command, opts *GlobalOptions) { eg. api keys.`), }, } - rootCmd.AddCommand(revoke.cmd) + AddCmd(rootCmd, revoke.cmd) AddRevokeAPIKeyCmd(revoke.cmd, opts) } diff --git a/cli/cmd/revoke_api_key.go b/cli/cmd/revoke_api_key.go index 3af7148a..e766af72 100644 --- a/cli/cmd/revoke_api_key.go +++ b/cli/cmd/revoke_api_key.go @@ -51,5 +51,5 @@ func AddRevokeAPIKeyCmd(list *cobra.Command, opts *GlobalOptions) { c.cmd.RunE = c.RunE - list.AddCommand(c.cmd) + AddCmd(list, c.cmd) } diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 6160e778..2cc6dd53 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -16,6 +16,15 @@ type GlobalOptions struct { OmsPortalApiKey string } +// AddCmd adds a command, inheriting the parent's Args validator if not explicitly set. +// Individual commands that need different argument rules can override this by setting their own Args validator. +func AddCmd(parent *cobra.Command, cmd *cobra.Command) { + if cmd.Args == nil { + cmd.Args = parent.Args + } + parent.AddCommand(cmd) +} + // GetRootCmd adds all child commands to the root command and sets flags appropriately. func GetRootCmd() *cobra.Command { opts := &GlobalOptions{} diff --git a/cli/cmd/root_test.go b/cli/cmd/root_test.go index dc326d86..06fa7b5e 100644 --- a/cli/cmd/root_test.go +++ b/cli/cmd/root_test.go @@ -4,8 +4,6 @@ package cmd_test import ( - "fmt" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/spf13/cobra" @@ -32,20 +30,16 @@ var _ = Describe("RootCmd", func() { rootCmd.SilenceErrors = true rootCmd.SilenceUsage = true - updateAPIKeyCmd := findCommand(rootCmd, "update", "api-key") - Expect(updateAPIKeyCmd).NotTo(BeNil()) - - // Avoid running real command logic; argument validation happens before RunE. - updateAPIKeyCmd.RunE = func(_ *cobra.Command, _ []string) error { return nil } + licensesCmd := findCommand(rootCmd, "licenses") + Expect(licensesCmd).NotTo(BeNil()) - rootCmd.SetArgs([]string{"update", "api-key", "--id", "abc123", "--valid-for", "1d", "extra"}) + rootCmd.SetArgs([]string{"licenses", "extra"}) err := rootCmd.Execute() - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("accepts 0 arg(s), received 1")) + Expect(err.Error()).To(ContainSubstring("unknown command \"extra\"")) }) - It("allows positional version for download package despite root cobra.NoArgs", func() { + It("allows positional args for commands explicitly defining them", func() { rootCmd := cmd.GetRootCmd() rootCmd.SilenceErrors = true rootCmd.SilenceUsage = true @@ -54,12 +48,10 @@ var _ = Describe("RootCmd", func() { Expect(downloadPackageCmd).NotTo(BeNil()) executed := false + capturedArgs := []string{} downloadPackageCmd.RunE = func(_ *cobra.Command, args []string) error { executed = true - if len(args) != 1 { - return fmt.Errorf("expected one positional arg, got %d", len(args)) - } - + capturedArgs = args return nil } @@ -68,5 +60,6 @@ var _ = Describe("RootCmd", func() { Expect(err).NotTo(HaveOccurred()) Expect(executed).To(BeTrue()) + Expect(capturedArgs).To(Equal([]string{"codesphere-v1.55.0"})) }) }) diff --git a/cli/cmd/smoketest.go b/cli/cmd/smoketest.go index a19ba4aa..02f1db46 100644 --- a/cli/cmd/smoketest.go +++ b/cli/cmd/smoketest.go @@ -21,7 +21,7 @@ func AddSmoketestCmd(rootCmd *cobra.Command, opts *GlobalOptions) { Long: io.Long(`Run automated smoke tests for Codesphere installations to verify functionality.`), }, } - rootCmd.AddCommand(smoketest.cmd) + AddCmd(rootCmd, smoketest.cmd) AddSmoketestCodesphereCmd(smoketest.cmd, opts) } diff --git a/cli/cmd/smoketest_codesphere.go b/cli/cmd/smoketest_codesphere.go index 7494ee34..c20b7f16 100644 --- a/cli/cmd/smoketest_codesphere.go +++ b/cli/cmd/smoketest_codesphere.go @@ -110,7 +110,7 @@ func AddSmoketestCodesphereCmd(parent *cobra.Command, opts *GlobalOptions) { c.cmd.RunE = c.RunE - parent.AddCommand(c.cmd) + AddCmd(parent, c.cmd) } func (c *SmoketestCodesphereCmd) RunSmoketest() (err error) { diff --git a/cli/cmd/update.go b/cli/cmd/update.go index 9d7b1692..45c5614c 100644 --- a/cli/cmd/update.go +++ b/cli/cmd/update.go @@ -26,5 +26,5 @@ func AddUpdateCmd(rootCmd *cobra.Command, opts *GlobalOptions) { AddUpdateDockerfileCmd(updateCmd.cmd, opts) AddUpdateInstallConfigCmd(updateCmd.cmd, opts) - rootCmd.AddCommand(updateCmd.cmd) + AddCmd(rootCmd, updateCmd.cmd) } diff --git a/cli/cmd/update_api_key.go b/cli/cmd/update_api_key.go index 8733ab86..4114f54b 100644 --- a/cli/cmd/update_api_key.go +++ b/cli/cmd/update_api_key.go @@ -45,7 +45,7 @@ func AddApiKeyUpdateCmd(parentCmd *cobra.Command) { util.MarkFlagRequired(apiKeyCmd, "id") util.MarkFlagRequired(apiKeyCmd, "valid-for") - parentCmd.AddCommand(apiKeyCmd) + AddCmd(parentCmd, apiKeyCmd) } func (c *UpdateAPIKeyCmd) UpdateAPIKey(p portal.Portal) error { diff --git a/cli/cmd/update_dockerfile.go b/cli/cmd/update_dockerfile.go index eaaba53e..59a6cd3f 100644 --- a/cli/cmd/update_dockerfile.go +++ b/cli/cmd/update_dockerfile.go @@ -75,7 +75,7 @@ in the specified Dockerfile to use that base image. The base image is loaded int util.MarkFlagRequired(dockerfileCmd.cmd, "dockerfile") util.MarkFlagRequired(dockerfileCmd.cmd, "package") - parentCmd.AddCommand(dockerfileCmd.cmd) + AddCmd(parentCmd, dockerfileCmd.cmd) dockerfileCmd.cmd.RunE = dockerfileCmd.RunE } diff --git a/cli/cmd/update_install_config.go b/cli/cmd/update_install_config.go index b51829f6..6c96f64f 100644 --- a/cli/cmd/update_install_config.go +++ b/cli/cmd/update_install_config.go @@ -138,7 +138,7 @@ func AddUpdateInstallConfigCmd(update *cobra.Command, opts *GlobalOptions) { util.MarkFlagRequired(c.cmd, "vault") c.cmd.RunE = c.RunE - update.AddCommand(c.cmd) + AddCmd(update, c.cmd) } func (c *UpdateInstallConfigCmd) UpdateInstallConfig(icg installer.InstallConfigManager) error { diff --git a/cli/cmd/update_oms.go b/cli/cmd/update_oms.go index e256bc27..78883e41 100644 --- a/cli/cmd/update_oms.go +++ b/cli/cmd/update_oms.go @@ -66,7 +66,7 @@ func AddOmsUpdateCmd(parentCmd *cobra.Command) { return cmdState.SelfUpdate() }, } - parentCmd.AddCommand(omsCmd) + AddCmd(parentCmd, omsCmd) } func (c *UpdateOmsCmd) SelfUpdate() error { diff --git a/cli/cmd/upgrade.go b/cli/cmd/upgrade.go index 52f81a38..17cc50cf 100644 --- a/cli/cmd/upgrade.go +++ b/cli/cmd/upgrade.go @@ -21,5 +21,5 @@ func AddUpgradeCmd(rootCmd *cobra.Command) { }, } - rootCmd.AddCommand(upgrade.cmd) + AddCmd(rootCmd, upgrade.cmd) } diff --git a/cli/cmd/upgrade_ceph.go b/cli/cmd/upgrade_ceph.go index b0bf5ba7..30d1ca4e 100644 --- a/cli/cmd/upgrade_ceph.go +++ b/cli/cmd/upgrade_ceph.go @@ -21,5 +21,5 @@ func AddUpgradeCephCmd(upgrade *cobra.Command) { Long: io.Long(`Coming soon: Install a Ceph cluster`), }, } - upgrade.AddCommand(ceph.cmd) + AddCmd(upgrade, ceph.cmd) } diff --git a/cli/cmd/upgrade_codesphere.go b/cli/cmd/upgrade_codesphere.go index 072082ff..cb35e556 100644 --- a/cli/cmd/upgrade_codesphere.go +++ b/cli/cmd/upgrade_codesphere.go @@ -21,5 +21,5 @@ func AddUpgradeCodesphereCmd(upgrade *cobra.Command) { Long: io.Long(`Coming soon: Upgrade Codesphere to the latest or a specific version`), }, } - upgrade.AddCommand(codesphere.cmd) + AddCmd(upgrade, codesphere.cmd) } diff --git a/cli/cmd/version.go b/cli/cmd/version.go index c8ec5477..03bf9277 100644 --- a/cli/cmd/version.go +++ b/cli/cmd/version.go @@ -33,6 +33,6 @@ func AddVersionCmd(rootCmd *cobra.Command) { Long: `Print current version of OMS.`, }, } - rootCmd.AddCommand(version.cmd) + AddCmd(rootCmd, version.cmd) version.cmd.RunE = version.RunE } From b87a3df9a4ad5552e95d783068309bb38a157434 Mon Sep 17 00:00:00 2001 From: siherrmann <25087590+siherrmann@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:59:54 +0000 Subject: [PATCH 3/4] chore(docs): Auto-update docs and licenses Signed-off-by: siherrmann <25087590+siherrmann@users.noreply.github.com> --- NOTICE | 16 ++++++++-------- internal/tmpl/NOTICE | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/NOTICE b/NOTICE index e45dd309..5616c76e 100644 --- a/NOTICE +++ b/NOTICE @@ -353,15 +353,15 @@ License URL: https://github.com/go-gorp/gorp/blob/v3.1.0/LICENSE ---------- Module: github.com/go-jose/go-jose/v4 -Version: v4.1.3 +Version: v4.1.4 License: Apache-2.0 -License URL: https://github.com/go-jose/go-jose/blob/v4.1.3/LICENSE +License URL: https://github.com/go-jose/go-jose/blob/v4.1.4/LICENSE ---------- Module: github.com/go-jose/go-jose/v4/json -Version: v4.1.3 +Version: v4.1.4 License: BSD-3-Clause -License URL: https://github.com/go-jose/go-jose/blob/v4.1.3/json/LICENSE +License URL: https://github.com/go-jose/go-jose/blob/v4.1.4/json/LICENSE ---------- Module: github.com/go-logr/logr @@ -1169,15 +1169,15 @@ License URL: https://github.com/gomodules/jsonpatch/blob/v2.5.0/v2/LICENSE ---------- Module: google.golang.org/api -Version: v0.273.1 +Version: v0.274.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.273.1/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.274.0/LICENSE ---------- Module: google.golang.org/api/internal/third_party/uritemplates -Version: v0.273.1 +Version: v0.274.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.273.1/internal/third_party/uritemplates/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.274.0/internal/third_party/uritemplates/LICENSE ---------- Module: google.golang.org/genproto/googleapis diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index e45dd309..5616c76e 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -353,15 +353,15 @@ License URL: https://github.com/go-gorp/gorp/blob/v3.1.0/LICENSE ---------- Module: github.com/go-jose/go-jose/v4 -Version: v4.1.3 +Version: v4.1.4 License: Apache-2.0 -License URL: https://github.com/go-jose/go-jose/blob/v4.1.3/LICENSE +License URL: https://github.com/go-jose/go-jose/blob/v4.1.4/LICENSE ---------- Module: github.com/go-jose/go-jose/v4/json -Version: v4.1.3 +Version: v4.1.4 License: BSD-3-Clause -License URL: https://github.com/go-jose/go-jose/blob/v4.1.3/json/LICENSE +License URL: https://github.com/go-jose/go-jose/blob/v4.1.4/json/LICENSE ---------- Module: github.com/go-logr/logr @@ -1169,15 +1169,15 @@ License URL: https://github.com/gomodules/jsonpatch/blob/v2.5.0/v2/LICENSE ---------- Module: google.golang.org/api -Version: v0.273.1 +Version: v0.274.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.273.1/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.274.0/LICENSE ---------- Module: google.golang.org/api/internal/third_party/uritemplates -Version: v0.273.1 +Version: v0.274.0 License: BSD-3-Clause -License URL: https://github.com/googleapis/google-api-go-client/blob/v0.273.1/internal/third_party/uritemplates/LICENSE +License URL: https://github.com/googleapis/google-api-go-client/blob/v0.274.0/internal/third_party/uritemplates/LICENSE ---------- Module: google.golang.org/genproto/googleapis From 09197b14ad78cd93568f73dcda07285a3793c0f8 Mon Sep 17 00:00:00 2001 From: siherrmann <25087590+siherrmann@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:02:39 +0000 Subject: [PATCH 4/4] chore(docs): Auto-update docs and licenses Signed-off-by: siherrmann <25087590+siherrmann@users.noreply.github.com> --- NOTICE | 24 ++++++++++++------------ internal/tmpl/NOTICE | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/NOTICE b/NOTICE index fa54b995..10ac6109 100644 --- a/NOTICE +++ b/NOTICE @@ -23,9 +23,9 @@ License URL: https://github.com/googleapis/google-cloud-go/blob/auth/oauth2adapt ---------- Module: cloud.google.com/go/compute -Version: v1.58.0 +Version: v1.59.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.58.0/compute/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.59.0/compute/LICENSE ---------- Module: cloud.google.com/go/compute/metadata @@ -1115,9 +1115,9 @@ License URL: https://cs.opensource.google/go/x/crypto/+/v0.49.0:LICENSE ---------- Module: golang.org/x/mod/semver -Version: v0.34.0 +Version: v0.35.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/mod/+/v0.34.0:LICENSE +License URL: https://cs.opensource.google/go/x/mod/+/v0.35.0:LICENSE ---------- Module: golang.org/x/net @@ -1139,15 +1139,15 @@ License URL: https://cs.opensource.google/go/x/sync/+/v0.20.0:LICENSE ---------- Module: golang.org/x/sys -Version: v0.42.0 +Version: v0.43.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/sys/+/v0.42.0:LICENSE +License URL: https://cs.opensource.google/go/x/sys/+/v0.43.0:LICENSE ---------- Module: golang.org/x/term -Version: v0.41.0 +Version: v0.42.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/term/+/v0.41.0:LICENSE +License URL: https://cs.opensource.google/go/x/term/+/v0.42.0:LICENSE ---------- Module: golang.org/x/text @@ -1187,9 +1187,9 @@ License URL: https://github.com/googleapis/go-genproto/blob/d00831a3d3e7/LICENSE ---------- Module: google.golang.org/genproto/googleapis/api -Version: v0.0.0-20260401001100-f93e5f3e9f0f +Version: v0.0.0-20260401024825-9d38bb4040a9 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/f93e5f3e9f0f/googleapis/api/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/9d38bb4040a9/googleapis/api/LICENSE ---------- Module: google.golang.org/genproto/googleapis/rpc @@ -1235,9 +1235,9 @@ License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE ---------- Module: helm.sh/helm/v4 -Version: v4.1.3 +Version: v4.1.4 License: Apache-2.0 -License URL: https://github.com/helm/helm/blob/v4.1.3/LICENSE +License URL: https://github.com/helm/helm/blob/v4.1.4/LICENSE ---------- Module: k8s.io/api diff --git a/internal/tmpl/NOTICE b/internal/tmpl/NOTICE index fa54b995..10ac6109 100644 --- a/internal/tmpl/NOTICE +++ b/internal/tmpl/NOTICE @@ -23,9 +23,9 @@ License URL: https://github.com/googleapis/google-cloud-go/blob/auth/oauth2adapt ---------- Module: cloud.google.com/go/compute -Version: v1.58.0 +Version: v1.59.0 License: Apache-2.0 -License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.58.0/compute/LICENSE +License URL: https://github.com/googleapis/google-cloud-go/blob/compute/v1.59.0/compute/LICENSE ---------- Module: cloud.google.com/go/compute/metadata @@ -1115,9 +1115,9 @@ License URL: https://cs.opensource.google/go/x/crypto/+/v0.49.0:LICENSE ---------- Module: golang.org/x/mod/semver -Version: v0.34.0 +Version: v0.35.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/mod/+/v0.34.0:LICENSE +License URL: https://cs.opensource.google/go/x/mod/+/v0.35.0:LICENSE ---------- Module: golang.org/x/net @@ -1139,15 +1139,15 @@ License URL: https://cs.opensource.google/go/x/sync/+/v0.20.0:LICENSE ---------- Module: golang.org/x/sys -Version: v0.42.0 +Version: v0.43.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/sys/+/v0.42.0:LICENSE +License URL: https://cs.opensource.google/go/x/sys/+/v0.43.0:LICENSE ---------- Module: golang.org/x/term -Version: v0.41.0 +Version: v0.42.0 License: BSD-3-Clause -License URL: https://cs.opensource.google/go/x/term/+/v0.41.0:LICENSE +License URL: https://cs.opensource.google/go/x/term/+/v0.42.0:LICENSE ---------- Module: golang.org/x/text @@ -1187,9 +1187,9 @@ License URL: https://github.com/googleapis/go-genproto/blob/d00831a3d3e7/LICENSE ---------- Module: google.golang.org/genproto/googleapis/api -Version: v0.0.0-20260401001100-f93e5f3e9f0f +Version: v0.0.0-20260401024825-9d38bb4040a9 License: Apache-2.0 -License URL: https://github.com/googleapis/go-genproto/blob/f93e5f3e9f0f/googleapis/api/LICENSE +License URL: https://github.com/googleapis/go-genproto/blob/9d38bb4040a9/googleapis/api/LICENSE ---------- Module: google.golang.org/genproto/googleapis/rpc @@ -1235,9 +1235,9 @@ License URL: https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE ---------- Module: helm.sh/helm/v4 -Version: v4.1.3 +Version: v4.1.4 License: Apache-2.0 -License URL: https://github.com/helm/helm/blob/v4.1.3/LICENSE +License URL: https://github.com/helm/helm/blob/v4.1.4/LICENSE ---------- Module: k8s.io/api