diff --git a/cmd/chisel/cmd_cut.go b/cmd/chisel/cmd_cut.go index 2a00b718..02fc9eda 100644 --- a/cmd/chisel/cmd_cut.go +++ b/cmd/chisel/cmd_cut.go @@ -91,6 +91,7 @@ func (cmd *cmdCut) Execute(args []string) error { Suites: archiveInfo.Suites, Components: archiveInfo.Components, CacheDir: cache.DefaultDir("chisel"), + Priority: archiveInfo.Priority, }) if err != nil { return err diff --git a/internal/archive/archive.go b/internal/archive/archive.go index 48777606..97ee208f 100644 --- a/internal/archive/archive.go +++ b/internal/archive/archive.go @@ -34,6 +34,7 @@ type Options struct { Suites []string Components []string CacheDir string + Priority int } func Open(options *Options) (Archive, error) { diff --git a/internal/setup/setup.go b/internal/setup/setup.go index 6f5ffda8..6bc3c2cc 100644 --- a/internal/setup/setup.go +++ b/internal/setup/setup.go @@ -29,6 +29,7 @@ type Archive struct { Version string Suites []string Components []string + Priority int } // Package holds a collection of slices that represent parts of themselves. @@ -321,6 +322,7 @@ type yamlArchive struct { Version string `yaml:"version"` Suites []string `yaml:"suites"` Components []string `yaml:"components"` + Priority int `yaml:"priority"` } type yamlPackage struct { @@ -427,6 +429,7 @@ func parseRelease(baseDir, filePath string, data []byte) (*Release, error) { Version: details.Version, Suites: details.Suites, Components: details.Components, + Priority: details.Priority, } } diff --git a/internal/slicer/slicer.go b/internal/slicer/slicer.go index fd4f1f4c..70c49e08 100644 --- a/internal/slicer/slicer.go +++ b/internal/slicer/slicer.go @@ -5,8 +5,10 @@ import ( "bytes" "fmt" "io" + "math" "os" "path/filepath" + "sort" "strings" "syscall" @@ -69,13 +71,48 @@ func Run(options *RunOptions) error { targetDirAbs = filepath.Join(dir, targetDir) } + // Archives are ordered in descending order by priority. The + // default priority is 0 and the maximum allowed priority is + // the upper limit of int. + // + // If a package to be downloaded exists in archive A and + // archive B, and archive A has higher priority than archive + // B, the package is downloaded from archive A even if archive + // B has a newer version. + // + // If two or more archives have the same priority, the most + // recent version of the package from all of them is selected, + // if any. Otherwise, archives with lower priority are tried, + // if any. + // + // If two or more archives with the same priority contain the + // package with the same version, the first archive from the + // options.Archives iterator is used. In effect, the choice is + // random. + orderedArchives := make([]archive.Archive, 0, len(options.Archives)) + for _, archive := range options.Archives { + orderedArchives = append(orderedArchives, archive) + } + sort.Slice(orderedArchives, func(a, b int) bool { + prioA := orderedArchives[a].Options().Priority + prioB := orderedArchives[b].Options().Priority + return prioA > prioB + }) + // Build information to process the selection. for _, slice := range options.Selection.Slices { extractPackage := extract[slice.Package] if extractPackage == nil { var selectedVersion string var selectedArchive archive.Archive - for _, currentArchive := range options.Archives { + currentPrio := math.MaxInt + for _, currentArchive := range orderedArchives { + if prio := currentArchive.Options().Priority; prio < currentPrio { + if selectedVersion != "" { + break + } + currentPrio = prio + } pkgInfo := currentArchive.Info(slice.Package) if pkgInfo == nil { continue diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index acc4a093..2cb611f3 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -687,6 +687,92 @@ var slicerTests = []slicerTest{{ "/speed/cheetah": "file 0644 e98b0879", "/speed/ostrich": "file 0644 c8fa2806", }, +}, { + summary: "Pick package from archive with highest priority", + pkgs: map[string]map[string]testPackage{ + "edge": { + "hello": testPackage{ + info: map[string]string{ + "Version": "2.0-beta", + }, + content: testutil.MakeTestDeb([]testutil.TarEntry{ + DIR(0755, "./"), + REG(0644, "./hello", "Hello, The Edge\n"), + }), + }, + }, + "candidate": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.8", + }, + content: testutil.MakeTestDeb([]testutil.TarEntry{ + DIR(0755, "./"), + REG(0644, "./hello", "Hello, The Candidate\n"), + }), + }, + }, + "stable": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.2", + }, + content: testutil.MakeTestDeb([]testutil.TarEntry{ + DIR(0755, "./"), + REG(0644, "./hello", "Hello, The Stable\n"), + }), + }, + }, + "obsolete": { + "hello": testPackage{ + info: map[string]string{ + "Version": "1.0", + }, + content: testutil.MakeTestDeb([]testutil.TarEntry{ + DIR(0755, "./"), + REG(0644, "./hello", "Hello, The Obsolete\n"), + }), + }, + }, + }, + release: map[string]string{ + "chisel.yaml": ` + format: chisel-v1 + archives: + edge: + version: 1 + suites: [main] + components: [main] + candidate: + version: 1 + suites: [main] + components: [main] + priority: 5 + stable: + version: 1 + suites: [main] + components: [main] + priority: 10 + obsolete: + version: 1 + suites: [main] + components: [main] + priority: 10 + `, + "slices/mydir/hello.yaml": ` + package: hello + slices: + all: + contents: + /hello: + `, + }, + slices: []setup.SliceKey{ + {"hello", "all"}, + }, + result: map[string]string{ + "/hello": "file 0644 b5621b65", + }, }} const defaultChiselYaml = ` @@ -792,6 +878,7 @@ func (s *S) TestRun(c *C) { Version: setupArchive.Version, Suites: setupArchive.Suites, Components: setupArchive.Components, + Priority: setupArchive.Priority, Arch: test.arch, }, pkgs: archivePkgs,