@@ -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{}
5051type 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
104106func (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
116121func (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.
209257func (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.
247301func (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.
317371func 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.
330384const helmMakefileTemplateFormat = `
331385##@ Helm Deployment
332386
0 commit comments