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

kubernetes-sigs / kubebuilder / 25489743986

07 May 2026 10:14AM UTC coverage: 82.317% (+0.004%) from 82.313%
25489743986

Pull #5676

github

dongjiang1989
update goconst lint

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

6 of 7 new or added lines in 5 files covered. (85.71%)

84 existing lines in 5 files now uncovered.

7709 of 9365 relevant lines covered (82.32%)

73.74 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
197
                //nolint:goconst
UNCOV
198
                if excludeDefaultScaffold {
×
199
                        if pluginKey == "go.kubebuilder.io/v4" ||
×
200
                                pluginKey == "kustomize.common.kubebuilder.io/v2" {
×
201
                                continue
×
202
                        }
203
                }
204

UNCOV
205
                shortKey := getShortKey(pluginKey)
×
206

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

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

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

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

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

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

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

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