Skip to content

Commit 6ea1b11

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 6ea1b11

3 files changed

Lines changed: 282 additions & 44 deletions

File tree

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ kubebuilder edit --plugins=helm/v2-alpha --output-dir=charts
7272
kubebuilder edit --plugins=helm/v2-alpha \
7373
--manifests=manifests/install.yaml \
7474
--output-dir=helm-charts
75+
76+
# Work with a project in a different directory
77+
# All relative paths (manifests, output-dir) are resolved relative to input-dir
78+
kubebuilder edit --plugins=helm/v2-alpha --input-dir=./path/to/project
79+
80+
# Use custom manifests and output paths (both resolved relative to input-dir)
81+
kubebuilder edit --plugins=helm/v2-alpha \
82+
--input-dir=./path/to/project \
83+
--manifests=custom/install.yaml \
84+
--output-dir=helm-charts
7585
```
7686

7787
## Chart Structure
@@ -353,9 +363,10 @@ The Makefile targets use sensible defaults extracted from your project configura
353363

354364
| Flag | Description |
355365
|---------------------|-----------------------------------------------------------------------------|
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`) |
366+
| **--input-dir** | Path to the project directory containing the PROJECT file. Defaults to current directory. All relative paths are resolved relative to this directory. |
367+
| **--manifests** | Path to YAML file containing Kubernetes manifests from kustomize output. Default: `dist/install.yaml`. If not absolute, resolved relative to `--input-dir`. |
368+
| **--output-dir** | Output directory for the generated Helm chart. Default: `dist`. If not absolute, resolved relative to `--input-dir`. |
369+
| **--force** | If true, regenerates all preserved files except `Chart.yaml` (includes `values.yaml`, `NOTES.txt`, `_helpers.tpl`, `.helmignore`, `test-chart.yml`). |
359370

360371
<aside class="note" role="note">
361372
<p class="note-title"> Examples </p>

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

Lines changed: 89 additions & 25 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
}
@@ -76,6 +78,12 @@ distribution of your project. When enabled, adds Helm helpers targets to Makefil
7678
# Generate from custom manifests to custom output directory
7779
%[1]s edit --plugins=%[2]s --manifests=manifests/install.yaml --output-dir=helm-charts
7880
81+
# Generate chart from a different project directory
82+
%[1]s edit --plugins=%[2]s --input-dir=./path/to/project
83+
84+
# Use custom manifests file (resolved relative to input-dir)
85+
%[1]s edit --plugins=%[2]s --input-dir=./path/to/project --manifests=custom/install.yaml
86+
7987
# Typical workflow:
8088
make build-installer # Generate dist/install.yaml with latest changes
8189
%[1]s edit --plugins=%[2]s # Generate/update Helm chart in dist/chart/
@@ -103,8 +111,11 @@ The generated chart structure mirrors your config/ directory:
103111

104112
func (p *editSubcommand) BindFlags(fs *pflag.FlagSet) {
105113
fs.BoolVar(&p.force, "force", false, "if true, regenerates all the files")
114+
fs.StringVar(&p.inputDir, "input-dir", "",
115+
"path to the directory containing the PROJECT file and manifests (defaults to current directory)")
106116
fs.StringVar(&p.manifestsFile, "manifests", DefaultManifestsFile,
107-
"path to the YAML file containing Kubernetes manifests from kustomize output")
117+
"path to the YAML file containing Kubernetes manifests from kustomize output "+
118+
"(resolved relative to input-dir if not absolute)")
108119
fs.StringVar(&p.outputDir, "output-dir", DefaultOutputDir, "output directory for the generated Helm chart")
109120
}
110121

@@ -114,14 +125,44 @@ func (p *editSubcommand) InjectConfig(c config.Config) error {
114125
}
115126

116127
func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
128+
// Set input directory to current working directory if not specified
129+
if p.inputDir == "" {
130+
cwd, err := os.Getwd()
131+
if err != nil {
132+
return fmt.Errorf("failed to get current working directory: %w", err)
133+
}
134+
p.inputDir = cwd
135+
}
136+
137+
// Ensure input directory is an absolute path
138+
if !filepath.IsAbs(p.inputDir) {
139+
absInputDir, err := filepath.Abs(p.inputDir)
140+
if err != nil {
141+
return fmt.Errorf("failed to resolve input directory %q: %w", p.inputDir, err)
142+
}
143+
p.inputDir = absInputDir
144+
}
145+
146+
// Resolve manifests path relative to input directory when not absolute
147+
manifestsPath := p.manifestsFile
148+
if !filepath.IsAbs(manifestsPath) {
149+
manifestsPath = filepath.Join(p.inputDir, manifestsPath)
150+
}
151+
152+
// Resolve output directory relative to input directory when not absolute
153+
outputDir := p.outputDir
154+
if !filepath.IsAbs(outputDir) {
155+
outputDir = filepath.Join(p.inputDir, outputDir)
156+
}
157+
117158
// If using default manifests file, ensure it exists by running make build-installer
118159
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)
160+
if err := p.ensureManifestsExist(manifestsPath); err != nil {
161+
slog.Warn("Failed to generate default manifests file", "error", err, "file", manifestsPath)
121162
}
122163
}
123164

