@@ -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}
@@ -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
104112func (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
116127func (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.
247311func (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.
317381func 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.
330394const helmMakefileTemplateFormat = `
331395##@ Helm Deployment
332396
0 commit comments