Skip to content

Commit f92ce4d

Browse files
chore(helm/v2-alpha): add --input-dir flag for flexible project path resolution
Add --input-dir flag to the helm v2-alpha plugin to align with the alpha generate command pattern. This provides a consistent user experience across commands and makes the plugin more flexible. Changes: - Add --input-dir flag to specify the directory containing PROJECT file and manifests - Resolve manifests path relative to input-dir when not absolute - Add documentation and examples for the new flag Co-Authored-By: porridge@redhat.com Assisted-by: Claude
1 parent 1ad3260 commit f92ce4d

3 files changed

Lines changed: 229 additions & 45 deletions

File tree

docs/book/src/plugins/available/helm-v2-alpha.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ kubebuilder edit --plugins=helm/v2-alpha \
7474
--output-dir=helm-charts
7575
```
7676

77+
**Note**: The `--input-dir` flag sets the base directory for resolving relative paths and running make commands. The CLI currently loads the `PROJECT` file from the current working directory before the plugin runs, so you must still run `kubebuilder edit` from the project root.
78+
7779
## Chart Structure
7880

7981
The plugin creates a chart layout that matches your `config/`:
@@ -353,9 +355,10 @@ The Makefile targets use sensible defaults extracted from your project configura
353355

354356
| Flag | Description |
355357
|---------------------|-----------------------------------------------------------------------------|
356-
| **--manifests** | Path to YAML file containing Kubernetes manifests (default: `dist/install.yaml`) |
357-
| **--output-dir** string | Output directory for chart (default: `dist`) |
358-
| **--force** | Regenerates preserved files except `Chart.yaml` (`values.yaml`, `NOTES.txt`, `_helpers.tpl`, `.helmignore`, `test-chart.yml`) |
358+
| **--input-dir** | Base directory for resolving relative paths and running make commands. Defaults to current directory. Note: The `PROJECT` file is loaded from the current working directory by the CLI framework. |
359+
| **--manifests** | Path to YAML file containing Kubernetes manifests from kustomize output. Default: `dist/install.yaml`. If not absolute, resolved relative to `--input-dir`. |
360+
| **--output-dir** | Output directory for the generated Helm chart. Default: `dist`. If not absolute, resolved relative to `--input-dir`. |
361+
| **--force** | If true, regenerates all preserved files except `Chart.yaml` (includes `values.yaml`, `NOTES.txt`, `_helpers.tpl`, `.helmignore`, `test-chart.yml`). |
359362

360363
<aside class="note" role="note">
361364
<p class="note-title"> Examples </p>

pkg/plugins/optional/helm/v2alpha/edit.go

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"io"
2323
"log/slog"
2424
"os"
25+
"os/exec"
2526
"path/filepath"
2627
"strings"
2728

@@ -50,6 +51,7 @@ var _ plugin.EditSubcommand = &editSubcommand{}
5051
type editSubcommand struct {
5152
config config.Config
5253
force bool
54+
inputDir string
5355
manifestsFile string
5456
outputDir string
5557
}
@@ -103,8 +105,11 @@ The generated chart structure mirrors your config/ directory:
103105

104106
func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
105107
fs.BoolVar(&p.force, "force", false, "if true, regenerates all the files")
108+
fs.StringVar(&p.inputDir, "input-dir", "",
109+
"base directory for resolving relative paths and running make commands (defaults to current directory)")
106110
fs.StringVar(&p.manifestsFile, "manifests", DefaultManifestsFile,
107-
"path to the YAML file containing Kubernetes manifests from kustomize output")
111+
"path to the YAML file containing Kubernetes manifests from kustomize output "+
112+
"(resolved relative to input-dir if not absolute)")
108113
fs.StringVar(&p.outputDir, "output-dir", DefaultOutputDir, "output directory for the generated Helm chart")
109114
}
110115

@@ -114,14 +119,44 @@ func (p *editSubcommand) InjectConfig(c config.Config) error {
114119
}
115120

116121
func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
122+
// Set input directory to current working directory if not specified
123+
if p.inputDir == "" {
124+
cwd, err := os.Getwd()
125+
if err != nil {
126+
return fmt.Errorf("failed to get current working directory: %w", err)
127+
}
128+
p.inputDir = cwd
129+
}
130+
131+
// Ensure input directory is an absolute path
132+
if !filepath.IsAbs(p.inputDir) {
133+
absInputDir, err := filepath.Abs(p.inputDir)
134+
if err != nil {
135+
return fmt.Errorf("failed to resolve input directory %q: %w", p.inputDir, err)
136+
}
137+
p.inputDir = absInputDir
138+
}
139+
140+
// Resolve manifests path relative to input directory when not absolute
141+
manifestsPath := p.manifestsFile
142+
if !filepath.IsAbs(manifestsPath) {
143+
manifestsPath = filepath.Join(p.inputDir, manifestsPath)
144+
}
145+
146+
// Resolve output directory relative to input directory when not absolute
147+
outputDir := p.outputDir
148+
if !filepath.IsAbs(outputDir) {
149+
outputDir = filepath.Join(p.inputDir, outputDir)
150+
}
151+
117152
// If using default manifests file, ensure it exists by running make build-installer
118153
if p.manifestsFile == DefaultManifestsFile {
119-
if err := p.ensureManifestsExist(); err != nil {
120-
slog.Warn("Failed to generate default manifests file", "error", err, "file", p.manifestsFile)
154+
if err := p.ensureManifestsExist(manifestsPath); err != nil {
155+
slog.Warn("Failed to generate default manifests file", "error", err, "file", manifestsPath)
121156
}
122157
}
123158

124-
scaffolder := scaffolds.NewKustomizeHelmScaffolder(p.config, p.force, p.manifestsFile, p.outputDir)
159+
scaffolder := scaffolds.NewKustomizeHelmScaffolder(p.config, p.force, manifestsPath, outputDir)
125160
scaffolder.InjectFS(fs)
126161
err := scaffolder.Scaffold()
127162
if err != nil {
@@ -175,7 +210,7 @@ func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
175210
if isFirstRun {
176211
slog.Info("adding Helm deployment targets to Makefile...")
177212
// Extract namespace from manifests for accurate Makefile generation
178-
namespace := p.extractNamespaceFromManifests()
213+
namespace := p.extractNamespaceFromManifests(manifestsPath)
179214
if err := p.addHelmMakefileTargets(namespace); err != nil {
180215
slog.Warn("failed to add Helm targets to Makefile", "error", err)
181216
}
@@ -184,33 +219,52 @@ func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
184219
return nil
185220
}
186221

187-
// ensureManifestsExist runs make build-installer to generate the default manifests file
188-
func (p *editSubcommand) ensureManifestsExist() error {
189-
slog.Info("Generating default manifests file", "file", p.manifestsFile)
222+
// ensureManifestsExist runs make build-installer to generate the default manifests file.
223+
func (p *editSubcommand) ensureManifestsExist(manifestsPath string) error {
224+
slog.Info("Generating default manifests file", "file", manifestsPath)
190225

191-
// Run the required make targets to generate the manifests file
226+
// Run required make targets to generate the manifests file
192227
targets := []string{"manifests", "generate", "build-installer"}
193228
for _, target := range targets {
194-
if err := util.RunCmd(fmt.Sprintf("Running make %s", target), "make", target); err != nil {
229+
if err := p.runMakeTarget(target); err != nil {
195230
return fmt.Errorf("make %s failed: %w", target, err)
196231
}
197232
}
198233

199234
// Verify the file was created
200-
if _, err := os.Stat(p.manifestsFile); err != nil {
201-
return fmt.Errorf("manifests file %s was not created: %w", p.manifestsFile, err)
235+
if _, err := os.Stat(manifestsPath); err != nil {
236+
return fmt.Errorf("manifests file %s was not created: %w", manifestsPath, err)
202237
}
203238

204-
slog.Info("Successfully generated manifests file", "file", p.manifestsFile)
239+
slog.Info("Successfully generated manifests file", "file", manifestsPath)
205240
return nil
206241
}
207242

208-
// PostScaffold automatically uncomments cert-manager installation when webhooks are present
243+
// runMakeTarget executes a make target in the input directory.
244+
func (p *editSubcommand) runMakeTarget(target string) error {
245+
cmd := exec.Command("make", target)
246+
cmd.Dir = p.inputDir
247+
cmd.Stdout = os.Stdout
248+
cmd.Stderr = os.Stderr
249+
slog.Info(fmt.Sprintf("Running make %s", target), "dir", p.inputDir)
250+
if err := cmd.Run(); err != nil {
251+
return fmt.Errorf("error running make %s: %w", target, err)
252+
}
253+
return nil
254+
}
255+
256+
// PostScaffold automatically uncomments cert-manager installation when webhooks are present.
209257
func (p *editSubcommand) PostScaffold() error {
210258
hasWebhooks := hasWebhooksWith(p.config)
211259

212260
if hasWebhooks {
213-
workflowFile := filepath.Join(".github", "workflows", "test-chart.yml")
261+
// The scaffolder creates the workflow file relative to CWD (via machinery.Filesystem),
262+
// not relative to inputDir, so we look for it in CWD
263+
baseDir, err := os.Getwd()
264+
if err != nil {
265+
return fmt.Errorf("failed to get current directory: %w", err)
266+
}
267+
workflowFile := filepath.Join(baseDir, ".github", "workflows", "test-chart.yml")
214268
if _, err := os.Stat(workflowFile); err != nil {
215269
slog.Info(
216270
"Workflow file not found, unable to uncomment cert-manager installation",
@@ -243,14 +297,14 @@ func (p *editSubcommand) PostScaffold() error {
243297
return nil
244298
}
245299

246-
// addHelmMakefileTargets appends Helm deployment targets to the Makefile if they don't already exist
300+
// addHelmMakefileTargets appends Helm deployment targets to the Makefile if they don't already exist.
247301
func (p *editSubcommand) addHelmMakefileTargets(namespace string) error {
248-
makefilePath := "Makefile"
302+
makefilePath := filepath.Join(p.inputDir, "Makefile")
249303
if _, err := os.Stat(makefilePath); os.IsNotExist(err) {
250-
return fmt.Errorf("makefile not found")
304+
return fmt.Errorf("makefile not found at %q", makefilePath)
251305
}
252306

253-
// Get the Helm Makefile targets
307+
// Use original outputDir value (not absolute path) for Makefile variables
254308
helmTargets := getHelmMakefileTargets(p.config.GetProjectName(), namespace, p.outputDir)
255309

256310
// Append the targets if they don't already exist
@@ -265,17 +319,17 @@ func (p *editSubcommand) addHelmMakefileTargets(namespace string) error {
265319

266320
// extractNamespaceFromManifests parses the manifests file to extract the manager namespace.
267321
// Returns projectName-system if manifests don't exist or namespace not found.
268-
func (p *editSubcommand) extractNamespaceFromManifests() string {
322+
func (p *editSubcommand) extractNamespaceFromManifests(manifestsPath string) string {
269323
// Default to project-name-system pattern
270324
defaultNamespace := p.config.GetProjectName() + "-system"
271325

272326
// If manifests file doesn't exist, use default
273-
if _, err := os.Stat(p.manifestsFile); os.IsNotExist(err) {
327+
if _, err := os.Stat(manifestsPath); os.IsNotExist(err) {
274328
return defaultNamespace
275329
}
276330

277331
// Parse the manifests to get the namespace
278-
file, err := os.Open(p.manifestsFile)
332+
file, err := os.Open(manifestsPath)
279333
if err != nil {
280334
return defaultNamespace
281335
}
@@ -312,8 +366,8 @@ func (p *editSubcommand) extractNamespaceFromManifests() string {
312366
return defaultNamespace
313367
}
314368

315-
// getHelmMakefileTargets returns the Helm Makefile targets as a string
316-
// following the same patterns as the existing Makefile deployment section
369+
// getHelmMakefileTargets returns the Helm Makefile targets as a string.
370+
// It follows the same patterns as the existing Makefile deployment section.
317371
func getHelmMakefileTargets(projectName, namespace, outputDir string) string {
318372
if outputDir == "" {
319373
outputDir = "dist"
@@ -325,8 +379,8 @@ func getHelmMakefileTargets(projectName, namespace, outputDir string) string {
325379
return helmMakefileTemplate(namespace, release, outputDir)
326380
}
327381

328-
// helmMakefileTemplate returns the Helm deployment section template
329-
// This follows the same pattern as the Kustomize deployment section in the Go plugin
382+
// helmMakefileTemplate returns the Helm deployment section template.
383+
// It follows the same pattern as the Kustomize deployment section in the Go plugin.
330384
const helmMakefileTemplateFormat = `
331385
##@ Helm Deployment
332386

0 commit comments

Comments
 (0)