From 2394cbfa88b5e3263e6df51f08d6e569328a8577 Mon Sep 17 00:00:00 2001 From: lczyk Date: Sat, 10 Oct 2026 03:13:41 +0100 Subject: [PATCH] fix: prefer ignores paths listed for other architectures Selection.Prefers walked every path in the selected slices regardless of the "arch" field, so a package listing a path only for other architectures still won the "prefer" relationship on the requested one. The losing package then skipped the path during extraction and nothing provided it, without any error. The selection now records the architecture it was made for and Prefers skips listings that do not apply to it. --- internal/setup/setup.go | 9 ++++++- internal/setup/setup_test.go | 28 ++++++++++++++++++++++ internal/slicer/slicer_test.go | 44 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/internal/setup/setup.go b/internal/setup/setup.go index 10953ada..f8e86f94 100644 --- a/internal/setup/setup.go +++ b/internal/setup/setup.go @@ -153,6 +153,9 @@ func (s *Slice) String() string { return s.Package + "_" + s.Name } type Selection struct { Release *Release Slices []*Slice + // Arch is the architecture the selection was made for. Paths listed for + // other architectures do not take part in it. + Arch string } // Prefers uses the prefer relationships and returns a map from each path to @@ -166,7 +169,10 @@ func (s *Selection) Prefers() (map[string]*Package, error) { pathPreferredPkg := make(map[string]*Package) for _, slice := range s.Slices { - for path := range slice.Contents { + for path, info := range slice.Contents { + if len(info.Arch) > 0 && s.Arch != "" && !slices.Contains(info.Arch, s.Arch) { + continue + } _, hasPrefers := prefers[preferKey{preferSource, path, ""}] if !hasPrefers { continue @@ -482,6 +488,7 @@ func Select(release *Release, slices []SliceKey, arch string) (*Selection, error selection := &Selection{ Release: release, + Arch: arch, } sorted, err := order(release.Packages, slices, arch) diff --git a/internal/setup/setup_test.go b/internal/setup/setup_test.go index ec61490b..4b742fba 100644 --- a/internal/setup/setup_test.go +++ b/internal/setup/setup_test.go @@ -428,6 +428,7 @@ var setupTests = []setupTest{{ }, selslices: []setup.SliceKey{{"mypkg1", "myslice1"}}, selection: &setup.Selection{ + Arch: "amd64", Slices: []*setup.Slice{{ Package: "mypkg1", Name: "myslice1", @@ -451,6 +452,7 @@ var setupTests = []setupTest{{ }, selslices: []setup.SliceKey{{"mypkg2", "myslice2"}}, selection: &setup.Selection{ + Arch: "amd64", Slices: []*setup.Slice{{ Package: "mypkg1", Name: "myslice1", @@ -1891,6 +1893,7 @@ var setupTests = []setupTest{{ }, selslices: []setup.SliceKey{{"mypkg", "myslice"}}, selection: &setup.Selection{ + Arch: "amd64", Slices: []*setup.Slice{{ Package: "mypkg", Name: "myslice", @@ -2729,6 +2732,31 @@ var setupTests = []setupTest{{ "/path": "mypkg2", "/link": "mypkg1", }, +}, { + summary: "Path 'prefer' ignores packages listing the path for another arch", + selslices: []setup.SliceKey{ + {"mypkg1", "myslice1"}, + {"mypkg2", "myslice1"}, + }, + input: map[string]string{ + "slices/mydir/mypkg1.yaml": ` + package: mypkg1 + slices: + myslice1: + contents: + /path: {arch: arm64} + `, + "slices/mydir/mypkg2.yaml": ` + package: mypkg2 + slices: + myslice1: + contents: + /path: {prefer: mypkg1} + `, + }, + prefers: map[string]string{ + "/path": "mypkg2", + }, }, { summary: "Cannot specify same package in 'prefer'", input: map[string]string{ diff --git a/internal/slicer/slicer_test.go b/internal/slicer/slicer_test.go index d6ef9ca0..abb3a926 100644 --- a/internal/slicer/slicer_test.go +++ b/internal/slicer/slicer_test.go @@ -1868,6 +1868,50 @@ var slicerTests = []slicerTest{{ "/link": "symlink /file1 {test-package1_myslice}", "/text": "file 0644 2c26b46b {test-package1_myslice}", }, +}, { + summary: "Preferred package lists the path only for another arch", + arch: "i386", + slices: []setup.SliceKey{ + {"test-package1", "myslice"}, + {"test-package2", "myslice"}, + }, + pkgs: []*testutil.TestPackage{{ + Name: "test-package1", + Data: testutil.MustMakeDeb([]testutil.TarEntry{ + testutil.Dir(0755, "./"), + testutil.Reg(0644, "./file", "foo"), + }), + }, { + Name: "test-package2", + Data: testutil.MustMakeDeb([]testutil.TarEntry{ + testutil.Dir(0755, "./"), + testutil.Reg(0644, "./file", "bar"), + }), + }}, + release: map[string]string{ + "slices/mydir/test-package1.yaml": ` + package: test-package1 + slices: + myslice: + contents: + /file: {arch: amd64} + `, + "slices/mydir/test-package2.yaml": ` + package: test-package2 + slices: + myslice: + contents: + /file: {prefer: test-package1} + `, + }, + // test-package1 does not list /file for i386, so test-package2 is the + // only package providing it and must extract it. + filesystem: map[string]string{ + "/file": "file 0644 fcde2b2e", + }, + manifestPaths: map[string]string{ + "/file": "file 0644 fcde2b2e {test-package2_myslice}", + }, }, { summary: "Warning when implicit parent directories conflict", slices: []setup.SliceKey{