124-
scaffolder := scaffolds.NewKustomizeHelmScaffolder(p.config, p.force, p.manifestsFile, p.outputDir)
165+
scaffolder := scaffolds.NewKustomizeHelmScaffolder(p.config, p.force, manifestsPath, outputDir)
125166
scaffolder.InjectFS(fs)
126167
err := scaffolder.Scaffold()
127168
if err != nil {
@@ -175,7 +216,7 @@ func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
175216
if isFirstRun {
176217
slog.Info("adding Helm deployment targets to Makefile...")
177218
// Extract namespace from manifests for accurate Makefile generation
178-
namespace := p.extractNamespaceFromManifests()
219+
namespace := p.extractNamespaceFromManifests(manifestsPath)
179220
if err := p.addHelmMakefileTargets(namespace); err != nil {
180221
slog.Warn("failed to add Helm targets to Makefile", "error", err)
181222
}
@@ -184,24 +225,37 @@ func (p *editSubcommand) Scaffold(fs machinery.Filesystem) error {
184225
return nil
185226
}
186227

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)
228+
// ensureManifestsExist runs make build-installer to generate the default manifests file.
229+
func (p *editSubcommand) ensureManifestsExist(manifestsPath string) error {
230+
slog.Info("Generating default manifests file", "file", manifestsPath)
190231

191-
// Run the required make targets to generate the manifests file
232+
// Run required make targets to generate the manifests file
192233
targets := []string{"manifests", "generate", "build-installer"}
193234
for _, target := range targets {
194-
if err := util.RunCmd(fmt.Sprintf("Running make %s", target), "make", target); err != nil {
235+
if err := p.runMakeTarget(target); err != nil {
195236
return fmt.Errorf("make %s failed: %w", target, err)
196237
}
197238
}
198239

199240
// 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)
241+
if _, err := os.Stat(manifestsPath); err != nil {
242+
return fmt.Errorf("manifests file %s was not created: %w", manifestsPath, err)
202243
}
203244

204-
slog.Info("Successfully generated manifests file", "file", p.manifestsFile)
245+
slog.Info("Successfully generated manifests file", "file", manifestsPath)
246+
return nil
247+
}
248+
249+
// runMakeTarget executes a make target in the input directory.
250+
func (p *editSubcommand) runMakeTarget(target string) error {
251+
cmd := exec.Command("make", target)
252+
cmd.Dir = p.inputDir
253+
cmd.Stdout = os.Stdout
254+
cmd.Stderr = os.Stderr
255+
slog.Info(fmt.Sprintf("Running make %s", target), "dir", p.inputDir)
256+
if err := cmd.Run(); err != nil {
257+
return fmt.Errorf("error running make %s: %w", target, err)
258+
}
205259
return nil
206260
}
207261

@@ -210,7 +264,17 @@ func (p *editSubcommand) PostScaffold() error {
210264
hasWebhooks := hasWebhooksWith(p.config)
211265

212266
if hasWebhooks {
213-
workflowFile := filepath.Join(".github", "workflows", "test-chart.yml")
267+
// Use input directory as base, or current directory if not set
268+
// (inputDir may not be set when PostScaffold is called directly in tests)
269+
baseDir := p.inputDir
270+
if baseDir == "" {
271+
var err error
272+
baseDir, err = os.Getwd()
273+
if err != nil {
274+
return fmt.Errorf("failed to get current directory: %w", err)
275+
}
276+
}
277+
workflowFile := filepath.Join(baseDir, ".github", "workflows", "test-chart.yml")
214278
if _, err := os.Stat(workflowFile); err != nil {
215279
slog.Info(
216280
"Workflow file not found, unable to uncomment cert-manager installation",
@@ -243,14 +307,14 @@ func (p *editSubcommand) PostScaffold() error {
243307
return nil
244308
}
245309

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

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

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

266330
// extractNamespaceFromManifests parses the manifests file to extract the manager namespace.
267331
// Returns projectName-system if manifests don't exist or namespace not found.
268-
func (p *editSubcommand) extractNamespaceFromManifests() string {
332+
func (p *editSubcommand) extractNamespaceFromManifests(manifestsPath string) string {
269333
// Default to project-name-system pattern
270334
defaultNamespace := p.config.GetProjectName() + "-system"
271335

272336
// If manifests file doesn't exist, use default
273-
if _, err := os.Stat(p.manifestsFile); os.IsNotExist(err) {
337+
if _, err := os.Stat(manifestsPath); os.IsNotExist(err) {
274338
return defaultNamespace
275339
}
276340

277341
// Parse the manifests to get the namespace
278-
file, err := os.Open(p.manifestsFile)
342+
file, err := os.Open(manifestsPath)
279343
if err != nil {
280344
return defaultNamespace
281345
}
@@ -312,8 +376,8 @@ func (p *editSubcommand) extractNamespaceFromManifests() string {
312376
return defaultNamespace
313377
}
314378

315-
// getHelmMakefileTargets returns the Helm Makefile targets as a string
316-
// following the same patterns as the existing Makefile deployment section
379+
// getHelmMakefileTargets returns the Helm Makefile targets as a string.
380+
// It follows the same patterns as the existing Makefile deployment section.
317381
func getHelmMakefileTargets(projectName, namespace, outputDir string) string {
318382
if outputDir == "" {
319383
outputDir = "dist"
@@ -325,8 +389,8 @@ func getHelmMakefileTargets(projectName, namespace, outputDir string) string {
325389
return helmMakefileTemplate(namespace, release, outputDir)
326390
}
327391

328-
// helmMakefileTemplate returns the Helm deployment section template
329-
// This follows the same pattern as the Kustomize deployment section in the Go plugin
392+
// helmMakefileTemplate returns the Helm deployment section template.
393+
// It follows the same pattern as the Kustomize deployment section in the Go plugin.
330394
const helmMakefileTemplateFormat = `
331395
##@ Helm Deployment
332396

0 commit comments

Comments
 (0)