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

kubernetes-sigs / kubebuilder / 25602897403

09 May 2026 01:58PM UTC coverage: 82.322% (+0.009%) from 82.313%
25602897403

Pull #5676

github

dongjiang1989
remove //nolint:gocost

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

10 of 14 new or added lines in 6 files covered. (71.43%)

72 existing lines in 4 files now uncovered.

7707 of 9362 relevant lines covered (82.32%)

73.69 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

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

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

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

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

×
64
        if version == "" {
×
65
                return shortName
×
66
        }
×
67
        return shortName + "/" + version
×
68
}
69

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

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

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

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

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

26✔
117
        return cmd
26✔
118
}
119

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

26✔
124
    %[1]s init --domain <YOUR_DOMAIN>
26✔
125

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

26✔
128
    %[1]s init --plugins=<PLUGIN_KEYS>
26✔
129

26✔
130
Available plugins:
26✔
131

26✔
132
%[2]s
26✔
133

26✔
134
To see which plugins support a specific command:
26✔
135

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

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

146
        return str
26✔
147
}
148

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

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

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

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

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

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

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

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

196
                // For subcommands, skip default scaffold and its component plugins
UNCOV
197
                if excludeDefaultScaffold {
×
UNCOV
198
                        if pluginKey == "go.kubebuilder.io/v4" ||
×
199
                                pluginKey == "kustomize.common.kubebuilder.io/v2" {
×
NEW
200
                                continue
×
201
                        }
202
                }
203

UNCOV
204
                shortKey := getShortKey(pluginKey)
×
UNCOV
205

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

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

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

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

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

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

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

256
        return strings.Join(lines, "\n")
×
257
}
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