From 35ffe38892970682387b47682d6fa372da94e0f5 Mon Sep 17 00:00:00 2001 From: Chandana Mallikarjuna Date: Sun, 8 Mar 2026 22:46:41 +0530 Subject: [PATCH] issue-5446-cli-help-enhancements Update init.go --- internal/cli/alpha/generate.go | 34 ++++++++------ internal/cli/alpha/update.go | 35 +++++++------- pkg/cli/root.go | 2 +- pkg/plugins/golang/v4/api.go | 24 +++++----- pkg/plugins/golang/v4/edit.go | 80 +++++++++++++++++++------------- pkg/plugins/golang/v4/init.go | 28 ++++++----- pkg/plugins/golang/v4/webhook.go | 33 ++++++------- 7 files changed, 133 insertions(+), 103 deletions(-) diff --git a/internal/cli/alpha/generate.go b/internal/cli/alpha/generate.go index 024e344dcfe..4a20aaaf11b 100644 --- a/internal/cli/alpha/generate.go +++ b/internal/cli/alpha/generate.go @@ -43,20 +43,27 @@ func NewScaffoldCommand() *cobra.Command { scaffoldCmd := &cobra.Command{ Use: "generate", Short: "Re-scaffold a Kubebuilder project from its PROJECT file", - Long: `The 'generate' command re-creates a Kubebuilder project scaffold based on the configuration -defined in the PROJECT file, using the latest installed Kubebuilder version and plugins. + Long: `Re-generate a Kubebuilder project scaffold based on the PROJECT file configuration. -This is helpful for migrating projects to a newer Kubebuilder layout or plugin version (e.g., v3 to v4) -as update your project from any previous version to the current one. +This command uses the latest installed Kubebuilder version and plugins to regenerate +the entire project structure. -If no output directory is provided, the current working directory will be cleaned (except .git and PROJECT).`, +This is helpful for migrating projects to newer Kubebuilder +layouts or plugin version (e.g., v3 to v4) + +This command deletes all files except .git/ and PROJECT before regenerating in-place when +--output-dir is not specified. + +The PROJECT file must exist and contain valid plugin configuration.`, Example: ` - # **WARNING**(will delete all files to allow the re-scaffold except .git and PROJECT) - # Re-scaffold the project in-place + # Re-scaffold in-place (WARNING: deletes all files except .git and PROJECT) kubebuilder alpha generate - # Re-scaffold the project from ./test into ./my-output - kubebuilder alpha generate --input-dir="./path/to/project" --output-dir="./my-output" + # Re-scaffold to a different directory (safe, preserves original) + kubebuilder alpha generate --output-dir="./regenerated" + + # Re-scaffold from a specific project directory to a different directory + kubebuilder alpha generate --input-dir="./my-project" --output-dir="./my-project-v4" `, PreRunE: func(_ *cobra.Command, _ []string) error { return opts.Validate() @@ -70,13 +77,12 @@ If no output directory is provided, the current working directory will be cleane } scaffoldCmd.Flags().StringVar(&opts.InputDir, "input-dir", "", - "Path to the directory containing the PROJECT file. "+ - "Defaults to the current working directory. WARNING: delete existing files (except .git and PROJECT).") + "path to directory containing the PROJECT file (default: current directory). "+ + "WARNING: if --output-dir is not set, all files except .git/ and PROJECT will be deleted") scaffoldCmd.Flags().StringVar(&opts.OutputDir, "output-dir", "", - "Directory where the new project scaffold will be written. "+ - "If unset, re-scaffolding occurs in-place "+ - "and will delete existing files (except .git and PROJECT).") + "path to directory where regenerated scaffold will be written. "+ + "If not set, regenerates in-place and deletes existing files (except .git/ and PROJECT)") return scaffoldCmd } diff --git a/internal/cli/alpha/update.go b/internal/cli/alpha/update.go index 299b9abce48..b750c61fea7 100644 --- a/internal/cli/alpha/update.go +++ b/internal/cli/alpha/update.go @@ -145,38 +145,39 @@ Defaults: } updateCmd.Flags().StringVar(&opts.FromVersion, "from-version", "", - "binary release version to upgrade from. Should match the version used to init the project and be "+ - "a valid release version, e.g., v4.6.0. If not set, it defaults to the version specified in the PROJECT file.") + "Kubebuilder version to upgrade from (e.g., v4.6.0). Should match version used to initialize project "+ + "If not set, uses version from PROJECT file") updateCmd.Flags().StringVar(&opts.ToVersion, "to-version", "", - "binary release version to upgrade to. Should be a valid release version, e.g., v4.7.0. "+ - "If not set, it defaults to the latest release version available in the project repository.") + "Kubebuilder version to upgrade to (e.g., v4.7.0). "+ + "If not set, uses latest version available in the project repository") updateCmd.Flags().StringVar(&opts.FromBranch, "from-branch", "", - "Git branch to use as current state of the project for the update.") + "Git branch containing current project state (default: main)") updateCmd.Flags().BoolVar(&opts.Force, "force", false, - "Force the update even if conflicts occur. Conflicted files will include conflict markers, and a "+ - "commit will be created automatically. Ideal for automation (e.g., cronjobs, CI).") + "if true, commit even with conflicts (adds conflict markers). "+ + "Ideal for automation (CI/CD pipelines, cronjobs)") updateCmd.Flags().BoolVar(&opts.ShowCommits, "show-commits", false, - "If set, the update will keep the full history instead of squashing into a single commit.") + "if true, keep full commit history instead of squashing. "+ + "Cannot be used with --restore-path") updateCmd.Flags().StringArrayVar(&opts.RestorePath, "restore-path", nil, - "Paths to preserve from the base branch (repeatable). Not supported with --show-commits.") + "paths to preserve from base branch (repeatable, e.g., --restore-path .github/workflows). "+ + "Cannot be used with --show-commits") updateCmd.Flags().StringVar(&opts.OutputBranch, "output-branch", "", "Override the default output branch name (default: kubebuilder-update-from--to-).") updateCmd.Flags().BoolVar(&opts.Push, "push", false, - "Push the output branch to the remote repository after the update.") + "if true, push output branch to origin after update") updateCmd.Flags().StringVar(&opts.CommitMessage, "merge-message", "", - "Custom commit message for successful merges (no conflicts). "+ - "Defaults to 'chore(kubebuilder): update scaffold -> '.") + "custom commit message for clean merges (no conflicts)"+ + "(default: 'chore(kubebuilder): update scaffold -> ')") updateCmd.Flags().StringVar(&opts.CommitMessageConflict, "conflict-message", "", - "Custom commit message for merges with conflicts. "+ - "Defaults to 'chore(kubebuilder): (:warning: manual conflict resolution required) update scaffold -> '.") + "custom commit message for merges with conflicts. "+ + "(default: 'chore(kubebuilder): (:warning: manual conflict resolution required) update scaffold -> ')") updateCmd.Flags().BoolVar(&opts.OpenGhIssue, "open-gh-issue", false, - "Create a GitHub issue with a pre-filled checklist and compare link after the update completes (requires `gh`).") + "if true, create GitHub issue with a pre-filled checklist and compare link (requires gh CLI)") updateCmd.Flags().BoolVar( &opts.UseGhModels, "use-gh-models", false, - "Generate and post an AI summary comment to the GitHub Issue using `gh models run`. "+ - "Requires --open-gh-issue and GitHub CLI (`gh`) with the `gh-models` extension.") + "if true, add AI-generated summary comment to GitHub issue (requires --open-gh-issue and gh CLI with gh-models extension)") updateCmd.Flags().StringArrayVar( &gitCfg, "git-config", diff --git a/pkg/cli/root.go b/pkg/cli/root.go index b1fa1c77bc5..7f473f9dc02 100644 --- a/pkg/cli/root.go +++ b/pkg/cli/root.go @@ -105,7 +105,7 @@ func (c CLI) newRootCmd() *cobra.Command { cmd.PersistentFlags().StringSlice(pluginsFlag, nil, "plugin keys to be used for this subcommand execution") // Register --project-version on the root command so that it shows up in help. - cmd.Flags().String(projectVersionFlag, c.defaultProjectVersion.String(), "project version") + cmd.Flags().String(projectVersionFlag, c.defaultProjectVersion.String(), "project version to scaffold (default: latest stable)") // As the root command will be used to shot the help message under some error conditions, // like during plugin resolving, we need to allow unknown flags to prevent parsing errors. diff --git a/pkg/plugins/golang/v4/api.go b/pkg/plugins/golang/v4/api.go index 3d49fb10966..2b58cfe0dea 100644 --- a/pkg/plugins/golang/v4/api.go +++ b/pkg/plugins/golang/v4/api.go @@ -70,7 +70,6 @@ make generate will be run. %[1]s create api --group ship --version v1beta1 --kind Frigate # Edit the API Scheme - nano api/v1beta1/frigate_types.go # Edit the Controller @@ -91,34 +90,35 @@ make generate will be run. } func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { - fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after generating files") + fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after scaffolding (default: true)") fs.BoolVar(&p.force, "force", false, - "attempt to create resource even if it already exists") + "if true, attempt to create resource even if it already exists") p.options = &goPlugin.Options{} - fs.StringVar(&p.options.Plural, "plural", "", "resource irregular plural form") + fs.StringVar(&p.options.Plural, "plural", "", "specify irregular plural form of the resource (e.g., 'mice' for 'Mouse')") fs.BoolVar(&p.options.DoAPI, "resource", true, - "if set, generate the resource without prompting the user") + "if true, scaffold the API resource types without prompting (default: true)") p.resourceFlag = fs.Lookup("resource") - fs.BoolVar(&p.options.Namespaced, "namespaced", true, "resource is namespaced") + fs.BoolVar(&p.options.Namespaced, "namespaced", true, "if true, resource is namespace-scoped rather than cluster-scoped (default: true)") fs.BoolVar(&p.options.DoController, "controller", true, - "if set, generate the controller without prompting the user") + "if true, scaffold the controller without prompting (default: true)") p.controllerFlag = fs.Lookup("controller") fs.StringVar(&p.options.ExternalAPIPath, "external-api-path", "", - "Specify the Go package import path for the external API. This is used to scaffold controllers for resources "+ - "defined outside this project (e.g., github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1).") + "specify Go package import path for external API (e.g., github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1). "+ + "Used to scaffold controllers for external types with --resource=false") fs.StringVar(&p.options.ExternalAPIDomain, "external-api-domain", "", - "Specify the domain name for the external API. This domain is used to generate accurate RBAC "+ - "markers and permissions for the external resources (e.g., cert-manager.io).") + "specify domain for external API (e.g., cert-manager.io). "+ + "Used to generate accurate RBAC markers for external resources. Requires --external-api-path") fs.StringVar(&p.options.ExternalAPIModule, "external-api-module", "", - "external API module with optional version (e.g., github.com/cert-manager/cert-manager@v1.18.2)") + "external API Go module with optional version (e.g., github.com/cert-manager/cert-manager@v1.18.2). "+ + "Requires --external-api-path") } func (p *createAPISubcommand) InjectConfig(c config.Config) error { diff --git a/pkg/plugins/golang/v4/edit.go b/pkg/plugins/golang/v4/edit.go index 6ebe6348a5a..a1ef3b066c4 100644 --- a/pkg/plugins/golang/v4/edit.go +++ b/pkg/plugins/golang/v4/edit.go @@ -43,31 +43,40 @@ type editSubcommand struct { func (p *editSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) { subcmdMeta.Description = `Edit project configuration to enable or disable layout settings. -Multigroup (--multigroup): - Enable or disable multi-group layout. - Changes API structure: api// becomes api/// - Automatic: Updates PROJECT file, future APIs use new structure - Manual: Move existing API files, update import paths in controllers - More info: https://book.kubebuilder.io/migration/multi-group.html - -Namespaced (--namespaced): - Enable or disable namespace-scoped deployment. - Manager watches one or more specific namespaces vs all namespaces. - Namespaces to watch are configured via WATCH_NAMESPACE environment variable. - Automatic: Updates PROJECT file, scaffolds Role/RoleBinding, uses --force to regenerate manager.yaml - Manual: Add namespace= to RBAC markers in existing controllers, update cmd/main.go, run 'make manifests' - More info: https://book.kubebuilder.io/migration/namespace-scoped.html - - WARNING - Webhooks and Namespace-Scoped Mode: - Webhooks remain cluster-scoped even in namespace-scoped mode. - The manager cache is restricted to WATCH_NAMESPACE, but webhooks receive requests - from ALL namespaces. You must configure namespaceSelector or objectSelector to align - webhook scope with the cache. - -Force (--force): - Overwrite existing scaffolded files to apply configuration changes. - Example: With --namespaced, regenerates config/manager/manager.yaml to add WATCH_NAMESPACE env var. - Warning: This overwrites default scaffold files; manual changes in those files may be lost. +This command modifies the PROJECT file and optionally regenerates scaffolded files. +Use this to change layout settings or add optional plugins after project initialization. + + +Plugin flags: + --plugins: Comma-separated list of plugins to add to the project + Plugins are saved to the PROJECT file and used in future operations + Run 'kubebuilder edit --plugins --help' to see available plugins + +Layout flags: + --multigroup: Enable/Disable multigroup layout to organize APIs by group + Scaffolds APIs in api/// instead of api// + Useful when managing multiple API groups (e.g., batch, apps, crew) + Automatic: Updates PROJECT file; future APIs use new structure + Manual: Move existing API files, update import paths in controllers + More info: https://book.kubebuilder.io/migration/multi-group.html + + --namespaced: Enable/Disable namespace-scoped deployment instead of cluster-scoped + Manager watches one or more specific namespaces instead of all namespaces + Namespaces to watch are configured via WATCH_NAMESPACE environment variable + Uses Role/RoleBinding instead of ClusterRole/ClusterRoleBinding + Automatic: Updates PROJECT file, scaffolds Role/RoleBinding + Manual: Add namespace= to RBAC markers in controllers, update cmd/main.go, run 'make manifests' + More info: https://book.kubebuilder.io/migration/namespace-scoped.html + + WARNING - Webhooks and Namespace-Scoped Mode: + Webhooks remain cluster-scoped even in namespace-scoped mode. + The manager cache is restricted to WATCH_NAMESPACE, but webhooks receive requests + from ALL namespaces. Configure namespaceSelector or objectSelector to align + webhook scope with the cache. + + --force: Overwrite existing scaffolded files to apply configuration changes + Example: With --namespaced, regenerates config/manager/manager.yaml to add WATCH_NAMESPACE + Warning: Overwrites default scaffold files; manual changes may be lost Note: To add optional plugins after initialization, use 'kubebuilder edit --plugins '. Run 'kubebuilder edit --plugins --help' to see available plugins. @@ -75,25 +84,34 @@ Note: To add optional plugins after initialization, use 'kubebuilder edit --plug subcmdMeta.Examples = fmt.Sprintf(` # Enable multigroup layout %[1]s edit --multigroup - # Enable namespace-scoped permissions + # Enable namespace-scoped deployment %[1]s edit --namespaced - # Enable with automatic file regeneration + # Enable namespace-scoped with automatic file regeneration %[1]s edit --namespaced --force # Disable multigroup layout %[1]s edit --multigroup=false - # Enable/disable multiple settings + # Enable/Disable multiple settings at once %[1]s edit --multigroup --namespaced --force + + # Add Helm plugin to existing project + %[1]s edit --plugins helm/v2-alpha + + # Add multiple plugins + %[1]s edit --plugins grafana/v1-alpha,autoupdate/v1-alpha `, cliMeta.CommandName) } func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) { p.fs = fs - fs.BoolVar(&p.multigroup, "multigroup", false, "enable or disable multigroup layout") - fs.BoolVar(&p.namespaced, "namespaced", false, "enable or disable namespace-scoped deployment") - fs.BoolVar(&p.force, "force", false, "overwrite scaffolded files to apply changes (manual edits may be lost)") + fs.BoolVar(&p.multigroup, "multigroup", false, + "enable or disable multigroup layout (api///)") + fs.BoolVar(&p.namespaced, "namespaced", false, + "enable or disable namespace-scoped deployment (default: cluster-scoped)") + fs.BoolVar(&p.force, "force", false, + "overwrite scaffolded files to apply configuration changes (manual edits may be lost)") } func (p *editSubcommand) InjectConfig(c config.Config) error { diff --git a/pkg/plugins/golang/v4/init.go b/pkg/plugins/golang/v4/init.go index e0d24d432f7..549c74af094 100644 --- a/pkg/plugins/golang/v4/init.go +++ b/pkg/plugins/golang/v4/init.go @@ -64,12 +64,16 @@ type initSubcommand struct { func (p *initSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) { p.commandName = cliMeta.CommandName - subcmdMeta.Description = `Initialize a new project including the following files: - - a "go.mod" with project dependencies - - a "PROJECT" file that stores project configuration - - a "Makefile" with several useful make targets for the project - - several YAML files for project deployment under the "config" directory - - a "cmd/main.go" file that creates the manager that will run the project controllers + subcmdMeta.Description = `Initialize a new project within the current directory. Following files will be generated automatically: + - go.mod: Go module with project dependencies + - PROJECT: file that stores project configuration + - Makefile: provides useful make targets for the project + - config/: Kubernetes manifests for deployment + - cmd/main.go: controller manager entry point + - Dockerfile: build controller manager container image + - test/: unit tests for the project + - hack/: contains licensing boilerplate. + Required flags: --domain: Domain for your APIs (e.g., example.org creates crew.example.org for API groups) @@ -77,7 +81,7 @@ Required flags: Configuration flags: --repo: Go module path (e.g., github.com/user/repo); auto-detected if not provided --owner: Owner name for copyright license headers - --license: License to use (apache2 or none, default: apache2) + --license: License to use (apache2 | none; default "apache2") Plugin flags: --plugins: Comma-separated list of plugins to use (default: go/v4) @@ -120,21 +124,21 @@ Note: Layout settings can be changed later with 'kubebuilder edit'. func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) { fs.BoolVar(&p.skipGoVersionCheck, "skip-go-version-check", - false, "skip Go version check") + false, "if true, skip compatibility check of Kubebuilder plugin supported version vs installed Go version (default: false)") // dependency args - fs.BoolVar(&p.fetchDeps, "fetch-deps", true, "download dependencies after scaffolding") + fs.BoolVar(&p.fetchDeps, "fetch-deps", true, "if true, download Go dependencies after scaffolding (default: true)") // boilerplate args fs.StringVar(&p.license, "license", "apache2", - "license header to use (apache2 or none)") - fs.StringVar(&p.owner, "owner", "", "copyright owner for license headers") + "license to use for boilerplate headers (apache2 or none)") + fs.StringVar(&p.owner, "owner", "", "owner name for copyright license headers") // project args fs.StringVar(&p.repo, "repo", "", "Go module name (e.g., github.com/user/repo); "+ "auto-detected from current directory if not provided") fs.BoolVar(&p.multigroup, "multigroup", false, - "enable multigroup layout (organize APIs by group)") + "enable multigroup layout to organize APIs by group (api///)") fs.BoolVar(&p.namespaced, "namespaced", false, "enable namespace-scoped deployment (default: cluster-scoped)") } diff --git a/pkg/plugins/golang/v4/webhook.go b/pkg/plugins/golang/v4/webhook.go index 8e21e6ff3b5..f113eef79c2 100644 --- a/pkg/plugins/golang/v4/webhook.go +++ b/pkg/plugins/golang/v4/webhook.go @@ -89,45 +89,46 @@ validating and/or conversion webhooks. func (p *createWebhookSubcommand) BindFlags(fs *pflag.FlagSet) { p.options = &goPlugin.Options{} - fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after generating files") + fs.BoolVar(&p.runMake, "make", true, "if true, run `make generate` after scaffolding (default: true)") - fs.StringVar(&p.options.Plural, "plural", "", "resource irregular plural form") + fs.StringVar(&p.options.Plural, "plural", "", "specify irregular plural form of the resource (e.g., 'mice' for 'Mouse')") fs.BoolVar(&p.options.DoDefaulting, "defaulting", false, - "if set, scaffold the defaulting webhook") + "if true, scaffold defaulting webhook (mutates resources on create/update)") fs.BoolVar(&p.options.DoValidation, "programmatic-validation", false, - "if set, scaffold the validating webhook") + "if true, scaffold validating webhook (validates resources on create/update)") fs.BoolVar(&p.options.DoConversion, "conversion", false, - "if set, scaffold the conversion webhook") + "if true, scaffold conversion webhook (converts between API versions)") fs.StringSliceVar(&p.options.Spoke, "spoke", nil, - "Comma-separated list of spoke versions to be added to the conversion webhook (e.g., --spoke v1,v2)") + "comma-separated list of spoke versions for conversion webhook (e.g., --spoke v1,v2). Requires --conversion") fs.StringVar(&p.options.DefaultingPath, "defaulting-path", "", - "Custom path for the defaulting/mutating webhook (only valid with --defaulting)") + "Custom path for the defaulting/mutating webhook. Requires --defaulting") fs.StringVar(&p.options.ValidationPath, "validation-path", "", - "Custom path for the validation webhook (only valid with --programmatic-validation)") + "Custom path for the validation webhook. Requires --programmatic-validation") // TODO: remove for go/v5 fs.BoolVar(&p.isLegacyPath, "legacy", false, - "[DEPRECATED] Attempts to create resource under the API directory (legacy path). "+ - "This option will be removed in future versions.") + "[DEPRECATED] if true, scaffold webhook under api/ directory (legacy layout). "+ + "This flag will be removed in go/v5") fs.StringVar(&p.options.ExternalAPIPath, "external-api-path", "", - "Specify the Go package import path for the external API. This is used to scaffold webhooks for resources "+ - "defined outside this project (e.g., github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1).") + "specify Go package import path for external API (e.g., github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1). "+ + "Used to scaffold webhooks for external types") fs.StringVar(&p.options.ExternalAPIDomain, "external-api-domain", "", - "Specify the domain name for the external API. This domain is used to generate accurate RBAC "+ - "markers and permissions for the external resources (e.g., cert-manager.io).") + "specify domain for external API (e.g., cert-manager.io). "+ + "Used to generate accurate RBAC markers for external resources. Requires --external-api-path") fs.StringVar(&p.options.ExternalAPIModule, "external-api-module", "", - "external API module with optional version (e.g., github.com/cert-manager/cert-manager@v1.18.2)") + "external API Go module with optional version (e.g., github.com/cert-manager/cert-manager@v1.18.2). "+ + "Requires --external-api-path") fs.BoolVar(&p.force, "force", false, - "attempt to create resource even if it already exists") + "if true, attempt to create webhook even if it already exists") } func (p *createWebhookSubcommand) InjectConfig(c config.Config) error {