From 13ceedcc381c2735884f83f5a01080ef05ec325b Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Mon, 19 May 2025 17:38:03 +0200 Subject: [PATCH 01/10] Expose run state to allow enumeration of releases, etc. --- pkg/app/run.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2beab11ae..55a1ab45a 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -222,3 +222,9 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig return &infoMsg, releasesToBeUpdated, releasesToBeDeleted, nil } + +// Hack to get access to helmfile’s API +// -rluba, 2025-05-19 +func (r *Run) GetState() *state.HelmState { + return r.state +} From 0e253251f80131dcaac135ca48fe3622c2871b07 Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Thu, 22 May 2025 10:25:18 +0200 Subject: [PATCH 02/10] Rename & reorder variables to make equivalent code in sync and apply actually be the same --- pkg/app/app.go | 110 +++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 16a9c7209..a8857e6b3 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1360,17 +1360,17 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { return false, false, []error{err} } - var toApplyWithNeeds []state.ReleaseSpec + var releasesWithNeeds []state.ReleaseSpec for _, rs := range plan { for _, r := range rs { - toApplyWithNeeds = append(toApplyWithNeeds, r.ReleaseSpec) + releasesWithNeeds = append(releasesWithNeeds, r.ReleaseSpec) } } // Do build deps and prepare only on selected releases so that we won't waste time // on running various helm commands on unnecessary releases - st.Releases = toApplyWithNeeds + st.Releases = releasesWithNeeds // helm must be 2.11+ and helm-diff should be provided `--detailed-exitcode` in order for `helmfile apply` to work properly detailedExitCode := true @@ -1393,27 +1393,27 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { TakeOwnership: c.TakeOwnership(), } - infoMsg, releasesToBeUpdated, releasesToBeDeleted, errs := r.diff(false, detailedExitCode, c, diffOpts) - if len(errs) > 0 { - return false, false, errs + infoMsg, releasesToUpdate, releasesToDelete, diffErrs := r.diff(false, detailedExitCode, c, diffOpts) + if len(diffErrs) > 0 { + return false, false, diffErrs } var toDelete []state.ReleaseSpec - for _, r := range releasesToBeDeleted { + for _, r := range releasesToDelete { toDelete = append(toDelete, r) } var toUpdate []state.ReleaseSpec - for _, r := range releasesToBeUpdated { + for _, r := range releasesToUpdate { toUpdate = append(toUpdate, r) } releasesWithNoChange := map[string]state.ReleaseSpec{} - for _, r := range toApplyWithNeeds { + for _, r := range releasesWithNeeds { release := r id := state.ReleaseToID(&release) - _, uninstalled := releasesToBeDeleted[id] - _, updated := releasesToBeUpdated[id] + _, uninstalled := releasesToDelete[id] + _, updated := releasesToUpdate[id] if !uninstalled && !updated { releasesWithNoChange[id] = release } @@ -1435,15 +1435,15 @@ Do you really want to apply? a.Logger.Debug(infoMsgStr) } - var applyErrs []error - - affectedReleases := state.AffectedReleases{} + var errs []error // Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies st.Releases = selectedAndNeededReleases + affectedReleases := state.AffectedReleases{} + if !interactive || interactive && r.askForConfirmation(confMsg) { - if _, preapplyErrors := withDAG(st, helm, a.Logger, state.PlanOptions{Purpose: "invoking preapply hooks for", Reverse: true, SelectedReleases: toApplyWithNeeds, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error { + if _, preapplyErrors := withDAG(st, helm, a.Logger, state.PlanOptions{Purpose: "invoking preapply hooks for", Reverse: true, SelectedReleases: releasesWithNeeds, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error { for _, r := range subst.Releases { release := r if _, err := st.TriggerPreapplyEvent(&release, "apply"); err != nil { @@ -1457,13 +1457,13 @@ Do you really want to apply? } // We deleted releases by traversing the DAG in reverse order - if len(releasesToBeDeleted) > 0 { + if len(releasesToDelete) > 0 { _, deletionErrs := withDAG(st, helm, a.Logger, state.PlanOptions{Reverse: true, SelectedReleases: toDelete, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error { var rs []state.ReleaseSpec for _, r := range subst.Releases { release := r - if r2, ok := releasesToBeDeleted[state.ReleaseToID(&release)]; ok { + if r2, ok := releasesToDelete[state.ReleaseToID(&release)]; ok { rs = append(rs, r2) } } @@ -1474,18 +1474,18 @@ Do you really want to apply? })) if len(deletionErrs) > 0 { - applyErrs = append(applyErrs, deletionErrs...) + errs = append(errs, deletionErrs...) } } // We upgrade releases by traversing the DAG - if len(releasesToBeUpdated) > 0 { - _, updateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, Reverse: false, SkipNeeds: true, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error { + if len(releasesToUpdate) > 0 { + _, updateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, SkipNeeds: true, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error { var rs []state.ReleaseSpec for _, r := range subst.Releases { release := r - if r2, ok := releasesToBeUpdated[state.ReleaseToID(&release)]; ok { + if r2, ok := releasesToUpdate[state.ReleaseToID(&release)]; ok { rs = append(rs, r2) } } @@ -1493,27 +1493,27 @@ Do you really want to apply? subst.Releases = rs syncOpts := &state.SyncOpts{ + HideNotes: c.HideNotes(), + PostRenderer: c.PostRenderer(), + PostRendererArgs: c.PostRendererArgs(), + ResetValues: c.ResetValues(), + ReuseValues: c.ReuseValues(), Set: c.Set(), SkipCleanup: c.SkipCleanup(), SkipCRDs: c.SkipCRDs(), - Wait: c.Wait(), - WaitRetries: c.WaitRetries(), - WaitForJobs: c.WaitForJobs(), - ReuseValues: c.ReuseValues(), - ResetValues: c.ResetValues(), - PostRenderer: c.PostRenderer(), - PostRendererArgs: c.PostRendererArgs(), SkipSchemaValidation: c.SkipSchemaValidation(), SyncArgs: c.SyncArgs(), - HideNotes: c.HideNotes(), - TakeOwnership: c.TakeOwnership(), SyncReleaseLabels: c.SyncReleaseLabels(), + TakeOwnership: c.TakeOwnership(), + Wait: c.Wait(), + WaitForJobs: c.WaitForJobs(), + WaitRetries: c.WaitRetries(), } return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), syncOpts) })) if len(updateErrs) > 0 { - applyErrs = append(applyErrs, updateErrs...) + errs = append(errs, updateErrs...) } } } @@ -1526,11 +1526,11 @@ Do you really want to apply? a.Logger.Warnf("warn: %v\n", err) } } - if releasesToBeDeleted == nil && releasesToBeUpdated == nil { + if releasesToDelete == nil && releasesToUpdate == nil { return true, false, nil } - return true, true, applyErrs + return true, true, errs } func (a *App) delete(r *Run, purge bool, c DestroyConfigProvider) (bool, []error) { @@ -1743,6 +1743,8 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { st := r.state helm := r.helm + helm.SetExtraArgs(GetArgs(c.Args(), r.state)...) + selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds()) if err != nil { return false, []error{err} @@ -1757,24 +1759,24 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { // See https://github.com/roboll/helmfile/issues/1818 for more context. st.Releases = selectedAndNeededReleases - batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(), SkipNeeds: c.SkipNeeds()}) + batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}) if err != nil { return false, []error{err} } - var toSyncWithNeeds []state.ReleaseSpec + var releasesWithNeeds []state.ReleaseSpec for _, rs := range batches { for _, r := range rs { - toSyncWithNeeds = append(toSyncWithNeeds, r.ReleaseSpec) + releasesWithNeeds = append(releasesWithNeeds, r.ReleaseSpec) } } // Do build deps and prepare only on selected releases so that we won't waste time // on running various helm commands on unnecessary releases - st.Releases = toSyncWithNeeds + st.Releases = releasesWithNeeds - toDelete, err := st.DetectReleasesToBeDeletedForSync(helm, toSyncWithNeeds) + toDelete, err := st.DetectReleasesToBeDeletedForSync(helm, releasesWithNeeds) if err != nil { return false, []error{err} } @@ -1787,7 +1789,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { } var toUpdate []state.ReleaseSpec - for _, r := range toSyncWithNeeds { + for _, r := range releasesWithNeeds { release := r if _, deleted := releasesToDelete[state.ReleaseToID(&release)]; !deleted { if r.Desired() { @@ -1807,7 +1809,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { } releasesWithNoChange := map[string]state.ReleaseSpec{} - for _, r := range toSyncWithNeeds { + for _, r := range releasesWithNeeds { release := r id := state.ReleaseToID(&release) _, uninstalled := releasesToDelete[id] @@ -1851,8 +1853,6 @@ Do you really want to sync? var errs []error - r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...) - // Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies st.Releases = selectedAndNeededReleases @@ -1893,23 +1893,23 @@ Do you really want to sync? subst.Releases = rs - opts := &state.SyncOpts{ - Set: c.Set(), - SkipCRDs: c.SkipCRDs(), - Wait: c.Wait(), - WaitRetries: c.WaitRetries(), - WaitForJobs: c.WaitForJobs(), - ReuseValues: c.ReuseValues(), - ResetValues: c.ResetValues(), + syncOpts := &state.SyncOpts{ + HideNotes: c.HideNotes(), PostRenderer: c.PostRenderer(), PostRendererArgs: c.PostRendererArgs(), - SyncArgs: c.SyncArgs(), - HideNotes: c.HideNotes(), - TakeOwnership: c.TakeOwnership(), + ResetValues: c.ResetValues(), + ReuseValues: c.ReuseValues(), + Set: c.Set(), + SkipCRDs: c.SkipCRDs(), SkipSchemaValidation: c.SkipSchemaValidation(), + SyncArgs: c.SyncArgs(), SyncReleaseLabels: c.SyncReleaseLabels(), + TakeOwnership: c.TakeOwnership(), + Wait: c.Wait(), + WaitForJobs: c.WaitForJobs(), + WaitRetries: c.WaitRetries(), } - return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), opts) + return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), syncOpts) })) if len(syncErrs) > 0 { @@ -1917,7 +1917,9 @@ Do you really want to sync? } } } + affectedReleases.DisplayAffectedReleases(c.Logger()) + return true, errs } From 274f2d882d9cf9861522ec710427b73843aac4f0 Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Thu, 22 May 2025 10:26:36 +0200 Subject: [PATCH 03/10] sync: Emit "cleanup" events later to match the behavior in "apply" --- pkg/app/app.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index a8857e6b3..27ef5458a 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1819,13 +1819,6 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { } } - for id := range releasesWithNoChange { - r := releasesWithNoChange[id] - if _, err := st.TriggerCleanupEvent(&r, "sync"); err != nil { - a.Logger.Warnf("warn: %v\n", err) - } - } - names := []string{} for _, r := range releasesToUpdate { names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart)) @@ -1920,6 +1913,13 @@ Do you really want to sync? affectedReleases.DisplayAffectedReleases(c.Logger()) + for id := range releasesWithNoChange { + r := releasesWithNoChange[id] + if _, err := st.TriggerCleanupEvent(&r, "sync"); err != nil { + a.Logger.Warnf("warn: %v\n", err) + } + } + return true, errs } From 9c21fc168123518361c9ff468064a73bea8815a9 Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Thu, 22 May 2025 10:36:09 +0200 Subject: [PATCH 04/10] Fixup --- pkg/app/app.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 27ef5458a..594098027 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1355,14 +1355,14 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { // See https://github.com/roboll/helmfile/issues/1818 for more context. st.Releases = selectedAndNeededReleases - plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}) + batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}) if err != nil { return false, false, []error{err} } var releasesWithNeeds []state.ReleaseSpec - for _, rs := range plan { + for _, rs := range batches { for _, r := range rs { releasesWithNeeds = append(releasesWithNeeds, r.ReleaseSpec) } From 0013306fb16e1b69f492b38faaa890641733bb72 Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Thu, 22 May 2025 17:39:23 +0200 Subject: [PATCH 05/10] Extract common preparation code from sync + apply --- pkg/app/app.go | 59 ++++++++++++++++++++++---------------------------- pkg/app/run.go | 6 +++++ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 594098027..c18996630 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1335,29 +1335,24 @@ func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state. return selected, deduplicated, nil } -func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { - st := r.state - helm := r.helm - - helm.SetExtraArgs(GetArgs(c.Args(), r.state)...) - - selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds()) +func (a *App) GetPlannedAndSelectedReleasesWithNeeds(r *Run, skipNeeds bool, includeNeeds bool, includeTransitiveNeeds bool) ([]state.ReleaseSpec, []state.ReleaseSpec, error) { + selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, includeTransitiveNeeds) if err != nil { - return false, false, []error{err} + return nil, nil, err } if len(selectedReleases) == 0 { - return false, false, nil + return nil, nil, nil } // This is required when you're trying to deduplicate releases by the selector. // Without this, `PlanReleases` conflates duplicates and return both in `batches`, // even if we provided `SelectedReleases: selectedReleases`. // See https://github.com/roboll/helmfile/issues/1818 for more context. - st.Releases = selectedAndNeededReleases + r.state.Releases = selectedAndNeededReleases - batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}) + batches, err := r.state.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: skipNeeds, IncludeNeeds: includeNeeds, IncludeTransitiveNeeds: includeTransitiveNeeds}) if err != nil { - return false, false, []error{err} + return nil, nil, err } var releasesWithNeeds []state.ReleaseSpec @@ -1368,6 +1363,23 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { } } + return releasesWithNeeds, selectedAndNeededReleases, nil +} + +func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { + st := r.state + helm := r.helm + + helm.SetExtraArgs(GetArgs(c.Args(), r.state)...) + + releasesWithNeeds, selectedAndNeededReleases, err := a.GetPlannedAndSelectedReleasesWithNeeds(r, c.SkipNeeds(), c.IncludeNeeds(), c.IncludeTransitiveNeeds()) + if err != nil { + return false, false, []error{err} + } + if len(releasesWithNeeds) == 0 { + return false, false, nil + } + // Do build deps and prepare only on selected releases so that we won't waste time // on running various helm commands on unnecessary releases st.Releases = releasesWithNeeds @@ -1745,33 +1757,14 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { helm.SetExtraArgs(GetArgs(c.Args(), r.state)...) - selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds()) + releasesWithNeeds, selectedAndNeededReleases, err := a.GetPlannedAndSelectedReleasesWithNeeds(r, c.SkipNeeds(), c.IncludeNeeds(), c.IncludeTransitiveNeeds()) if err != nil { return false, []error{err} } - if len(selectedReleases) == 0 { + if len(releasesWithNeeds) == 0 { return false, nil } - // This is required when you're trying to deduplicate releases by the selector. - // Without this, `PlanReleases` conflates duplicates and return both in `batches`, - // even if we provided `SelectedReleases: selectedReleases`. - // See https://github.com/roboll/helmfile/issues/1818 for more context. - st.Releases = selectedAndNeededReleases - - batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}) - if err != nil { - return false, []error{err} - } - - var releasesWithNeeds []state.ReleaseSpec - - for _, rs := range batches { - for _, r := range rs { - releasesWithNeeds = append(releasesWithNeeds, r.ReleaseSpec) - } - } - // Do build deps and prepare only on selected releases so that we won't waste time // on running various helm commands on unnecessary releases st.Releases = releasesWithNeeds diff --git a/pkg/app/run.go b/pkg/app/run.go index 55a1ab45a..27cce13fa 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -228,3 +228,9 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig func (r *Run) GetState() *state.HelmState { return r.state } + +// Hack to get access to helmfile’s API +// -rluba, 2025-05-19 +func (r *Run) GetHelm() helmexec.Interface { + return r.helm +} From 162cfe579470cde63dde9be13f731ed4c7ba7447 Mon Sep 17 00:00:00 2001 From: Raphael Luba Date: Mon, 26 May 2025 10:32:49 +0200 Subject: [PATCH 06/10] Expose more APIs --- pkg/app/app.go | 30 +++++++++++++++--------------- pkg/app/run.go | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index c18996630..0bba8e40b 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -154,7 +154,7 @@ func (a *App) Diff(c DiffConfigProvider) error { includeCRDs := !c.SkipCRDs() - prepErr := run.withPreparedCharts("diff", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("diff", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -224,7 +224,7 @@ func (a *App) Template(c TemplateConfigProvider) error { // https://github.com/helmfile/helmfile/issues/1749 run.helm.SetExtraArgs() - prepErr := run.withPreparedCharts("template", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("template", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -250,7 +250,7 @@ func (a *App) Template(c TemplateConfigProvider) error { func (a *App) WriteValues(c WriteValuesConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - prepErr := run.withPreparedCharts("write-values", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("write-values", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -301,7 +301,7 @@ func (a *App) Lint(c LintConfigProvider) error { var lintErrs []error // `helm lint` on helm v2 and v3 does not support remote charts, that we need to set `forceDownload=true` here - prepErr := run.withPreparedCharts("lint", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("lint", state.ChartPrepareOptions{ ForceDownload: true, SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), @@ -337,7 +337,7 @@ func (a *App) Lint(c LintConfigProvider) error { func (a *App) Fetch(c FetchConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - prepErr := run.withPreparedCharts("pull", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("pull", state.ChartPrepareOptions{ ForceDownload: true, SkipRefresh: c.SkipRefresh(), SkipRepos: c.SkipRefresh() || c.SkipDeps(), @@ -360,7 +360,7 @@ func (a *App) Sync(c SyncConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { includeCRDs := !c.SkipCRDs() - prepErr := run.withPreparedCharts("sync", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("sync", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -372,7 +372,7 @@ func (a *App) Sync(c SyncConfigProvider) error { Validate: c.Validate(), Concurrency: c.Concurrency(), }, func() { - ok, errs = a.sync(run, c) + ok, errs = a.SyncRun(run, c) }) if prepErr != nil { @@ -395,7 +395,7 @@ func (a *App) Apply(c ApplyConfigProvider) error { err := a.ForEachState(func(run *Run) (ok bool, errs []error) { includeCRDs := !c.SkipCRDs() - prepErr := run.withPreparedCharts("apply", state.ChartPrepareOptions{ + prepErr := run.WithPreparedCharts("apply", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -439,7 +439,7 @@ func (a *App) Apply(c ApplyConfigProvider) error { func (a *App) Status(c StatusesConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - err := run.withPreparedCharts("status", state.ChartPrepareOptions{ + err := run.WithPreparedCharts("status", state.ChartPrepareOptions{ SkipRepos: true, SkipDeps: true, Concurrency: c.Concurrency(), @@ -458,7 +458,7 @@ func (a *App) Status(c StatusesConfigProvider) error { func (a *App) Destroy(c DestroyConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { if !c.SkipCharts() { - err := run.withPreparedCharts("destroy", state.ChartPrepareOptions{ + err := run.WithPreparedCharts("destroy", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -486,7 +486,7 @@ func (a *App) Test(c TestConfigProvider) error { "or set helm.sh/hook-delete-policy\n") } - err := run.withPreparedCharts("test", state.ChartPrepareOptions{ + err := run.WithPreparedCharts("test", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), @@ -506,7 +506,7 @@ func (a *App) Test(c TestConfigProvider) error { func (a *App) PrintDAGState(c DAGConfigProvider) error { var err error return a.ForEachState(func(run *Run) (ok bool, errs []error) { - err = run.withPreparedCharts("show-dag", state.ChartPrepareOptions{ + err = run.WithPreparedCharts("show-dag", state.ChartPrepareOptions{ SkipRepos: true, SkipDeps: true, Concurrency: 2, @@ -522,7 +522,7 @@ func (a *App) PrintDAGState(c DAGConfigProvider) error { func (a *App) PrintState(c StateConfigProvider) error { return a.ForEachState(func(run *Run) (_ bool, errs []error) { - err := run.withPreparedCharts("build", state.ChartPrepareOptions{ + err := run.WithPreparedCharts("build", state.ChartPrepareOptions{ SkipRepos: true, SkipDeps: true, Concurrency: 2, @@ -594,7 +594,7 @@ func (a *App) ListReleases(c ListConfigProvider) error { var err error if !c.SkipCharts() { - err = run.withPreparedCharts("list", state.ChartPrepareOptions{ + err = run.WithPreparedCharts("list", state.ChartPrepareOptions{ SkipRepos: true, SkipDeps: true, Concurrency: 2, @@ -1751,7 +1751,7 @@ func (a *App) status(r *Run, c StatusesConfigProvider) (bool, []error) { return true, errs } -func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { +func (a *App) SyncRun(r *Run, c SyncConfigProvider) (bool, []error) { st := r.state helm := r.helm diff --git a/pkg/app/run.go b/pkg/app/run.go index 27cce13fa..b438e80a8 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -57,7 +57,7 @@ func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurre return releaseToChart, nil } -func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepareOptions, f func()) error { +func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepareOptions, f func()) error { if r.ReleaseToChart != nil { panic("Run.PrepareCharts can be called only once") } From a8b1da1a684d3b380b324f73de120df6cc0a441a Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Mon, 27 Oct 2025 17:05:07 +0100 Subject: [PATCH 07/10] fix: #344 allow helmfile to continue on partial install errors --- cmd/root.go | 1 + pkg/app/app.go | 74 ++++++++++++++++++++++++----------------- pkg/app/app_test.go | 10 ++++++ pkg/app/config.go | 10 ++++++ pkg/app/destroy_test.go | 5 +++ pkg/app/diff_test.go | 5 +++ pkg/app/run.go | 28 +++++++++++++--- pkg/config/global.go | 7 ++++ pkg/state/state.go | 30 ++++++++++++----- 9 files changed, 126 insertions(+), 44 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index ed629d12e..d2504b5ee 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -122,6 +122,7 @@ func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalO fs.StringArrayVar(&globalOptions.StateValuesFile, "state-values-file", nil, "specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).") fs.BoolVar(&globalOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) fs.BoolVar(&globalOptions.SkipRefresh, "skip-refresh", false, `skip running "helm repo update"`) + fs.BoolVar(&globalOptions.AllowPartialErrors, "allow-partial-errors", false, `allow partial errors during release processing, will continue and report individual releases that failed and continue to install the others`) fs.BoolVar(&globalOptions.StripArgsValuesOnExitError, "strip-args-values-on-exit-error", true, `Strip the potential secret values of the helm command args contained in a helmfile error message`) fs.BoolVar(&globalOptions.DisableForceUpdate, "disable-force-update", false, `do not force helm repos to update when executing "helm repo add"`) fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "Silence output. Equivalent to log-level warn") diff --git a/pkg/app/app.go b/pkg/app/app.go index 0bba8e40b..b9679b939 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -158,6 +158,7 @@ func (a *App) Diff(c DiffConfigProvider) error { SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), + AllowPartialErrors: c.AllowPartialErrors(), IncludeCRDs: &includeCRDs, Validate: c.Validate(), Concurrency: c.Concurrency(), @@ -227,6 +228,7 @@ func (a *App) Template(c TemplateConfigProvider) error { prepErr := run.WithPreparedCharts("template", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), IncludeCRDs: &includeCRDs, SkipCleanup: c.SkipCleanup(), @@ -251,11 +253,12 @@ func (a *App) Template(c TemplateConfigProvider) error { func (a *App) WriteValues(c WriteValuesConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { prepErr := run.WithPreparedCharts("write-values", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - SkipCleanup: c.SkipCleanup(), - Concurrency: c.Concurrency(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + SkipCleanup: c.SkipCleanup(), + Concurrency: c.Concurrency(), }, func() { ok, errs = a.writeValues(run, c) }) @@ -305,6 +308,7 @@ func (a *App) Lint(c LintConfigProvider) error { ForceDownload: true, SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), SkipCleanup: c.SkipCleanup(), Concurrency: c.Concurrency(), @@ -338,13 +342,14 @@ func (a *App) Lint(c LintConfigProvider) error { func (a *App) Fetch(c FetchConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { prepErr := run.WithPreparedCharts("pull", state.ChartPrepareOptions{ - ForceDownload: true, - SkipRefresh: c.SkipRefresh(), - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipDeps: c.SkipDeps(), - OutputDir: c.OutputDir(), - OutputDirTemplate: c.OutputDirTemplate(), - Concurrency: c.Concurrency(), + ForceDownload: true, + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipDeps: c.SkipDeps(), + OutputDir: c.OutputDir(), + OutputDirTemplate: c.OutputDirTemplate(), + Concurrency: c.Concurrency(), }, func() { }) @@ -363,6 +368,7 @@ func (a *App) Sync(c SyncConfigProvider) error { prepErr := run.WithPreparedCharts("sync", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), Wait: c.Wait(), WaitRetries: c.WaitRetries(), @@ -398,6 +404,7 @@ func (a *App) Apply(c ApplyConfigProvider) error { prepErr := run.WithPreparedCharts("apply", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), Wait: c.Wait(), WaitRetries: c.WaitRetries(), @@ -459,12 +466,13 @@ func (a *App) Destroy(c DestroyConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { if !c.SkipCharts() { err := run.WithPreparedCharts("destroy", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), - DeleteWait: c.DeleteWait(), - DeleteTimeout: c.DeleteTimeout(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), + DeleteWait: c.DeleteWait(), + DeleteTimeout: c.DeleteTimeout(), }, func() { ok, errs = a.delete(run, true, c) }) @@ -487,10 +495,11 @@ func (a *App) Test(c TestConfigProvider) error { } err := run.WithPreparedCharts("test", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), }, func() { errs = a.test(run, c) }) @@ -507,9 +516,10 @@ func (a *App) PrintDAGState(c DAGConfigProvider) error { var err error return a.ForEachState(func(run *Run) (ok bool, errs []error) { err = run.WithPreparedCharts("show-dag", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { err = a.dag(run) if err != nil { @@ -523,9 +533,10 @@ func (a *App) PrintDAGState(c DAGConfigProvider) error { func (a *App) PrintState(c StateConfigProvider) error { return a.ForEachState(func(run *Run) (_ bool, errs []error) { err := run.WithPreparedCharts("build", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { if c.EmbedValues() { for i := range run.state.Releases { @@ -595,9 +606,10 @@ func (a *App) ListReleases(c ListConfigProvider) error { if !c.SkipCharts() { err = run.WithPreparedCharts("list", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { rel, err := a.list(run) if err != nil { diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 9494c2adb..77fbc3c38 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2103,6 +2103,7 @@ type configImpl struct { skipTests bool skipSchemaValidation bool skipRefresh bool + allowPartialErrors bool skipNeeds bool includeNeeds bool @@ -2147,6 +2148,10 @@ func (c configImpl) SkipRefresh() bool { return c.skipRefresh } +func (c configImpl) AllowPartialErrors() bool { + return c.allowPartialErrors +} + func (c configImpl) SkipNeeds() bool { return c.skipNeeds } @@ -2226,6 +2231,7 @@ type applyConfig struct { skipCRDs bool skipDeps bool skipRefresh bool + allowPartialErrors bool skipNeeds bool includeNeeds bool includeTransitiveNeeds bool @@ -2314,6 +2320,10 @@ func (a applyConfig) SkipRefresh() bool { return a.skipRefresh } +func (a applyConfig) AllowPartialErrors() bool { + return a.allowPartialErrors +} + func (a applyConfig) SkipNeeds() bool { return a.skipNeeds } diff --git a/pkg/app/config.go b/pkg/app/config.go index e699d86f1..886f5a7da 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -11,6 +11,7 @@ type ConfigProvider interface { DisableForceUpdate() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool FileOrDir() string KubeContext() string @@ -53,6 +54,7 @@ type ApplyConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Wait() bool WaitRetries() int WaitForJobs() bool @@ -104,6 +106,7 @@ type SyncConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Wait() bool WaitRetries() int WaitForJobs() bool @@ -138,6 +141,7 @@ type DiffConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool IncludeTests() bool @@ -169,6 +173,7 @@ type DestroyConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCharts() bool DeleteWait() bool DeleteTimeout() int @@ -183,6 +188,7 @@ type TestConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Timeout() int Cleanup() bool Logs() bool @@ -197,6 +203,7 @@ type LintConfigProvider interface { Set() []string SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool DAGConfig @@ -207,6 +214,7 @@ type LintConfigProvider interface { type FetchConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool OutputDir() string OutputDirTemplate() string @@ -225,6 +233,7 @@ type TemplateConfigProvider interface { Validate() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool SkipTests() bool OutputDir() string @@ -250,6 +259,7 @@ type WriteValuesConfigProvider interface { OutputFileTemplate() string SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool IncludeTransitiveNeeds() bool diff --git a/pkg/app/destroy_test.go b/pkg/app/destroy_test.go index 4d578851b..64033e642 100644 --- a/pkg/app/destroy_test.go +++ b/pkg/app/destroy_test.go @@ -39,6 +39,7 @@ type destroyConfig struct { interactive bool skipDeps bool skipRefresh bool + allowPartialErrors bool logger *zap.SugaredLogger includeTransitiveNeeds bool skipCharts bool @@ -78,6 +79,10 @@ func (d destroyConfig) SkipRefresh() bool { return d.skipRefresh } +func (d destroyConfig) AllowPartialErrors() bool { + return d.allowPartialErrors +} + func (d destroyConfig) IncludeTransitiveNeeds() bool { return d.includeTransitiveNeeds } diff --git a/pkg/app/diff_test.go b/pkg/app/diff_test.go index 18dde7fc5..631a5286e 100644 --- a/pkg/app/diff_test.go +++ b/pkg/app/diff_test.go @@ -25,6 +25,7 @@ type diffConfig struct { skipCRDs bool skipDeps bool skipRefresh bool + allowPartialErrors bool includeTests bool skipNeeds bool includeNeeds bool @@ -81,6 +82,10 @@ func (a diffConfig) SkipRefresh() bool { return a.skipRefresh } +func (a diffConfig) AllowPartialErrors() bool { + return a.allowPartialErrors +} + func (a diffConfig) IncludeTests() bool { return a.includeTests } diff --git a/pkg/app/run.go b/pkg/app/run.go index b438e80a8..60726d9df 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -51,7 +51,13 @@ func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurre releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, concurrency, helmfileCommand, opts) if len(errs) > 0 { - return nil, fmt.Errorf("%v", errs) + if !opts.AllowPartialErrors { + // abort on first error + return nil, fmt.Errorf("%v", errs) + } else { + // return partial results with errors for the failed ones + return releaseToChart, fmt.Errorf("%v", errs) + } } return releaseToChart, nil @@ -90,8 +96,11 @@ func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepare } releaseToChart, err := r.prepareChartsIfNeeded(helmfileCommand, dir, opts.Concurrency, opts) - if err != nil { - return err + // IMPORTANT: on opts.AllowPartialErrors: do not abort on error here, just forward it to the caller in order to allow for partial results + if !opts.AllowPartialErrors { + if err != nil { + return err + } } for i := range r.state.Releases { @@ -115,8 +124,17 @@ func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepare f() - _, err = r.state.TriggerGlobalCleanupEvent(helmfileCommand) - return err + _, cleanupErr := r.state.TriggerGlobalCleanupEvent(helmfileCommand) + if !opts.AllowPartialErrors { + // return directly on first error + return cleanupErr + } else { + // merge the two errors into a single error output + if err != nil || cleanupErr != nil { + return fmt.Errorf("prepare charts error: %v; cleanup error: %v", err, cleanupErr) + } + return nil + } } func (r *Run) Deps(c DepsConfigProvider) []error { diff --git a/pkg/config/global.go b/pkg/config/global.go index fe9061954..97e7af932 100644 --- a/pkg/config/global.go +++ b/pkg/config/global.go @@ -34,6 +34,8 @@ type GlobalOptions struct { SkipDeps bool // SkipRefresh is true if the running "helm repo update" should be skipped SkipRefresh bool + // AllowPartialErrors is true if partial errors during release processing are allowed + AllowPartialErrors bool // StripArgsValuesOnExitError is true if the ARGS output on exit error should be suppressed StripArgsValuesOnExitError bool // DisableForceUpdate is true if force updating repos is not desirable when executing "helm repo add" @@ -181,6 +183,11 @@ func (g *GlobalImpl) SkipRefresh() bool { return g.GlobalOptions.SkipRefresh } +// AllowPartialErrors return if partial errors during release processing are allowed +func (g *GlobalImpl) AllowPartialErrors() bool { + return g.GlobalOptions.AllowPartialErrors +} + // StripArgsValuesOnExitError return if the ARGS output on exit error should be suppressed func (g *GlobalImpl) StripArgsValuesOnExitError() bool { return g.GlobalOptions.StripArgsValuesOnExitError diff --git a/pkg/state/state.go b/pkg/state/state.go index b167fa883..ec6995d72 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1133,12 +1133,13 @@ func releasesNeedCharts(releases []ReleaseSpec) []ReleaseSpec { } type ChartPrepareOptions struct { - ForceDownload bool - SkipRepos bool - SkipDeps bool - SkipRefresh bool - SkipResolve bool - SkipCleanup bool + ForceDownload bool + SkipRepos bool + SkipDeps bool + SkipRefresh bool + SkipResolve bool + SkipCleanup bool + AllowPartialErrors bool // Validate is a helm-3-only option. When it is set to true, it configures chartify to pass --validate to helm-template run by it. // It's required when one of your chart relies on Capabilities.APIVersions in a template Validate bool @@ -1428,7 +1429,12 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre if downloadRes.err != nil { errs = append(errs, downloadRes.err) - return + if !opts.AllowPartialErrors { + return + } else { + // continue processing other releases even if one fails + continue + } } func() { prepareChartInfoMutex.Lock() @@ -1448,7 +1454,15 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre ) if len(errs) > 0 { - return nil, errs + if !opts.AllowPartialErrors { + return nil, errs + } else { + // continue processing other releases even if one fails, return partial results with errors for the failed ones + st.logger.Warnf("Some charts failed to prepare:\n") + for _, err := range errs { + st.logger.Warnf(" - %v\n", err) + } + } } if len(builds) > 0 { From de7fb8b572e8dc4f37ced871acd3ad7ff6ddb2ff Mon Sep 17 00:00:00 2001 From: Peter Honeder Date: Mon, 27 Oct 2025 17:05:07 +0100 Subject: [PATCH 08/10] fix: #344 allow helmfile to continue on partial install errors --- cmd/root.go | 1 + pkg/app/app.go | 74 ++++++++++++++++++++++++----------------- pkg/app/app_test.go | 10 ++++++ pkg/app/config.go | 10 ++++++ pkg/app/destroy_test.go | 5 +++ pkg/app/diff_test.go | 5 +++ pkg/app/run.go | 28 +++++++++++++--- pkg/config/global.go | 7 ++++ pkg/state/state.go | 30 ++++++++++++----- 9 files changed, 126 insertions(+), 44 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index bb4dd64f0..4cd8bd00a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -122,6 +122,7 @@ func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalO fs.StringArrayVar(&globalOptions.StateValuesFile, "state-values-file", nil, "specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).") fs.BoolVar(&globalOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) fs.BoolVar(&globalOptions.SkipRefresh, "skip-refresh", false, `skip running "helm repo update"`) + fs.BoolVar(&globalOptions.AllowPartialErrors, "allow-partial-errors", false, `allow partial errors during release processing, will continue and report individual releases that failed and continue to install the others`) fs.BoolVar(&globalOptions.StripArgsValuesOnExitError, "strip-args-values-on-exit-error", true, `Strip the potential secret values of the helm command args contained in a helmfile error message`) fs.BoolVar(&globalOptions.DisableForceUpdate, "disable-force-update", false, `do not force helm repos to update when executing "helm repo add"`) fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "Silence output. Equivalent to log-level warn") diff --git a/pkg/app/app.go b/pkg/app/app.go index a151de5b8..e85bd01fd 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -158,6 +158,7 @@ func (a *App) Diff(c DiffConfigProvider) error { SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), SkipDeps: c.SkipDeps(), + AllowPartialErrors: c.AllowPartialErrors(), IncludeCRDs: &includeCRDs, Validate: c.Validate(), Concurrency: c.Concurrency(), @@ -227,6 +228,7 @@ func (a *App) Template(c TemplateConfigProvider) error { prepErr := run.WithPreparedCharts("template", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), IncludeCRDs: &includeCRDs, SkipCleanup: c.SkipCleanup(), @@ -251,11 +253,12 @@ func (a *App) Template(c TemplateConfigProvider) error { func (a *App) WriteValues(c WriteValuesConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { prepErr := run.WithPreparedCharts("write-values", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - SkipCleanup: c.SkipCleanup(), - Concurrency: c.Concurrency(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + SkipCleanup: c.SkipCleanup(), + Concurrency: c.Concurrency(), }, func() { ok, errs = a.writeValues(run, c) }) @@ -305,6 +308,7 @@ func (a *App) Lint(c LintConfigProvider) error { ForceDownload: true, SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), SkipCleanup: c.SkipCleanup(), Concurrency: c.Concurrency(), @@ -338,13 +342,14 @@ func (a *App) Lint(c LintConfigProvider) error { func (a *App) Fetch(c FetchConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { prepErr := run.WithPreparedCharts("pull", state.ChartPrepareOptions{ - ForceDownload: true, - SkipRefresh: c.SkipRefresh(), - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipDeps: c.SkipDeps(), - OutputDir: c.OutputDir(), - OutputDirTemplate: c.OutputDirTemplate(), - Concurrency: c.Concurrency(), + ForceDownload: true, + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipDeps: c.SkipDeps(), + OutputDir: c.OutputDir(), + OutputDirTemplate: c.OutputDirTemplate(), + Concurrency: c.Concurrency(), }, func() {}) if prepErr != nil { @@ -362,6 +367,7 @@ func (a *App) Sync(c SyncConfigProvider) error { prepErr := run.WithPreparedCharts("sync", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), Wait: c.Wait(), WaitRetries: c.WaitRetries(), @@ -397,6 +403,7 @@ func (a *App) Apply(c ApplyConfigProvider) error { prepErr := run.WithPreparedCharts("apply", state.ChartPrepareOptions{ SkipRepos: c.SkipRefresh() || c.SkipDeps(), SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), SkipDeps: c.SkipDeps(), Wait: c.Wait(), WaitRetries: c.WaitRetries(), @@ -458,12 +465,13 @@ func (a *App) Destroy(c DestroyConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { if !c.SkipCharts() { err := run.WithPreparedCharts("destroy", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), - DeleteWait: c.DeleteWait(), - DeleteTimeout: c.DeleteTimeout(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), + DeleteWait: c.DeleteWait(), + DeleteTimeout: c.DeleteTimeout(), }, func() { ok, errs = a.delete(run, true, c) }) @@ -486,10 +494,11 @@ func (a *App) Test(c TestConfigProvider) error { } err := run.WithPreparedCharts("test", state.ChartPrepareOptions{ - SkipRepos: c.SkipRefresh() || c.SkipDeps(), - SkipRefresh: c.SkipRefresh(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), + SkipRepos: c.SkipRefresh() || c.SkipDeps(), + SkipRefresh: c.SkipRefresh(), + AllowPartialErrors: c.AllowPartialErrors(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), }, func() { errs = a.test(run, c) }) @@ -506,9 +515,10 @@ func (a *App) PrintDAGState(c DAGConfigProvider) error { var err error return a.ForEachState(func(run *Run) (ok bool, errs []error) { err = run.WithPreparedCharts("show-dag", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { err = a.dag(run) if err != nil { @@ -522,9 +532,10 @@ func (a *App) PrintDAGState(c DAGConfigProvider) error { func (a *App) PrintState(c StateConfigProvider) error { return a.ForEachState(func(run *Run) (_ bool, errs []error) { err := run.WithPreparedCharts("build", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { if c.EmbedValues() { for i := range run.state.Releases { @@ -594,9 +605,10 @@ func (a *App) ListReleases(c ListConfigProvider) error { if !c.SkipCharts() { err = run.WithPreparedCharts("list", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, + SkipRepos: true, + SkipDeps: true, + AllowPartialErrors: true, + Concurrency: 2, }, func() { rel, err := a.list(run) if err != nil { diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index b14543f40..98929a4f1 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2194,6 +2194,7 @@ type configImpl struct { skipTests bool skipSchemaValidation bool skipRefresh bool + allowPartialErrors bool skipNeeds bool includeNeeds bool @@ -2238,6 +2239,10 @@ func (c configImpl) SkipRefresh() bool { return c.skipRefresh } +func (c configImpl) AllowPartialErrors() bool { + return c.allowPartialErrors +} + func (c configImpl) SkipNeeds() bool { return c.skipNeeds } @@ -2317,6 +2322,7 @@ type applyConfig struct { skipCRDs bool skipDeps bool skipRefresh bool + allowPartialErrors bool skipNeeds bool includeNeeds bool includeTransitiveNeeds bool @@ -2405,6 +2411,10 @@ func (a applyConfig) SkipRefresh() bool { return a.skipRefresh } +func (a applyConfig) AllowPartialErrors() bool { + return a.allowPartialErrors +} + func (a applyConfig) SkipNeeds() bool { return a.skipNeeds } diff --git a/pkg/app/config.go b/pkg/app/config.go index e699d86f1..886f5a7da 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -11,6 +11,7 @@ type ConfigProvider interface { DisableForceUpdate() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool FileOrDir() string KubeContext() string @@ -53,6 +54,7 @@ type ApplyConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Wait() bool WaitRetries() int WaitForJobs() bool @@ -104,6 +106,7 @@ type SyncConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Wait() bool WaitRetries() int WaitForJobs() bool @@ -138,6 +141,7 @@ type DiffConfigProvider interface { SkipCRDs() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool IncludeTests() bool @@ -169,6 +173,7 @@ type DestroyConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCharts() bool DeleteWait() bool DeleteTimeout() int @@ -183,6 +188,7 @@ type TestConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool Timeout() int Cleanup() bool Logs() bool @@ -197,6 +203,7 @@ type LintConfigProvider interface { Set() []string SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool DAGConfig @@ -207,6 +214,7 @@ type LintConfigProvider interface { type FetchConfigProvider interface { SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool OutputDir() string OutputDirTemplate() string @@ -225,6 +233,7 @@ type TemplateConfigProvider interface { Validate() bool SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool SkipTests() bool OutputDir() string @@ -250,6 +259,7 @@ type WriteValuesConfigProvider interface { OutputFileTemplate() string SkipDeps() bool SkipRefresh() bool + AllowPartialErrors() bool SkipCleanup() bool IncludeTransitiveNeeds() bool diff --git a/pkg/app/destroy_test.go b/pkg/app/destroy_test.go index 4d578851b..64033e642 100644 --- a/pkg/app/destroy_test.go +++ b/pkg/app/destroy_test.go @@ -39,6 +39,7 @@ type destroyConfig struct { interactive bool skipDeps bool skipRefresh bool + allowPartialErrors bool logger *zap.SugaredLogger includeTransitiveNeeds bool skipCharts bool @@ -78,6 +79,10 @@ func (d destroyConfig) SkipRefresh() bool { return d.skipRefresh } +func (d destroyConfig) AllowPartialErrors() bool { + return d.allowPartialErrors +} + func (d destroyConfig) IncludeTransitiveNeeds() bool { return d.includeTransitiveNeeds } diff --git a/pkg/app/diff_test.go b/pkg/app/diff_test.go index 18dde7fc5..631a5286e 100644 --- a/pkg/app/diff_test.go +++ b/pkg/app/diff_test.go @@ -25,6 +25,7 @@ type diffConfig struct { skipCRDs bool skipDeps bool skipRefresh bool + allowPartialErrors bool includeTests bool skipNeeds bool includeNeeds bool @@ -81,6 +82,10 @@ func (a diffConfig) SkipRefresh() bool { return a.skipRefresh } +func (a diffConfig) AllowPartialErrors() bool { + return a.allowPartialErrors +} + func (a diffConfig) IncludeTests() bool { return a.includeTests } diff --git a/pkg/app/run.go b/pkg/app/run.go index b438e80a8..60726d9df 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -51,7 +51,13 @@ func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurre releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, concurrency, helmfileCommand, opts) if len(errs) > 0 { - return nil, fmt.Errorf("%v", errs) + if !opts.AllowPartialErrors { + // abort on first error + return nil, fmt.Errorf("%v", errs) + } else { + // return partial results with errors for the failed ones + return releaseToChart, fmt.Errorf("%v", errs) + } } return releaseToChart, nil @@ -90,8 +96,11 @@ func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepare } releaseToChart, err := r.prepareChartsIfNeeded(helmfileCommand, dir, opts.Concurrency, opts) - if err != nil { - return err + // IMPORTANT: on opts.AllowPartialErrors: do not abort on error here, just forward it to the caller in order to allow for partial results + if !opts.AllowPartialErrors { + if err != nil { + return err + } } for i := range r.state.Releases { @@ -115,8 +124,17 @@ func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepare f() - _, err = r.state.TriggerGlobalCleanupEvent(helmfileCommand) - return err + _, cleanupErr := r.state.TriggerGlobalCleanupEvent(helmfileCommand) + if !opts.AllowPartialErrors { + // return directly on first error + return cleanupErr + } else { + // merge the two errors into a single error output + if err != nil || cleanupErr != nil { + return fmt.Errorf("prepare charts error: %v; cleanup error: %v", err, cleanupErr) + } + return nil + } } func (r *Run) Deps(c DepsConfigProvider) []error { diff --git a/pkg/config/global.go b/pkg/config/global.go index fe9061954..97e7af932 100644 --- a/pkg/config/global.go +++ b/pkg/config/global.go @@ -34,6 +34,8 @@ type GlobalOptions struct { SkipDeps bool // SkipRefresh is true if the running "helm repo update" should be skipped SkipRefresh bool + // AllowPartialErrors is true if partial errors during release processing are allowed + AllowPartialErrors bool // StripArgsValuesOnExitError is true if the ARGS output on exit error should be suppressed StripArgsValuesOnExitError bool // DisableForceUpdate is true if force updating repos is not desirable when executing "helm repo add" @@ -181,6 +183,11 @@ func (g *GlobalImpl) SkipRefresh() bool { return g.GlobalOptions.SkipRefresh } +// AllowPartialErrors return if partial errors during release processing are allowed +func (g *GlobalImpl) AllowPartialErrors() bool { + return g.GlobalOptions.AllowPartialErrors +} + // StripArgsValuesOnExitError return if the ARGS output on exit error should be suppressed func (g *GlobalImpl) StripArgsValuesOnExitError() bool { return g.GlobalOptions.StripArgsValuesOnExitError diff --git a/pkg/state/state.go b/pkg/state/state.go index 03f22ed4b..e697d49fd 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1231,12 +1231,13 @@ func filterReleasesForBuild(releases []ReleaseSpec) []ReleaseSpec { } type ChartPrepareOptions struct { - ForceDownload bool - SkipRepos bool - SkipDeps bool - SkipRefresh bool - SkipResolve bool - SkipCleanup bool + ForceDownload bool + SkipRepos bool + SkipDeps bool + SkipRefresh bool + SkipResolve bool + SkipCleanup bool + AllowPartialErrors bool // Validate is a helm-3-only option. When it is set to true, it configures chartify to pass --validate to helm-template run by it. // It's required when one of your chart relies on Capabilities.APIVersions in a template Validate bool @@ -1568,7 +1569,12 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre if downloadRes.err != nil { errs = append(errs, downloadRes.err) - return + if !opts.AllowPartialErrors { + return + } else { + // continue processing other releases even if one fails + continue + } } func() { prepareChartInfoMutex.Lock() @@ -1588,7 +1594,15 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre ) if len(errs) > 0 { - return nil, errs + if !opts.AllowPartialErrors { + return nil, errs + } else { + // continue processing other releases even if one fails, return partial results with errors for the failed ones + st.logger.Warnf("Some charts failed to prepare:\n") + for _, err := range errs { + st.logger.Warnf(" - %v\n", err) + } + } } if len(builds) > 0 { From 3c46ed90fee0ea623e5166184cb04a72b5d92ffd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Nov 2025 08:32:48 +0800 Subject: [PATCH 09/10] build(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.31.16 to 1.31.17 (#2245) build(deps): bump github.com/aws/aws-sdk-go-v2/config Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.31.16 to 1.31.17. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/changelog-template.json) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.31.16...config/v1.31.17) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.31.17 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 412dd9e67..c9214a5d3 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( dario.cat/mergo v1.0.2 github.com/Masterminds/semver/v3 v3.4.0 github.com/Masterminds/sprig/v3 v3.3.0 - github.com/aws/aws-sdk-go-v2/config v1.31.16 + github.com/aws/aws-sdk-go-v2/config v1.31.17 github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/go-test/deep v1.1.1 @@ -145,8 +145,8 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.20 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.21 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.9 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect @@ -159,9 +159,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 // indirect github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 // indirect github.com/aws/smithy-go v1.23.2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect diff --git a/go.sum b/go.sum index f00c7b0da..9e471bb4e 100644 --- a/go.sum +++ b/go.sum @@ -144,12 +144,12 @@ github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+X github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y= -github.com/aws/aws-sdk-go-v2/config v1.31.16 h1:E4Tz+tJiPc7kGnXwIfCyUj6xHJNpENlY11oKpRTgsjc= -github.com/aws/aws-sdk-go-v2/config v1.31.16/go.mod h1:2S9hBElpCyGMifv14WxQ7EfPumgoeCPZUpuPX8VtW34= -github.com/aws/aws-sdk-go-v2/credentials v1.18.20 h1:KFndAnHd9NUuzikHjQ8D5CfFVO+bgELkmcGY8yAw98Q= -github.com/aws/aws-sdk-go-v2/credentials v1.18.20/go.mod h1:9mCi28a+fmBHSQ0UM79omkz6JtN+PEsvLrnG36uoUv0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 h1:VO3FIM2TDbm0kqp6sFNR0PbioXJb/HzCDW6NtIZpIWE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12/go.mod h1:6C39gB8kg82tx3r72muZSrNhHia9rjGkX7ORaS2GKNE= +github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= +github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= +github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= +github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.9 h1:Z1897HnnfLLgbs3pcUv8xLvtbai9TEfPUZfA0BFw968= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.9/go.mod h1:8oVESJIPBYGWdZhaHcIvTm7BnI6hbsR3ggKn0uyRMhk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= @@ -176,12 +176,12 @@ github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 h1:9PWl450XOG+m5lKv+ github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6/go.mod h1:hwt7auGsDcaNQ8pzLgE2kCNyIWouYlAKSjuUu5Dqr7I= github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1 h1:TFg6XiS7EsHN0/jpV3eVNczZi/sPIVP5jxIs+euIESQ= github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1/go.mod h1:OIezd9K0sM/64DDP4kXx/i0NdgXu6R5KE6SCsIPJsjc= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 h1:xHXvxst78wBpJFgDW07xllOx0IAzbryrSdM4nMVQ4Dw= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.0/go.mod h1:/e8m+AO6HNPPqMyfKRtzZ9+mBF5/x1Wk8QiDva4m07I= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 h1:tBw2Qhf0kj4ZwtsVpDiVRU3zKLvjvjgIjHMKirxXg8M= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4/go.mod h1:Deq4B7sRM6Awq/xyOBlxBdgW8/Z926KYNNaGMW2lrkA= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 h1:C+BRMnasSYFcgDw8o9H5hzehKzXyAb9GY5v/8bP9DUY= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.0/go.mod h1:4EjU+4mIx6+JqKQkruye+CaigV7alL3thVPfDd9VlMs= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= +github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= +github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= From 6bf6ba897c30239f86bfaa74c3100f0f63f1c383 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:26:48 +0000 Subject: [PATCH 10/10] build(deps): bump golang.org/x/term from 0.36.0 to 0.37.0 Bumps [golang.org/x/term](https://github.com/golang/term) from 0.36.0 to 0.37.0. - [Commits](https://github.com/golang/term/compare/v0.36.0...v0.37.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c9214a5d3..ea0e54d61 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( go.yaml.in/yaml/v2 v2.4.3 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/sync v0.17.0 - golang.org/x/term v0.36.0 + golang.org/x/term v0.37.0 helm.sh/helm/v3 v3.19.0 k8s.io/apimachinery v0.34.1 ) @@ -93,7 +93,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.44.0 // indirect golang.org/x/oauth2 v0.31.0 // indirect - golang.org/x/sys v0.37.0 // indirect + golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.13.0 // indirect google.golang.org/api v0.252.0 // indirect diff --git a/go.sum b/go.sum index 9e471bb4e..04933dce5 100644 --- a/go.sum +++ b/go.sum @@ -829,14 +829,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=