• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / kubebuilder / 25603346581

09 May 2026 02:20PM UTC coverage: 82.316% (+0.003%) from 82.313%
25603346581

Pull #5676

github

dongjiang1989
fix golangci lint check

Signed-off-by: dongjiang <dongjiang1989@126.com>
Pull Request #5676: ✨ (go/v4): upgrade golangci-lint to v2.12.1

8 of 13 new or added lines in 6 files covered. (61.54%)

1 existing line in 1 file now uncovered.

7704 of 9359 relevant lines covered (82.32%)

73.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.5
/pkg/cli/root.go
1
/*
2
Copyright 2022 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package cli
18

19
import (
20
        "errors"
21
        "fmt"
22
        "slices"
23
        "strings"
24

25
        "github.com/spf13/cobra"
26

27
        "sigs.k8s.io/kubebuilder/v4/pkg/plugin"
28
)
29

30
var (
31
        supportedPlatforms = []string{"darwin", "linux"}
32
        // errHelpDisplayed is returned when help is displayed to prevent command execution
33
        errHelpDisplayed = errors.New("help displayed")
34

35
        kustomizeCommonPluginV2 = "kustomize.common.kubebuilder.io/v2"
36
)
37

38
// isHelpFlag checks if the given string is a help flag
39
func isHelpFlag(s string) bool {
28✔
40
        return s == "--help" || s == "-h" || s == "help"
28✔
41
}
28✔
42

43
// getShortKey converts a full plugin key to a short display key
44
// Example: "deploy-image.go.kubebuilder.io/v1-alpha" -> "deploy-image/v1-alpha"
45
func getShortKey(fullKey string) string {
×
46
        name, version := plugin.SplitKey(fullKey)
×
47

×
48
        // Extract the short name (part before .kubebuilder.io or other domain)
×
49
        shortName := name
×
50
        if strings.Contains(name, ".kubebuilder.io") {
×
51
                shortName = strings.TrimSuffix(name, ".kubebuilder.io")
×
52
        } else if idx := strings.LastIndex(name, "."); idx > 0 {
×
53
                // For external plugins, try to get a reasonable short name
×
54
                // Keep the part before the last dot if it looks like a domain
×
55
                parts := strings.Split(name, ".")
×
56
                if len(parts) > 2 {
×
57
                        shortName = strings.Join(parts[:len(parts)-1], ".")
×
58
                }
×
59
        }
60

61
        // Strip common suffixes for cleaner display
62
        // e.g., "deploy-image.go" -> "deploy-image", "kustomize.common" -> "kustomize"
63
        shortName = strings.TrimSuffix(shortName, ".go")
×
64
        shortName = strings.TrimSuffix(shortName, ".common")
×
65

×
66
        if version == "" {
×
67
                return shortName
×
68
        }
×
69
        return shortName + "/" + version
×
70
}
71

72
// getPluginDescription returns a short description for a plugin key
73
// This is a fallback for plugins that don't implement Describable interface
74
func getPluginDescription(_ string) string {
×
75
        // Fallback for external plugins that don't provide descriptions
×
76
        return "External or custom plugin"
×
77
}
×
78

79
func (c CLI) newRootCmd() *cobra.Command {
26✔
80
        cmd := &cobra.Command{
26✔
81
                Use:     c.commandName,
26✔
82
                Long:    c.description,
26✔
83
                Example: c.rootExamples(),
26✔
84
                RunE: func(cmd *cobra.Command, _ []string) error {
26✔
85
                        return cmd.Help()
×
86
                },
×
87
                PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
2✔
88
                        // Check if --plugins flag contains help flags (--help, -h, help)
2✔
89
                        // This handles cases like: kubebuilder init --plugins --help
2✔
90
                        if pluginKeys, err := cmd.Flags().GetStringSlice(pluginsFlag); err == nil {
4✔
91
                                for _, key := range pluginKeys {
4✔
92
                                        key = strings.TrimSpace(key)
2✔
93
                                        if isHelpFlag(key) {
2✔
94
                                                // Help was requested, show help and stop execution
×
95
                                                cmd.SilenceUsage = true
×
96
                                                cmd.SilenceErrors = true
×
97
                                                _ = cmd.Help()
×
98
                                                return errHelpDisplayed
×
99
                                        }
×
100
                                }
101
                        }
102
                        return nil
2✔
103
                },
104
        }
105

106
        // Global flags for all subcommands.
107
        cmd.PersistentFlags().StringSlice(pluginsFlag, nil,
26✔
108
                "Comma-separated list of plugin keys to use (e.g., go/v4, helm/v2-alpha). "+
26✔
109
                        "Defaults to the built-in go/v4 bundle if unset")
26✔
110

26✔
111
        // Register --project-version on the root command so that it shows up in help.
26✔
112
        cmd.Flags().String(projectVersionFlag, c.defaultProjectVersion.String(),
26✔
113
                "Project version (e.g., 3). Defaults to CLI version if unset")
26✔
114

26✔
115
        // As the root command will be used to shot the help message under some error conditions,
26✔
116
        // like during plugin resolving, we need to allow unknown flags to prevent parsing errors.
26✔
117
        cmd.FParseErrWhitelist = cobra.FParseErrWhitelist{UnknownFlags: true}
26✔
118

26✔
119
        return cmd
26✔
120
}
121

122
// rootExamples builds the examples string for the root command before resolving plugins
123
func (c CLI) rootExamples() string {
26✔
124
        str := fmt.Sprintf(`Get started by initializing a new project:
26✔
125

26✔
126
    %[1]s init --domain <YOUR_DOMAIN>
26✔
127

26✔
128
The default plugin scaffold includes everything you need. To use optional plugins:
26✔
129

26✔
130
    %[1]s init --plugins=<PLUGIN_KEYS>
26✔
131

26✔
132
Available plugins:
26✔
133

26✔
134
%[2]s
26✔
135

26✔
136
To see which plugins support a specific command:
26✔
137

26✔
138
    %[1]s <init|edit|create> --help
26✔
139
`,
26✔
140
                c.commandName, c.getPluginTable())
26✔
141

26✔
142
        if len(c.defaultPlugins) != 0 {
33✔
143
                if defaultPlugins, found := c.defaultPlugins[c.defaultProjectVersion]; found {
8✔
144
                        str += fmt.Sprintf("\nDefault plugin: %q\n", strings.Join(defaultPlugins, ","))
1✔
145
                }
1✔
146
        }
147

148
        return str
26✔
149
}
150

151
// getPluginTable returns an ASCII table of the available plugins and their supported project versions.
152
func (c CLI) getPluginTable() string {
26✔
153
        return c.getPluginTableFiltered(nil)
26✔
154
}
26✔
155

156
// getPluginTableFilteredForSubcommand returns a filtered list of plugins for subcommands,
157
// excluding the default scaffold bundle and its component plugins.
158
func (c CLI) getPluginTableFilteredForSubcommand(filter func(plugin.Plugin) bool) string {
34✔
159
        return c.getPluginTableFilteredWithOptions(filter, true)
34✔
160
}
34✔
161

162
// getPluginTableFiltered returns a formatted list of plugins filtered by a predicate.
163
// If filter is nil, all plugins are included.
164
// Deprecated plugins are automatically excluded from help output.
165
func (c CLI) getPluginTableFiltered(filter func(plugin.Plugin) bool) string {
26✔
166
        return c.getPluginTableFilteredWithOptions(filter, false)
26✔
167
}
26✔
168

169
// getPluginTableFilteredWithOptions returns a formatted list of plugins with filtering options.
170
func (c CLI) getPluginTableFilteredWithOptions(filter func(plugin.Plugin) bool, excludeDefaultScaffold bool) string {
60✔
171
        type pluginInfo struct {
60✔
172
                shortKey    string
60✔
173
                fullKey     string
60✔
174
                description string
60✔
175
                versions    string
60✔
176
        }
60✔
177

60✔
178
        plugins := make([]pluginInfo, 0, len(c.plugins))
60✔
179

60✔
180
        for pluginKey, p := range c.plugins {
98✔
181
                // Skip deprecated plugins in help output
38✔
182
                if deprecated, ok := p.(plugin.Deprecated); ok {
76✔
183
                        if deprecated.DeprecationWarning() != "" {
40✔
184
                                continue
2✔
185
                        }
186
                }
187

188
                // Apply filter if provided
189
                if filter != nil && !filter(p) {
36✔
190
                        continue
×
191
                }
192

193
                // Skip base.go plugin to avoid duplication with go plugin
194
                if strings.Contains(pluginKey, "base.go.kubebuilder.io") {
72✔
195
                        continue
36✔
196
                }
197

198
                // For subcommands, skip default scaffold and its component plugins
199
                if excludeDefaultScaffold {
×
NEW
200
                        if pluginKey == goPluginV4 ||
×
NEW
201
                                pluginKey == kustomizeCommonPluginV2 {
×
UNCOV
202
                                continue
×
203
                        }
204
                }
205

206
                shortKey := getShortKey(pluginKey)
×
207

×
208
                // Get description from plugin if it implements Describable, otherwise use fallback
×
209
                var desc string
×
210
                if describable, ok := p.(plugin.Describable); ok {
×
211
                        desc = describable.Description()
×
212
                } else {
×
213
                        desc = getPluginDescription(pluginKey)
×
214
                }
×
215

216
                // Get supported project versions
217
                supportedVersions := p.SupportedProjectVersions()
×
218
                versionStrs := make([]string, 0, len(supportedVersions))
×
219
                for _, ver := range supportedVersions {
×
220
                        versionStrs = append(versionStrs, ver.String())
×
221
                }
×
222
                versionsStr := strings.Join(versionStrs, ", ")
×
223

×
224
                plugins = append(plugins, pluginInfo{
×
225
                        shortKey:    shortKey,
×
226
                        fullKey:     pluginKey,
×
227
                        description: desc,
×
228
                        versions:    versionsStr,
×
229
                })
×
230
        }
231

232
        if len(plugins) == 0 {
120✔
233
                return "No plugins available for this subcommand"
60✔
234
        }
60✔
235

236
        // Sort by short key for better readability
237
        slices.SortFunc(plugins, func(a, b pluginInfo) int {
×
238
                return strings.Compare(a.shortKey, b.shortKey)
×
239
        })
×
240

241
        // Calculate max width for KEY column
242
        maxKeyWidth := len("KEY")
×
243
        for _, p := range plugins {
×
244
                if len(p.shortKey) > maxKeyWidth {
×
245
                        maxKeyWidth = len(p.shortKey)
×
246
                }
×
247
        }
248

249
        // Build aligned column output
250
        lines := make([]string, 0, len(plugins)+1)
×
251
        // Header
×
252
        lines = append(lines, fmt.Sprintf("  %-*s  %s", maxKeyWidth, "KEY", "DESCRIPTION"))
×
253
        // Entries
×
254
        for _, p := range plugins {
×
255
                lines = append(lines, fmt.Sprintf("  %-*s  %s", maxKeyWidth, p.shortKey, p.description))
×
256
        }
×
257

258
        return strings.Join(lines, "\n")
×
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc