From fdf9d6a23b89c6a0a206b142536fbcc02d245720 Mon Sep 17 00:00:00 2001 From: Momin Huzaifa Date: Fri, 22 May 2026 16:08:01 +0530 Subject: [PATCH 1/2] feat(downloader): add oci digest tracking support for helm v4 dependencies - Update `pkg/chart/interfaces.go` and `dependency.go` to support dynamic dependency digest accessors. - Expand internal v3 specifications to parse and propagate immutable asset checksums. - Modify engine routing logic in `pkg/downloader/manager.go` to construct container image references via specific digests during URL mapping. - Introduce package naming alias (`pkgchart`) in the downloader manager to cleanly sidestep v2/v4 structural namespace collisions. - Include verification logic and coverage updates inside `manager_test.go`. --- internal/chart/v3/dependency.go | 3 +++ pkg/chart/dependency.go | 9 +++++++++ pkg/chart/interfaces.go | 1 + pkg/downloader/manager.go | 9 +++++++-- pkg/downloader/manager_test.go | 6 +++--- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/chart/v3/dependency.go b/internal/chart/v3/dependency.go index 50ee5552e..4e34a213e 100644 --- a/internal/chart/v3/dependency.go +++ b/internal/chart/v3/dependency.go @@ -36,6 +36,8 @@ type Dependency struct { // Appending `index.yaml` to this string should result in a URL that can be // used to fetch the repository index. Repository string `json:"repository" yaml:"repository"` + // Digest + Digest string `json: "digest,omitempty" yaml: "digest,omitempty"` // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` // Tags can be used to group charts for enabling/disabling together @@ -59,6 +61,7 @@ func (d *Dependency) Validate() error { d.Name = sanitizeString(d.Name) d.Version = sanitizeString(d.Version) d.Repository = sanitizeString(d.Repository) + d.Digest = sanitizeString(d.Digest) d.Condition = sanitizeString(d.Condition) for i := range d.Tags { d.Tags[i] = sanitizeString(d.Tags[i]) diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go index 864fe6d2c..b58195af6 100644 --- a/pkg/chart/dependency.go +++ b/pkg/chart/dependency.go @@ -62,3 +62,12 @@ func (r *v3DependencyAccessor) Name() string { func (r *v3DependencyAccessor) Alias() string { return r.dep.Alias } + +func (r v2DependencyAccessor) Digest() string { + // helm v2 doesn't use digest, return empty sting + return "" +} + +func (r v3DependencyAccessor) Digest() string { + return r.dep.Digest +} diff --git a/pkg/chart/interfaces.go b/pkg/chart/interfaces.go index 6d94ad3ea..0f9f38ecb 100644 --- a/pkg/chart/interfaces.go +++ b/pkg/chart/interfaces.go @@ -41,4 +41,5 @@ type Accessor interface { type DependencyAccessor interface { Name() string Alias() string + Digest() string } diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 16459229d..9269878e9 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -43,6 +43,7 @@ import ( "helm.sh/helm/v4/pkg/helmpath" "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/repo/v1" + pkgchart "helm.sh/helm/v4/pkg/chart" ) // ErrRepoNotFound indicates that chart repositories can't be found in local repo cache. @@ -315,7 +316,8 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { // Any failure to resolve/download a chart should fail: // https://github.com/helm/helm/issues/1439 - churl, username, password, insecureSkipTLSVerify, passCredentialsAll, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) + depAccessor, _ := pkgchart.NewDependencyAccessor(dep) + churl, username, password, insecureSkipTLSVerify, passCredentialsAll, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, depAccessor.Digest(), dep.Repository, repos) if err != nil { saveError = fmt.Errorf("could not find %s: %w", churl, err) break @@ -725,8 +727,11 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { // repoURL is the repository to search // // If it finds a URL that is "relative", it will prepend the repoURL. -func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureSkipTLSVerify, passCredentialsAll bool, caFile, certFile, keyFile string, err error) { +func (m *Manager) findChartURL(name, version, digest, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureSkipTLSVerify, passCredentialsAll bool, caFile, certFile, keyFile string, err error) { if registry.IsOCI(repoURL) { + if digest != ""{ + return fmt.Sprintf("%s/%s@%s", repoURL, name, digest), "", "", false, false, "", "", "", nil + } return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", nil } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 9e27f183f..be5d77188 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -71,7 +71,7 @@ func TestFindChartURL(t *testing.T) { version := "0.1.0" repoURL := "http://example.com/charts" - churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err := m.findChartURL(name, version, repoURL, repos) + churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err := m.findChartURL(name, version, "", repoURL, repos) if err != nil { t.Fatal(err) } @@ -96,7 +96,7 @@ func TestFindChartURL(t *testing.T) { version = "1.2.3" repoURL = "https://example-https-insecureskiptlsverify.com" - churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, repoURL, repos) + churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, "", repoURL, repos) if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestFindChartURL(t *testing.T) { version = "1.2.3" repoURL = "http://example.com/helm" - churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, repoURL, repos) + churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, "", repoURL, repos) if err != nil { t.Fatal(err) } From d3eb0cd285fbb40a82ae8de50b8a9e0d69ac8117 Mon Sep 17 00:00:00 2001 From: Momin Huzaifa Date: Fri, 22 May 2026 16:27:26 +0530 Subject: [PATCH 2/2] fixed malformed spaces inside tag --- internal/chart/v3/dependency.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/chart/v3/dependency.go b/internal/chart/v3/dependency.go index 4e34a213e..ae8ed45e3 100644 --- a/internal/chart/v3/dependency.go +++ b/internal/chart/v3/dependency.go @@ -37,7 +37,7 @@ type Dependency struct { // used to fetch the repository index. Repository string `json:"repository" yaml:"repository"` // Digest - Digest string `json: "digest,omitempty" yaml: "digest,omitempty"` + Digest string `json:"digest,omitempty" yaml:"digest,omitempty"` // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` // Tags can be used to group charts for enabling/disabling together