diff --git a/buildmodule.go b/buildmodule.go index b7a2b45..86ad5bf 100644 --- a/buildmodule.go +++ b/buildmodule.go @@ -18,8 +18,10 @@ package libpak import ( "fmt" + "net/url" "os" "reflect" + "runtime" "sort" "strconv" "strings" @@ -501,6 +503,15 @@ func (d *DependencyResolver) Resolve(id string, version string) (BuildModuleDepe return BuildModuleDependency{}, fmt.Errorf("unable to parse version %s\n%w", c.Version, err) } + // filter out deps that do not match the current running architecture + arch, err := archFromPURL(c.PURL) + if err != nil { + return BuildModuleDependency{}, fmt.Errorf("unable to compare arch\n%w", err) + } + if arch != archFromSystem() { + continue + } + if c.ID == id && vc.Check(v) && d.contains(c.Stacks, d.StackID) { candidates = append(candidates, c) } @@ -529,6 +540,33 @@ func (d *DependencyResolver) Resolve(id string, version string) (BuildModuleDepe return candidate, nil } +func archFromPURL(rawPURL string) (string, error) { + if len(strings.TrimSpace(rawPURL)) == 0 { + return "amd64", nil + } + + purl, err := url.Parse(rawPURL) + if err != nil { + return "", fmt.Errorf("unable to parse PURL\n%w", err) + } + + queryParams := purl.Query() + if arch, ok := queryParams["arch"]; ok { + return arch[0], nil + } + + return archFromSystem(), nil +} + +func archFromSystem() string { + archFromEnv, ok := os.LookupEnv("BP_ARCH") + if !ok { + archFromEnv = runtime.GOARCH + } + + return archFromEnv +} + func (DependencyResolver) contains(candidates []string, value string) bool { if len(candidates) == 0 { return true diff --git a/buildmodule_test.go b/buildmodule_test.go index 6c8aa22..786c466 100644 --- a/buildmodule_test.go +++ b/buildmodule_test.go @@ -283,6 +283,10 @@ func testBuildpack(t *testing.T, context spec.G, it spec.S) { resolver libpak.DependencyResolver ) + it.Before(func() { + t.Setenv("BP_ARCH", "amd64") // force for test consistency + }) + context("Resolve", func() { it("filters by id", func() { resolver.Dependencies = []libpak.BuildModuleDependency{ @@ -315,6 +319,69 @@ func testBuildpack(t *testing.T, context spec.G, it spec.S) { })) }) + it("filters by arch", func() { + resolver.Dependencies = []libpak.BuildModuleDependency{ + { + ID: "test-id-1", + Name: "test-name", + Version: "1.0", + URI: "test-uri-amd64", + SHA256: "test-sha256", + Stacks: []string{"test-stack-1", "test-stack-2"}, + PURL: "pkg:generic/bellsoft-jdk@8.0.382?arch=amd64", + }, + { + ID: "test-id-1", + Name: "test-name", + Version: "1.0", + URI: "test-uri-arm64", + SHA256: "test-sha256", + Stacks: []string{"test-stack-1", "test-stack-2"}, + PURL: "pkg:generic/bellsoft-jdk@8.0.382?arch=arm64", + }, + } + resolver.StackID = "test-stack-1" + + t.Setenv("BP_ARCH", "arm64") + + Expect(resolver.Resolve("test-id-1", "1.0")).To(Equal(libpak.BuildModuleDependency{ + ID: "test-id-1", + Name: "test-name", + Version: "1.0", + URI: "test-uri-arm64", + SHA256: "test-sha256", + Stacks: []string{"test-stack-1", "test-stack-2"}, + PURL: "pkg:generic/bellsoft-jdk@8.0.382?arch=arm64", + })) + }) + + it("filters by arch where arch should match any", func() { + resolver.Dependencies = []libpak.BuildModuleDependency{ + { + ID: "test-id-1", + Name: "test-name", + Version: "1.0", + URI: "test-uri", + SHA256: "test-sha256", + Stacks: []string{"test-stack-1", "test-stack-2"}, + PURL: "pkg:generic/spring-cloud-bindings@1.2.3", + }, + } + resolver.StackID = "test-stack-1" + + t.Setenv("BP_ARCH", "arm64") + + Expect(resolver.Resolve("test-id-1", "1.0")).To(Equal(libpak.BuildModuleDependency{ + ID: "test-id-1", + Name: "test-name", + Version: "1.0", + URI: "test-uri", + SHA256: "test-sha256", + Stacks: []string{"test-stack-1", "test-stack-2"}, + PURL: "pkg:generic/spring-cloud-bindings@1.2.3", + })) + }) + it("filters by version constraint", func() { resolver.Dependencies = []libpak.BuildModuleDependency{ {