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

kubernetes-sigs / kubebuilder / 21708741144

05 Feb 2026 10:54AM UTC coverage: 73.91% (-0.009%) from 73.919%
21708741144

push

github

web-flow
Merge pull request #5436 from camilamacedo86/fix-project-file-helm-v2

🐛 (helm/v2-alpha): remove deprecated v1-alpha plugin entry from PROJECT file

11 of 15 new or added lines in 1 file covered. (73.33%)

6711 of 9080 relevant lines covered (73.91%)

43.86 hits per line

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

46.15
/pkg/plugins/optional/helm/v2alpha/edit.go
1
/*
2
Copyright 2025 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 v2alpha
18

19
import (
20
        "errors"
21
        "fmt"
22
        "log/slog"
23
        "os"
24
        "path/filepath"
25

26
        "github.com/spf13/pflag"
27

28
        "sigs.k8s.io/kubebuilder/v4/pkg/config"
29
        cfgv3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
30
        "sigs.k8s.io/kubebuilder/v4/pkg/machinery"
31
        "sigs.k8s.io/kubebuilder/v4/pkg/plugin"
32
        "sigs.k8s.io/kubebuilder/v4/pkg/plugin/util"
33
        "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/scaffolds"
34
)
35

36
const (
37
        // DefaultManifestsFile is the default path for kustomize output manifests
38
        DefaultManifestsFile = "dist/install.yaml"
39
        // DefaultOutputDir is the default output directory for Helm charts
40
        DefaultOutputDir = "dist"
41
        // v1AlphaPluginKey is the deprecated v1-alpha plugin key
42
        v1AlphaPluginKey = "helm.kubebuilder.io/v1-alpha"
43
)
44

45
var _ plugin.EditSubcommand = &editSubcommand{}
46

47
type editSubcommand struct {
48
        config        config.Config
49
        force         bool
50
        manifestsFile string
51
        outputDir     string
52
}
53

54
//nolint:lll
55
func (p *editSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
1✔
56
        subcmdMeta.Description = `Generate a Helm chart from your project's kustomize output.
1✔
57

1✔
58
This plugin dynamically generates Helm chart templates by parsing the output of 'make build-installer' 
1✔
59
(dist/install.yaml by default). The generated chart preserves all customizations made to your kustomize 
1✔
60
configuration including environment variables, labels, and annotations.
1✔
61

1✔
62
The chart structure mirrors your config/ directory organization for easy maintenance.`
1✔
63

1✔
64
        subcmdMeta.Examples = fmt.Sprintf(`# Generate Helm chart from default manifests (dist/install.yaml) to default output (dist/)
1✔
65
  %[1]s edit --plugins=%[2]s
1✔
66

1✔
67
# Generate Helm chart and overwrite existing files (useful for updates)
1✔
68
  %[1]s edit --plugins=%[2]s --force
1✔
69

1✔
70
# Generate Helm chart from a custom manifests file
1✔
71
  %[1]s edit --plugins=%[2]s --manifests=path/to/custom-install.yaml
1✔
72

1✔
73
# Generate Helm chart to a custom output directory
1✔
74
  %[1]s edit --plugins=%[2]s --output-dir=charts
1✔
75

1✔
76
# Generate from custom manifests to custom output directory
1✔
77
  %[1]s edit --plugins=%[2]s --manifests=manifests/install.yaml --output-dir=helm-charts
1✔
78

1✔
79
# Typical workflow:
1✔
80
  make build-installer  # Generate dist/install.yaml with latest changes
1✔
81
  %[1]s edit --plugins=%[2]s  # Generate/update Helm chart in dist/chart/
1✔
82

1✔
83
**NOTE**: Chart.yaml is never overwritten (contains user-managed version info).
1✔
84
Without --force, the plugin also preserves values.yaml, NOTES.txt, _helpers.tpl, .helmignore, and
1✔
85
.github/workflows/test-chart.yml. All template files in templates/ are always regenerated
1✔
86
to match your current kustomize output. Use --force to regenerate all files except Chart.yaml.
1✔
87

1✔
88
The generated chart structure mirrors your config/ directory:
1✔
89
<output>/chart/
1✔
90
├── Chart.yaml
1✔
91
├── values.yaml
1✔
92
├── .helmignore
1✔
93
└── templates/
1✔
94
    ├── NOTES.txt
1✔
95
    ├── _helpers.tpl
1✔
96
    ├── rbac/
1✔
97
    ├── manager/
1✔
98
    ├── webhook/
1✔
99
    └── ...
1✔
100
`, cliMeta.CommandName, plugin.KeyFor(Plugin{}))
1✔
101
}
1✔
102

103
func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
1✔
104
        fs.BoolVar(&p.force, "force", false, "if true, regenerates all the files")
1✔
105
        fs.StringVar(&p.manifestsFile, "manifests", DefaultManifestsFile,
1✔
106
                "path to the YAML file containing Kubernetes manifests from kustomize output")
1✔
107
        fs.StringVar(&p.outputDir, "output-dir", DefaultOutputDir, "output directory for the generated Helm chart")
1✔
108
}
1✔
109

110
func (p *editSubcommand) InjectConfig(c config.Config) error {
1✔
111
        p.config = c
1✔
112
        return nil
1✔
113
}
1✔
114

115
func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
×
116
        // If using default manifests file, ensure it exists by running make build-installer
×
117
        if p.manifestsFile == DefaultManifestsFile {
×
118
                if err := p.ensureManifestsExist(); err != nil {
×
119
                        slog.Warn("Failed to generate default manifests file", "error", err, "file", p.manifestsFile)
×
120
                }
×
121
        }
122

123
        scaffolder := scaffolds.NewKustomizeHelmScaffolder(p.config, p.force, p.manifestsFile, p.outputDir)
×
124
        scaffolder.InjectFS(fs)
×
125
        err := scaffolder.Scaffold()
×
126
        if err != nil {
×
127
                return fmt.Errorf("error scaffolding Helm chart: %w", err)
×
128
        }
×
129

130
        // Remove deprecated v1-alpha plugin entry from PROJECT file
131
        // This must happen in Scaffold (before config is saved) to be persisted
NEW
132
        p.removeV1AlphaPluginEntry()
×
NEW
133

×
134
        // Save plugin config to PROJECT file
×
135
        key := plugin.GetPluginKeyForConfig(p.config.GetPluginChain(), Plugin{})
×
136
        canonicalKey := plugin.KeyFor(Plugin{})
×
137
        cfg := pluginConfig{}
×
138
        if err = p.config.DecodePluginConfig(key, &cfg); err != nil {
×
139
                switch {
×
140
                case errors.As(err, &config.UnsupportedFieldError{}):
×
141
                        // Config version doesn't support plugin metadata
×
142
                        return nil
×
143
                case errors.As(err, &config.PluginKeyNotFoundError{}):
×
144
                        if key != canonicalKey {
×
145
                                if err2 := p.config.DecodePluginConfig(canonicalKey, &cfg); err2 != nil {
×
146
                                        if errors.As(err2, &config.UnsupportedFieldError{}) {
×
147
                                                return nil
×
148
                                        }
×
149
                                        if !errors.As(err2, &config.PluginKeyNotFoundError{}) {
×
150
                                                return fmt.Errorf("error decoding plugin configuration: %w", err2)
×
151
                                        }
×
152
                                }
153
                        }
154
                default:
×
155
                        return fmt.Errorf("error decoding plugin configuration: %w", err)
×
156
                }
157
        }
158

159
        // Update configuration with current parameters
160
        cfg.ManifestsFile = p.manifestsFile
×
161
        cfg.OutputDir = p.outputDir
×
162

×
163
        if err = p.config.EncodePluginConfig(key, cfg); err != nil {
×
164
                return fmt.Errorf("error encoding plugin configuration: %w", err)
×
165
        }
×
166

167
        return nil
×
168
}
169

170
// ensureManifestsExist runs make build-installer to generate the default manifests file
171
func (p *editSubcommand) ensureManifestsExist() error {
×
172
        slog.Info("Generating default manifests file", "file", p.manifestsFile)
×
173

×
174
        // Run the required make targets to generate the manifests file
×
175
        targets := []string{"manifests", "generate", "build-installer"}
×
176
        for _, target := range targets {
×
177
                if err := util.RunCmd(fmt.Sprintf("Running make %s", target), "make", target); err != nil {
×
178
                        return fmt.Errorf("make %s failed: %w", target, err)
×
179
                }
×
180
        }
181

182
        // Verify the file was created
183
        if _, err := os.Stat(p.manifestsFile); err != nil {
×
184
                return fmt.Errorf("manifests file %s was not created: %w", p.manifestsFile, err)
×
185
        }
×
186

187
        slog.Info("Successfully generated manifests file", "file", p.manifestsFile)
×
188
        return nil
×
189
}
190

191
// PostScaffold automatically uncomments cert-manager installation when webhooks are present
192
func (p *editSubcommand) PostScaffold() error {
2✔
193
        hasWebhooks := hasWebhooksWith(p.config)
2✔
194

2✔
195
        if hasWebhooks {
2✔
196
                workflowFile := filepath.Join(".github", "workflows", "test-chart.yml")
×
197
                if _, err := os.Stat(workflowFile); err != nil {
×
198
                        slog.Info(
×
199
                                "Workflow file not found, unable to uncomment cert-manager installation",
×
200
                                "error", err,
×
201
                                "file", workflowFile,
×
202
                        )
×
203
                        return nil
×
204
                }
×
205
                target := `
×
206
#      - name: Install cert-manager via Helm (wait for readiness)
×
207
#        run: |
×
208
#          helm repo add jetstack https://charts.jetstack.io
×
209
#          helm repo update
×
210
#          helm install cert-manager jetstack/cert-manager \
×
211
#            --namespace cert-manager \
×
212
#            --create-namespace \
×
213
#            --set crds.enabled=true \
×
214
#            --wait \
×
215
#            --timeout 300s`
×
216
                if err := util.UncommentCode(workflowFile, target, "#"); err != nil {
×
217
                        hasUncommented, errCheck := util.HasFileContentWith(workflowFile, "- name: Install cert-manager via Helm")
×
218
                        if !hasUncommented || errCheck != nil {
×
219
                                slog.Warn("Failed to uncomment cert-manager installation in workflow file", "error", err, "file", workflowFile)
×
220
                        }
×
221
                } else {
×
222
                        target = `# TODO: Uncomment if cert-manager is enabled`
×
223
                        _ = util.ReplaceInFile(workflowFile, target, "")
×
224
                }
×
225
        }
226
        return nil
2✔
227
}
228

229
func hasWebhooksWith(c config.Config) bool {
3✔
230
        resources, err := c.GetResources()
3✔
231
        if err != nil {
3✔
232
                return false
×
233
        }
×
234

235
        for _, res := range resources {
3✔
236
                if res.HasDefaultingWebhook() || res.HasValidationWebhook() || res.HasConversionWebhook() {
×
237
                        return true
×
238
                }
×
239
        }
240

241
        return false
3✔
242
}
243

244
// removeV1AlphaPluginEntry removes the deprecated helm.kubebuilder.io/v1-alpha plugin entry.
245
// This must be called from Scaffold (before config is saved) for changes to be persisted.
246
func (p *editSubcommand) removeV1AlphaPluginEntry() {
4✔
247
        // Only attempt to remove if using v3 config (which supports plugin configs)
4✔
248
        cfg, ok := p.config.(*cfgv3.Cfg)
4✔
249
        if !ok {
4✔
NEW
250
                return
×
NEW
251
        }
×
252

253
        // Check if v1-alpha plugin entry exists
254
        if cfg.Plugins == nil {
6✔
255
                return
2✔
256
        }
2✔
257

258
        if _, exists := cfg.Plugins[v1AlphaPluginKey]; exists {
4✔
259
                delete(cfg.Plugins, v1AlphaPluginKey)
2✔
260
                slog.Info("removed deprecated v1-alpha plugin entry")
2✔
261
        }
2✔
262
}
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