diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 00c8c56e8..fc70c5914 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -149,12 +149,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven } } - name := filepath.Base(u.Path) - if u.Scheme == registry.OCIScheme { - idx := strings.LastIndexByte(name, ':') - name = fmt.Sprintf("%s-%s.tgz", name[:idx], name[idx+1:]) - } - + name := c.getChartName(u.String()) destfile := filepath.Join(dest, name) if err := fileutil.AtomicWriteFile(destfile, data, 0644); err != nil { return destfile, nil, err @@ -353,11 +348,12 @@ func (c *ChartDownloader) DownloadToCache(ref, version string) (string, *provena // // TODO: support OCI hash func (c *ChartDownloader) ResolveChartVersion(ref, version string) (string, *url.URL, error) { - u, err := url.Parse(ref) + u, err := c.parseChartURL(ref, version) if err != nil { - return "", nil, fmt.Errorf("invalid chart URL format: %s", ref) + return "", u, err } + if registry.IsOCI(u.String()) { if c.RegistryClient == nil { return "", nil, fmt.Errorf("unable to lookup ref %s at version '%s', missing registry client", ref, version) @@ -574,6 +570,65 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, return nil, ErrNoOwnerRepo } +func (c *ChartDownloader) getChartName(url string) string { + name := filepath.Base(url) + if registry.IsOCI(url) { + idx := strings.LastIndexByte(name, ':') + name = fmt.Sprintf("%s-%s.tgz", name[:idx], name[idx+1:]) + } + + return name +} + +func (c *ChartDownloader) parseChartURL(ref string, version string) (*url.URL, error) { + u, err := url.Parse(ref) + if err != nil { + return nil, errors.Errorf("invalid chart URL format: %s", ref) + } + + if registry.IsOCI(u.String()) { + tag, err := c.getOciTag(ref, version) + + if err != nil { + return nil, err + } + + u.Path = fmt.Sprintf("%s:%s", u.Path, tag) + } + + return u, nil +} + +func (c *ChartDownloader) getOciTag(ref, version string) (string, error) { + var tag string + + // Evaluate whether an explicit version has been provided. Otherwise, determine version to use + _, errSemVer := semver.NewVersion(version) + if errSemVer == nil { + tag = version + } else { + // Retrieve list of repository tags + tags, err := c.RegistryClient.Tags(strings.TrimPrefix(ref, fmt.Sprintf("%s://", registry.OCIScheme))) + if err != nil { + return "", err + } + if len(tags) == 0 { + return "", errors.Errorf("Unable to locate any tags in provided repository: %s", ref) + } + + // Determine if version provided + // If empty, try to get the highest available tag + // If exact version, try to find it + // If semver constraint string, try to find a match + tag, err = registry.GetTagMatchingVersionOrConstraint(tags, version) + if err != nil { + return "", err + } + } + + return tag, nil +} + func loadRepoConfig(file string) (*repo.File, error) { r, err := repo.LoadFile(file) if err != nil && !errors.Is(err, fs.ErrNotExist) { diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 3bb776473..fd3ce694f 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -328,16 +328,14 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { continue } - if m.SkipDownloadIfExists { - name := filepath.Base(churl) - if _, err = os.Stat(filepath.Join(destPath, name)); err == nil { - fmt.Fprintf(m.Out, "Already exists locally %s from repo %s\n", dep.Name, dep.Repository) - continue + version := "" + if registry.IsOCI(churl) { + churl, version, err = parseOCIRef(churl) + if err != nil { + return errors.Wrapf(err, "could not parse OCI reference") } } - fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) - dl := ChartDownloader{ Out: m.Out, Verify: m.Verify, @@ -355,7 +353,6 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { }, } - version := "" if registry.IsOCI(churl) { churl, version, err = parseOCIRef(churl) if err != nil { @@ -366,6 +363,21 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { getter.WithTagName(version)) } + if m.SkipDownloadIfExists { + u, err := dl.parseChartURL(churl, version) + + if err != nil { + return err + } + + name := dl.getChartName(u.String()) + if _, err = os.Stat(filepath.Join(destPath, name)); err == nil { + fmt.Fprintf(m.Out, "Already exists locally %s from repo %s\n", dep.Name, dep.Repository) + continue + } + } + + fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil { saveError = fmt.Errorf("could not download %s: %w", churl, err) break