feat: Ensure repo update is only run once (#1310)

By default `helm dep build` will fetch repo updates before building the
chart. For helmfile this means for every chart with remote dependencies,
another repo update is performed. This isn't necessary as the cache only
needs to be refreshed once.

By performing a `helm repo update` before running any `helm dep`, it is
safe to pass `--skip-refresh` to all `helm dep build` commands as the
cache will be up-to-date.

A few notes to be aware of for this change:
* If there are **no remote dependencies** then this will lead to an
  extra refresh that could be unnecessary. However, a single repo update
  is usually quite fast and can still be skipped with a manual
  `--skip-refresh`.
* There are no tests for this new behaviour yet
* I think even this single update might be unnecessary as any initial
  `helm repo add --force-update` that is normally performed anyway and
  should already refresh all repos. However, I wasn't confident enough
  to simply force this flag at all times and considered this the safer
  option.

Resolves #1310

Signed-off-by: javex <code@inexplicity.de>
This commit is contained in:
javex 2025-08-02 19:04:47 +09:30 committed by Florian Ruechel
parent c354768e60
commit 4c72814bfa
No known key found for this signature in database
1 changed files with 15 additions and 2 deletions

View File

@ -1484,7 +1484,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
} }
if len(builds) > 0 { if len(builds) > 0 {
if err := st.runHelmDepBuilds(helm, concurrency, builds); err != nil { if err := st.runHelmDepBuilds(helm, concurrency, builds, opts); err != nil {
return nil, []error{err} return nil, []error{err}
} }
} }
@ -1493,7 +1493,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
} }
// nolint: unparam // nolint: unparam
func (st *HelmState) runHelmDepBuilds(helm helmexec.Interface, concurrency int, builds []*chartPrepareResult) error { func (st *HelmState) runHelmDepBuilds(helm helmexec.Interface, concurrency int, builds []*chartPrepareResult, opts ChartPrepareOptions) error {
// NOTES: // NOTES:
// 1. `helm dep build` fails when it was run concurrency on the same chart. // 1. `helm dep build` fails when it was run concurrency on the same chart.
// To avoid that, we run `helm dep build` only once per each local chart. // To avoid that, we run `helm dep build` only once per each local chart.
@ -1504,7 +1504,20 @@ func (st *HelmState) runHelmDepBuilds(helm helmexec.Interface, concurrency int,
// //
// See https://github.com/roboll/helmfile/issues/1521 // See https://github.com/roboll/helmfile/issues/1521
// Perform an update of repos once before running `helm dep build` so that we
// can safely pass --skip-refresh to the command to avoid doing a repo update
// for every iteration of the loop where charts have external dependencies.
if len(builds) > 0 && !opts.SkipRefresh {
if err := helm.UpdateRepo(); err != nil {
return fmt.Errorf("updating repo: %w", err)
}
}
for _, r := range builds { for _, r := range builds {
// Never update the local repository cache here, since we've already
// updated it before entering the loop
r.skipRefresh = true
buildDepsFlags := getBuildDepsFlags(r) buildDepsFlags := getBuildDepsFlags(r)
if err := helm.BuildDeps(r.releaseName, r.chartPath, buildDepsFlags...); err != nil { if err := helm.BuildDeps(r.releaseName, r.chartPath, buildDepsFlags...); err != nil {
if r.chartFetchedByGoGetter { if r.chartFetchedByGoGetter {