From fb78ba3d40f06e23598691783e3764f9cc093545 Mon Sep 17 00:00:00 2001 From: asmit27rai Date: Tue, 6 May 2025 02:16:44 +0530 Subject: [PATCH] feat(oci): support OCI index with duplicate filenames by deduplicating pulled files - Add \dedupePulledFiles\ helper in oci/oci.go to rename colliding files in a pull directory - Invoke \DedupePulledFiles\ after each OCI pull in downloader/downloader.go - Ensures multiple KCL packages with the same basename (e.g. main.k) do not overwrite one another Signed-off-by: asmit27rai --- pkg/downloader/downloader.go | 79 +++++++++++++++++++++--------------- pkg/oci/oci.go | 43 ++++++++++++++++++++ 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/pkg/downloader/downloader.go b/pkg/downloader/downloader.go index a24c81b7..87fc6aab 100644 --- a/pkg/downloader/downloader.go +++ b/pkg/downloader/downloader.go @@ -459,7 +459,11 @@ func (d *OciDownloader) Download(opts *DownloadOptions) error { if utils.IsTar(cacheTarPath) { err = utils.UnTarDir(cacheTarPath, localFullPath) } else { - err = utils.ExtractTarball(cacheTarPath, localFullPath) + if err = utils.ExtractTarball(cacheTarPath, localFullPath); err == nil { + if derr := oci.DedupePulledFiles(localFullPath); derr != nil { + return derr + } + } } if err != nil { return err @@ -485,7 +489,12 @@ func (d *OciDownloader) Download(opts *DownloadOptions) error { if utils.IsTar(tarPath) { err = utils.UnTarDir(tarPath, localPath) } else { - err = utils.ExtractTarball(tarPath, localPath) + // after extracting multiple packages, dedupe any same-named files + if err = utils.ExtractTarball(tarPath, localPath); err == nil { + if derr := oci.DedupePulledFiles(localPath); derr != nil { + return derr + } + } } if err != nil { return fmt.Errorf("failed to untar the kcl package tar from '%s' into '%s'", tarPath, localPath) @@ -499,39 +508,45 @@ func (d *OciDownloader) Download(opts *DownloadOptions) error { } } } - } else if !opts.Offline { - reporter.ReportMsgTo( - fmt.Sprintf( - "downloading '%s:%s' from '%s/%s:%s'", - ociSource.Repo, ociSource.Tag, ociSource.Reg, ociSource.Repo, ociSource.Tag, - ), - opts.LogWriter, - ) + } else if !opts.Offline { + reporter.ReportMsgTo( + fmt.Sprintf( + "downloading '%s:%s' from '%s/%s:%s'", + ociSource.Repo, ociSource.Tag, ociSource.Reg, ociSource.Repo, ociSource.Tag, + ), + opts.LogWriter, + ) - err = ociCli.Pull(localPath, ociSource.Tag) - if err != nil { - return err - } - tarPath, err := utils.FindPkgArchive(localPath) - if err != nil { - return err - } - if utils.IsTar(tarPath) { - err = utils.UnTarDir(tarPath, localPath) - } else { - err = utils.ExtractTarball(tarPath, localPath) - } - if err != nil { - return fmt.Errorf("failed to untar the kcl package tar from '%s' into '%s'", tarPath, localPath) - } + // pull & extract + err = ociCli.Pull(localPath, ociSource.Tag) + if err != nil { + return err + } + tarPath, err := utils.FindPkgArchive(localPath) + if err != nil { + return err + } + if utils.IsTar(tarPath) { + err = utils.UnTarDir(tarPath, localPath) + } else { + // after extracting multiple packages, dedupe any same-named files + if err = utils.ExtractTarball(tarPath, localPath); err == nil { + if derr := oci.DedupePulledFiles(localPath); derr != nil { + return derr + } + } + } + if err != nil { + return fmt.Errorf("failed to untar the kcl package tar from '%s' into '%s'", tarPath, localPath) + } - // After untar the downloaded kcl package tar file, remove the tar file. - if utils.DirExists(tarPath) { - rmErr := os.Remove(tarPath) - if rmErr != nil { - return fmt.Errorf("failed to remove the downloaded kcl package tar file '%s'", tarPath) + // After untar the downloaded kcl package tar file, remove the tar file. + if utils.DirExists(tarPath) { + rmErr := os.Remove(tarPath) + if rmErr != nil { + return fmt.Errorf("failed to remove the downloaded kcl package tar file '%s'", tarPath) + } } - } } diff --git a/pkg/oci/oci.go b/pkg/oci/oci.go index e7e6dd0e..c86264af 100644 --- a/pkg/oci/oci.go +++ b/pkg/oci/oci.go @@ -268,6 +268,49 @@ func (ociClient *OciClient) Pull(localPath, tag string) error { ) } + if err := DedupePulledFiles(localPath); err != nil { + return reporter.NewErrorEvent(reporter.FailedGetPkg, err, "failed to dedupe pulled OCI files") + } + + return nil +} + +// DedupePulledFiles renames any files in dir that share the same name by appending +// a numeric suffix (e.g. pkg.kcl, pkg-2.kcl, pkg-3.kcl). +func DedupePulledFiles(dir string) error { + entries, err := os.ReadDir(dir) + if err != nil { + return err + } + + // count how many of each name we have + counts := make(map[string]int) + for _, e := range entries { + if e.IsDir() { + continue + } + counts[e.Name()]++ + } + + // for any name that appears more than once, rename them with -1, -2, … + seen := make(map[string]int) + for _, e := range entries { + if e.IsDir() { + continue + } + name := e.Name() + if counts[name] > 1 { + seen[name]++ + ext := filepath.Ext(name) + base := strings.TrimSuffix(name, ext) + newName := fmt.Sprintf("%s-%d%s", base, seen[name], ext) + oldPath := filepath.Join(dir, name) + newPath := filepath.Join(dir, newName) + if err := os.Rename(oldPath, newPath); err != nil { + return err + } + } + } return nil }