Skip to content

Commit 45144d4

Browse files
chore(helm/v2-aplha): Add --input-dir flag to helm/v2-alpha plugin to allow operating on projects in different directories
Generated-by: Claude
1 parent 8c56039 commit 45144d4

5 files changed

Lines changed: 394 additions & 18 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,8 @@ The Makefile targets use sensible defaults extracted from your project configura
690690
| Flag | Description |
691691
|---------------------|-----------------------------------------------------------------------------|
692692
| **--manifests** | Path to YAML file containing Kubernetes manifests (default: `dist/install.yaml`) |
693-
| **--output-dir** string | Output directory for chart (default: `dist`) |
693+
| **--output-dir** | Output directory for chart (default: `dist`) |
694+
| **--input-dir** | Path to directory containing PROJECT file. All operations will use files from this directory (default: current working directory) |
694695
| **--force** | Regenerates preserved files except `Chart.yaml` (`values.yaml`, `NOTES.txt`, `_helpers.tpl`, `.helmignore`, `test-chart.yml`) |
695696

696697
<aside class="note" role="note">

pkg/cli/cli.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
log "log/slog"
2323
"os"
24+
"path/filepath"
2425
"strings"
2526

2627
"github.com/spf13/afero"
@@ -99,6 +100,12 @@ func New(options ...Option) (*CLI, error) {
99100
return nil, err
100101
}
101102

103+
// Handle --input-dir flag before building the command tree.
104+
// This must happen before getInfoFromConfigFile() tries to load the PROJECT file.
105+
if err := handleInputDirBeforeConfigLoad(); err != nil {
106+
return nil, err
107+
}
108+
102109
// Build the cmd tree.
103110
if err := c.buildCmd(); err != nil {
104111
c.cmd.RunE = errCmdFunc(err)
@@ -319,6 +326,129 @@ func patchProjectFileInMemoryIfNeeded(fs afero.Fs, path string) error {
319326
return nil
320327
}
321328

329+
// parseInputDirFromArgs extracts --input-dir from raw CLI args without depending on
330+
// Cobra flag registration. It supports both:
331+
//
332+
// --input-dir=/path/to/project
333+
// --input-dir /path/to/project
334+
func parseInputDirFromArgs(args []string) (string, bool) {
335+
for i := range args {
336+
arg := args[i]
337+
338+
if arg == "--input-dir" {
339+
if i+1 >= len(args) {
340+
// Flag needs an argument but none provided
341+
return "", false
342+
}
343+
if strings.HasPrefix(args[i+1], "-") {
344+
// Next token is another flag, so treat this as missing a value
345+
return "", false
346+
}
347+
return args[i+1], true
348+
}
349+
350+
if after, ok := strings.CutPrefix(arg, "--input-dir="); ok {
351+
return after, true
352+
}
353+
}
354+
355+
return "", false
356+
}
357+
358+
// consumesNextArgForCommandDetection reports whether a pre-command flag takes its
359+
// value as the next CLI token, so that token should not be treated as a subcommand.
360+
func consumesNextArgForCommandDetection(arg string) bool {
361+
switch arg {
362+
case "--" + pluginsFlag, "--" + projectVersionFlag:
363+
return true
364+
default:
365+
return false
366+
}
367+
}
368+
369+
// isEditCommand checks if the command invocation is `kubebuilder edit`
370+
// by scanning os.Args for the first positional argument, skipping flags and
371+
// flag values for known pre-command flags.
372+
func isEditCommand(args []string) bool {
373+
for i := 0; i < len(args); i++ {
374+
arg := args[i]
375+
376+
if arg == "--" {
377+
if i+1 < len(args) {
378+
return args[i+1] == "edit"
379+
}
380+
return false
381+
}
382+
383+
if strings.HasPrefix(arg, "-") {
384+
// Flags using --flag=value consume only the current token.
385+
if strings.Contains(arg, "=") {
386+
continue
387+
}
388+
389+
// Skip the next token for known flags that take a separate value.
390+
if consumesNextArgForCommandDetection(arg) && i+1 < len(args) {
391+
i++
392+
}
393+
continue
394+
}
395+
396+
// First positional argument is the subcommand.
397+
return arg == "edit"
398+
}
399+
return false
400+
}
401+
402+
// handleInputDirBeforeConfigLoad checks if --input-dir flag is set for edit command
403+
// and changes directory if needed. This must run BEFORE the framework loads PROJECT file.
404+
func handleInputDirBeforeConfigLoad() error {
405+
// Only process --input-dir for edit command
406+
if !isEditCommand(os.Args[1:]) {
407+
return nil
408+
}
409+
410+
inputDir, found := parseInputDirFromArgs(os.Args[1:])
411+
if !found || inputDir == "" {
412+
return nil
413+
}
414+
415+
// Resolve to absolute path
416+
if !filepath.IsAbs(inputDir) {
417+
absPath, err := filepath.Abs(inputDir)
418+
if err != nil {
419+
return fmt.Errorf("failed to resolve input-dir %q: %w", inputDir, err)
420+
}
421+
inputDir = absPath
422+
}
423+
424+
// Validate directory exists
425+
if info, err := os.Stat(inputDir); err != nil {
426+
if os.IsNotExist(err) {
427+
return fmt.Errorf("input-dir does not exist: %q", inputDir)
428+
}
429+
return fmt.Errorf("failed to access input-dir %q: %w", inputDir, err)
430+
} else if !info.IsDir() {
431+
return fmt.Errorf("input-dir is not a directory: %q", inputDir)
432+
}
433+
434+
// Validate PROJECT file exists in input-dir
435+
projectFilePath := filepath.Join(inputDir, yamlstore.DefaultPath)
436+
if _, err := os.Stat(projectFilePath); err != nil {
437+
if os.IsNotExist(err) {
438+
return fmt.Errorf("input-dir does not contain %q: %q", yamlstore.DefaultPath, inputDir)
439+
}
440+
return fmt.Errorf("failed to access project file %q: %w", projectFilePath, err)
441+
}
442+
443+
// Change working directory
444+
if err := os.Chdir(inputDir); err != nil {
445+
return fmt.Errorf("failed to change to input-dir %q: %w", inputDir, err)
446+
}
447+
448+
log.Info("Operating in input directory", "path", inputDir)
449+
return nil
450+
}
451+
322452
// getInfoFromConfig obtains the project version and plugin keys from the project config.
323453
// It is extracted from getInfoFromConfigFile for testing purposes.
324454
func (c *CLI) getInfoFromConfig(projectConfig config.Config) error {

0 commit comments

Comments
 (0)