Merge branch 'main' into copilot/fix-array-merging-issue

This commit is contained in:
Zubair Haque 2026-01-08 14:29:09 -05:00 committed by GitHub
commit cd855b5d14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 30 additions and 5 deletions

View File

@ -144,6 +144,7 @@ The name of a release can be used as a label: "--selector name=myrelease"`)
fs.BoolVar(&globalOptions.EnableLiveOutput, "enable-live-output", globalOptions.EnableLiveOutput, `Show live output from the Helm binary Stdout/Stderr into Helmfile own Stdout/Stderr.
It only applies for the Helm CLI commands, Stdout/Stderr for Hooks are still displayed only when it's execution finishes.`)
fs.BoolVarP(&globalOptions.Interactive, "interactive", "i", false, "Request confirmation before attempting to modify clusters")
fs.BoolVar(&globalOptions.SequentialHelmfiles, "sequential-helmfiles", false, "Process helmfile.d files sequentially in alphabetical order instead of in parallel. Useful when file order matters for dependencies.")
// avoid 'pflag: help requested' error (#251)
fs.BoolP("help", "h", false, "help for helmfile")
}

View File

@ -1338,12 +1338,19 @@ And there are two ways to organize your files.
The default helmfile directory is `helmfile.d`, that is,
in case helmfile is unable to locate `helmfile.yaml`, it tries to locate `helmfile.d/*.yaml`.
All the yaml files under the specified directory are processed in the alphabetical order. For example, you can use a `<two digit number>-<microservice>.yaml` naming convention to control the sync order.
By default, multiple files in `helmfile.d` are processed in **parallel** for better performance. If you need files to be processed **sequentially in alphabetical order** (e.g., for dependency ordering where databases must be deployed before applications), use the `--sequential-helmfiles` flag.
For example, you can use a `<two digit number>-<microservice>.yaml` naming convention to control the sync order when using `--sequential-helmfiles`:
* `helmfile.d`/
* `00-database.yaml`
* `00-backend.yaml`
* `01-frontend.yaml`
* `01-backend.yaml`
* `02-frontend.yaml`
```bash
# Process files sequentially in alphabetical order
helmfile --sequential-helmfiles sync
```
### Glob patterns

View File

@ -38,6 +38,7 @@ type App struct {
EnforcePluginVerification bool
HelmOCIPlainHTTP bool
DisableKubeVersionAutoDetection bool
SequentialHelmfiles bool
Logger *zap.SugaredLogger
Kubeconfig string
@ -86,6 +87,7 @@ func New(conf ConfigProvider) *App {
DisableForceUpdate: conf.DisableForceUpdate(),
EnforcePluginVerification: conf.EnforcePluginVerification(),
HelmOCIPlainHTTP: conf.HelmOCIPlainHTTP(),
SequentialHelmfiles: conf.SequentialHelmfiles(),
Logger: conf.Logger(),
Kubeconfig: conf.Kubeconfig(),
Env: conf.Env(),
@ -855,6 +857,9 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta
return a.visitStatesWithContext(fileOrDir, defOpts, converge, nil)
}
// processStateFileParallel processes a single helmfile state file in a goroutine.
// It is used for parallel processing of multiple helmfile.d files.
// Results are communicated via errChan (errors) and matchChan (whether file had matching releases).
func (a *App) processStateFileParallel(relPath string, defOpts LoadOpts, converge func(*state.HelmState) (bool, []error), sharedCtx *Context, errChan chan error, matchChan chan bool) {
var file string
var dir string
@ -974,7 +979,11 @@ func (a *App) visitStatesWithContext(fileOrDir string, defOpts LoadOpts, converg
desiredStateFiles, err := a.findDesiredStateFiles(fileOrDir, defOpts)
if len(desiredStateFiles) > 1 {
// Process files in parallel if we have multiple files and parallel mode is enabled
shouldProcessInParallel := len(desiredStateFiles) > 1 && !a.SequentialHelmfiles
if shouldProcessInParallel {
// Parallel processing for multiple files (default behavior)
var wg sync.WaitGroup
errChan := make(chan error, len(desiredStateFiles))
matchChan := make(chan bool, len(desiredStateFiles))
@ -1002,7 +1011,7 @@ func (a *App) visitStatesWithContext(fileOrDir string, defOpts LoadOpts, converg
noMatchInHelmfiles = false
}
} else {
// Sequential processing for single file
// Sequential processing for single file or when --sequential-helmfiles is set
err = a.visitStateFiles(fileOrDir, defOpts, func(f, d string) (retErr error) {
opts := defOpts.DeepCopy()

View File

@ -13,6 +13,7 @@ type ConfigProvider interface {
HelmOCIPlainHTTP() bool
SkipDeps() bool
SkipRefresh() bool
SequentialHelmfiles() bool
FileOrDir() string
KubeContext() string

View File

@ -74,6 +74,8 @@ type GlobalOptions struct {
Args string
// LogOutput is the writer to use for writing logs.
LogOutput io.Writer
// SequentialHelmfiles is true if helmfile.d files should be processed sequentially instead of in parallel.
SequentialHelmfiles bool
}
// Logger returns the logger to use.
@ -205,6 +207,11 @@ func (g *GlobalImpl) HelmOCIPlainHTTP() bool {
return g.GlobalOptions.HelmOCIPlainHTTP
}
// SequentialHelmfiles returns whether to process helmfile.d files sequentially
func (g *GlobalImpl) SequentialHelmfiles() bool {
return g.GlobalOptions.SequentialHelmfiles
}
// Logger returns the logger
func (g *GlobalImpl) Logger() *zap.SugaredLogger {
return g.logger