From e27537da0f752b2e9b91096cf53407cdc14de175 Mon Sep 17 00:00:00 2001 From: Mike Brancato Date: Fri, 27 Jun 2025 10:14:04 +0100 Subject: [PATCH] Pass pkg pins from meta package to installable package --- pkg/build/lock.go | 63 +++++++++++++++++++++++++++--------------- pkg/build/lock_test.go | 30 ++++++++++++++++++++ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/pkg/build/lock.go b/pkg/build/lock.go index 902494a69..c97f8a841 100644 --- a/pkg/build/lock.go +++ b/pkg/build/lock.go @@ -20,6 +20,7 @@ import ( "maps" "reflect" "regexp" + "slices" "sort" "strings" @@ -267,7 +268,7 @@ func unify(originals []string, inputs []resolved) (map[string][]string, map[stri // Allocate a list sufficient for holding all of our locked package versions // as well as the packages we were unable to lock. - pl := make([]string, 0, len(acc.versions)+missing.Len()) + allPl := make([]string, 0, len(acc.versions)+missing.Len()) // Append any missing packages with their original constraints coming in. // NOTE: the originalPackages "versions" includes the remainder of the @@ -275,42 +276,29 @@ func unify(originals []string, inputs []resolved) (map[string][]string, map[stri for _, pkg := range sets.List(missing) { if ver := originalPackages.versions[pkg]; ver != "" { if pin := originalPackages.versions[pkg]; pin != "" { - pl = append(pl, fmt.Sprintf("%s%s%s", pkg, ver, pin)) + allPl = append(allPl, fmt.Sprintf("%s%s%s", pkg, ver, pin)) } else { - pl = append(pl, fmt.Sprintf("%s%s", pkg, ver)) + allPl = append(allPl, fmt.Sprintf("%s%s", pkg, ver)) } } else { - pl = append(pl, pkg) + allPl = append(allPl, pkg) } } - // Append all of the resolved and unified packages with an exact match - // based on the resolved version we found. - for _, pkg := range sets.List(acc.packages) { - pkgName := fmt.Sprintf("%s=%s", pkg, acc.versions[pkg]) - if pin := originalPackages.pinned[pkg]; pin != "" { - pkgName = fmt.Sprintf("%s%s", pkgName, pin) - } - pl = append(pl, pkgName) - } + allPl = slices.Concat(setPkgNames(acc, originalPackages), allPl) + // Sort the package list explicitly with the `=` included. // This is because (foo, foo-bar) sorts differently than (foo=1, foo-bar=1) // due to the presence or absence of the `=` character. - sort.Strings(pl) + sort.Strings(allPl) // "index" is a sentinel value for the intersectino of all architectures. // This is a reference to the OCI image index we'll be producing with it. - byArch["index"] = pl + byArch["index"] = allPl for _, input := range inputs { - pl := make([]string, 0, len(input.packages)) - for _, pkg := range sets.List(input.packages) { - pkgName := fmt.Sprintf("%s=%s", pkg, input.versions[pkg]) - if pin := originalPackages.pinned[pkg]; pin != "" { - pkgName = fmt.Sprintf("%s%s", pkgName, pin) - } - pl = append(pl, pkgName) - } + pl := setPkgNames(input, originalPackages) + // Sort the package list explicitly with the `=` included. // This is because (foo, foo-bar) sorts differently than (foo=1, foo-bar=1) // due to the presence or absence of the `=` character. @@ -334,5 +322,34 @@ func unify(originals []string, inputs []resolved) (map[string][]string, map[stri return byArch, nil, nil } +// setPkgNames returns a list of package names with their versions and pins +func setPkgNames(input resolved, original resolved) []string { + pl := make([]string, 0, len(input.packages)) + + // Append all the resolved and unified packages with an exact match based on the + // resolved version we found. + for _, pkg := range sets.List(input.packages) { + pkgName := fmt.Sprintf("%s=%s", pkg, input.versions[pkg]) + if pin := original.pinned[pkg]; pin != "" { + pkgName = fmt.Sprintf("%s%s", pkgName, pin) + } + + // If the package provides something (such as a meta package) that has been + // specified with a pin, then we populate the pin based on what it provides. + for _, prov := range input.provided[pkg].UnsortedList() { + if strings.IndexAny(prov, ":") >= 0 { + continue + } + if pin := original.pinned[prov]; pin != "" { + pkgName = fmt.Sprintf("%s%s", pkgName, pin) + break + } + } + + pl = append(pl, pkgName) + } + return pl +} + // Copied from go-apk's version.go var packageNameRegex = regexp.MustCompile(`^([^@=><~]+)(([=><~]+)([^@]+))?(@([a-zA-Z0-9]+))?$`) diff --git a/pkg/build/lock_test.go b/pkg/build/lock_test.go index 376da6578..0ca03841f 100644 --- a/pkg/build/lock_test.go +++ b/pkg/build/lock_test.go @@ -85,6 +85,36 @@ func TestUnify(t *testing.T) { "foo=1.2.3", }, }, + }, { + name: "locked and pinned parent meta-package", + originals: []string{"foo=1.2.3", "bar-parent=2.4.6@local", "baz=0.0.1"}, + inputs: []resolved{{ + arch: "amd64", + packages: sets.New("foo", "bar", "baz"), + versions: map[string]string{ + "foo": "1.2.3", + "bar": "2.4.6", + "baz": "0.0.1", + }, + pinned: map[string]string{ + "bar-parent": "@local", + }, + provided: map[string]sets.Set[string]{ + "bar": sets.New("bar-parent"), + }, + }}, + want: map[string][]string{ + "amd64": { + "bar=2.4.6@local", + "baz=0.0.1", + "foo=1.2.3", + }, + "index": { + "bar=2.4.6@local", + "baz=0.0.1", + "foo=1.2.3", + }, + }, }, { name: "transitive dependency", originals: []string{"foo", "bar", "baz"},