diff --git a/internal/builder/trusted_builder.go b/internal/builder/trusted_builder.go index 6e3923ba2..d5e7261c1 100644 --- a/internal/builder/trusted_builder.go +++ b/internal/builder/trusted_builder.go @@ -1,113 +1,92 @@ package builder import ( + "slices" + "github.com/google/go-containerregistry/pkg/name" "github.com/buildpacks/pack/internal/config" ) -type KnownBuilder struct { +// TrustedBuilders is a flat list of builder image names that are trusted by default. +var TrustedBuilders = []string{ + "gcr.io/buildpacks/builder:google-22", + "heroku/builder:24", + "heroku/builder:22", + "heroku/builder:20", + "paketobuildpacks/builder-jammy-base", + "paketobuildpacks/builder-jammy-full", + "paketobuildpacks/builder-jammy-tiny", + "paketobuildpacks/builder-jammy-buildpackless-static", + "paketobuildpacks/ubuntu-noble-builder", + "paketobuildpacks/builder-ubi8-base", + "paketobuildpacks/ubi-9-builder", + "paketobuildpacks/ubi-10-builder", +} + +// SuggestedBuilder contains display metadata for recommended builders. +type SuggestedBuilder struct { Vendor string Image string DefaultDescription string - Suggested bool - Trusted bool } -var KnownBuilders = []KnownBuilder{ +// SuggestedBuilders is the list of builders shown by `builder suggest`. +var SuggestedBuilders = []SuggestedBuilder{ { Vendor: "Google", Image: "gcr.io/buildpacks/builder:google-22", DefaultDescription: "Ubuntu 22.04 base image with buildpacks for .NET, Dart, Go, Java, Node.js, PHP, Python, and Ruby", - Suggested: true, - Trusted: true, }, { Vendor: "Heroku", Image: "heroku/builder:24", DefaultDescription: "Ubuntu 24.04 AMD64+ARM64 base image with buildpacks for Go, Java, Node.js, PHP, Python, Ruby & Scala.", - Suggested: true, - Trusted: true, - }, - { - Vendor: "Heroku", - Image: "heroku/builder:22", - DefaultDescription: "Ubuntu 22.04 AMD64 base image with buildpacks for Go, Java, Node.js, PHP, Python, Ruby & Scala.", - Suggested: false, - Trusted: true, - }, - { - Vendor: "Heroku", - Image: "heroku/builder:20", - DefaultDescription: "Ubuntu 20.04 AMD64 base image with buildpacks for Go, Java, Node.js, PHP, Python, Ruby & Scala.", - Suggested: false, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/builder-jammy-base", DefaultDescription: "Small base image with buildpacks for Java, Node.js, Golang, .NET Core, Python & Ruby", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/builder-jammy-full", DefaultDescription: "Larger base image with buildpacks for Java, Node.js, Golang, .NET Core, Python, Ruby, & PHP", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/builder-jammy-tiny", DefaultDescription: "Tiny base image (jammy build image, distroless run image) with buildpacks for Golang & Java", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/builder-jammy-buildpackless-static", DefaultDescription: "Static base image (jammy build image, distroless run image) suitable for static binaries like Go or Rust", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/ubuntu-noble-builder", DefaultDescription: "Small base image with buildpacks for Java, Node.js or .NET Core", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/builder-ubi8-base", DefaultDescription: "Universal Base Image (RHEL8) with buildpacks to build Node.js or Java runtimes. Support also the new extension feature (aka apply Dockerfile)", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/ubi-9-builder", DefaultDescription: "Universal Base Image (RHEL9) with buildpacks to build Node.js runtimes.", - Suggested: true, - Trusted: true, }, { Vendor: "Paketo Buildpacks", Image: "paketobuildpacks/ubi-10-builder", DefaultDescription: "Universal Base Image (RHEL10) with buildpacks to build Node.js runtimes.", - Suggested: true, - Trusted: true, }, } func IsKnownTrustedBuilder(builderName string) bool { - for _, knownBuilder := range KnownBuilders { - if builderName == knownBuilder.Image && knownBuilder.Trusted { - return true - } - } - return false + return slices.Contains(TrustedBuilders, builderName) } func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) { @@ -115,11 +94,23 @@ func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) { if err != nil { return false, err } + + // Collect all trusted builder names + trustedBuilderNames := make([]string, len(TrustedBuilders)) + copy(trustedBuilderNames, TrustedBuilders) + + // Add user-configured trusted builders for _, trustedBuilder := range cfg.TrustedBuilders { - trustedBuilderReference, err := name.ParseReference(trustedBuilder.Name, name.WithDefaultTag("")) + trustedBuilderNames = append(trustedBuilderNames, trustedBuilder.Name) + } + + // Check if builder matches any trusted builder + for _, trustedBuilderName := range trustedBuilderNames { + trustedBuilderReference, err := name.ParseReference(trustedBuilderName, name.WithDefaultTag("")) if err != nil { return false, err } + if trustedBuilderReference.Identifier() != "" { if builderReference.Name() == trustedBuilderReference.Name() { return true, nil @@ -130,5 +121,6 @@ func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) { } } } + return false, nil } diff --git a/internal/builder/trusted_builder_test.go b/internal/builder/trusted_builder_test.go index 90e7599cd..acbbf6be2 100644 --- a/internal/builder/trusted_builder_test.go +++ b/internal/builder/trusted_builder_test.go @@ -20,6 +20,16 @@ func TestTrustedBuilder(t *testing.T) { } func trustedBuilder(t *testing.T, when spec.G, it spec.S) { + when("SuggestedBuilders", func() { + it("are all trusted", func() { + for _, suggestedBuilder := range bldr.SuggestedBuilders { + isTrusted, err := bldr.IsTrustedBuilder(config.Config{}, suggestedBuilder.Image) + h.AssertNil(t, err) + h.AssertTrue(t, isTrusted) + } + }) + }) + when("IsKnownTrustedBuilder", func() { it("matches exactly", func() { h.AssertTrue(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base")) @@ -30,6 +40,23 @@ func trustedBuilder(t *testing.T, when spec.G, it spec.S) { }) when("IsTrustedBuilder", func() { + it("trusts known trusted builders", func() { + // Known builder with exact tag match + isTrusted, err := bldr.IsTrustedBuilder(config.Config{}, "heroku/builder:24") + h.AssertNil(t, err) + h.AssertTrue(t, isTrusted) + + // Known builder without tag should match any tag + isTrusted, err = bldr.IsTrustedBuilder(config.Config{}, "paketobuildpacks/builder-jammy-base:latest") + h.AssertNil(t, err) + h.AssertTrue(t, isTrusted) + + // Unknown builder should not be trusted + isTrusted, err = bldr.IsTrustedBuilder(config.Config{}, "my/private/builder") + h.AssertNil(t, err) + h.AssertFalse(t, isTrusted) + }) + it("trust image without tag", func() { cfg := config.Config{ TrustedBuilders: []config.TrustedBuilder{ diff --git a/internal/commands/build.go b/internal/commands/build.go index d3db7a69b..2b75bcbb5 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -121,7 +121,7 @@ func Build(logger logging.Logger, cfg config.Config, packClient PackClient) *cob if err != nil { return err } - trustBuilder := isTrusted || bldr.IsKnownTrustedBuilder(builder) || flags.TrustBuilder + trustBuilder := isTrusted || flags.TrustBuilder if trustBuilder { logger.Debugf("Builder %s is trusted", style.Symbol(builder)) if flags.LifecycleImage != "" { diff --git a/internal/commands/builder_inspect_test.go b/internal/commands/builder_inspect_test.go index 9e4189bf7..636ed289a 100644 --- a/internal/commands/builder_inspect_test.go +++ b/internal/commands/builder_inspect_test.go @@ -215,6 +215,25 @@ func testBuilderInspectCommand(t *testing.T, when spec.G, it spec.S) { }) }) + when("image is a known trusted builder", func() { + it("passes builder info with trusted true to the writer's `Print` method", func() { + builderWriter := newDefaultBuilderWriter() + + command := commands.BuilderInspect( + logger, + config.Config{}, + newDefaultBuilderInspector(), + newWriterFactory(returnsForWriter(builderWriter)), + ) + command.SetArgs([]string{"heroku/builder:24"}) + + err := command.Execute() + assert.Nil(err) + + assert.Equal(builderWriter.ReceivedBuilderInfo.Trusted, true) + }) + }) + when("default builder is configured and is the same as specified by the command", func() { it("passes builder info with isDefault true to the writer's `Print` method", func() { cfg.DefaultBuilder = "the/default-builder" diff --git a/internal/commands/builder_suggest_test.go b/internal/commands/builder_suggest_test.go index 75a9db7af..f12bb0b16 100644 --- a/internal/commands/builder_suggest_test.go +++ b/internal/commands/builder_suggest_test.go @@ -47,7 +47,7 @@ func testSuggestCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays descriptions from metadata", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", @@ -65,7 +65,7 @@ func testSuggestCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays default descriptions", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", @@ -81,7 +81,7 @@ func testSuggestCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays default descriptions", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", diff --git a/internal/commands/config_trusted_builder.go b/internal/commands/config_trusted_builder.go index 36bcfb601..df5e72ed5 100644 --- a/internal/commands/config_trusted_builder.go +++ b/internal/commands/config_trusted_builder.go @@ -55,7 +55,7 @@ func addTrustedBuilder(args []string, logger logging.Logger, cfg config.Config, if err != nil { return err } - if isTrusted || bldr.IsKnownTrustedBuilder(imageName) { + if isTrusted { logger.Infof("Builder %s is already trusted", style.Symbol(imageName)) return nil } @@ -103,12 +103,8 @@ func removeTrustedBuilder(args []string, logger logging.Logger, cfg config.Confi } func getTrustedBuilders(cfg config.Config) []string { - var trustedBuilders []string - for _, knownBuilder := range bldr.KnownBuilders { - if knownBuilder.Trusted { - trustedBuilders = append(trustedBuilders, knownBuilder.Image) - } - } + trustedBuilders := make([]string, len(bldr.TrustedBuilders)) + copy(trustedBuilders, bldr.TrustedBuilders) for _, builder := range cfg.TrustedBuilders { trustedBuilders = append(trustedBuilders, builder.Name) diff --git a/internal/commands/config_trusted_builder_test.go b/internal/commands/config_trusted_builder_test.go index 2def80e7e..c78f77387 100644 --- a/internal/commands/config_trusted_builder_test.go +++ b/internal/commands/config_trusted_builder_test.go @@ -85,7 +85,7 @@ func testTrustedBuilderCommand(t *testing.T, when spec.G, it spec.S) { when("list", func() { var args = []string{"list"} - it("shows suggested builders and locally trusted builder in alphabetical order", func() { + it("shows known and locally trusted builders in alphabetical order", func() { builderName := "great-builder-" + h.RandString(8) command.SetArgs(args) @@ -171,7 +171,7 @@ func testTrustedBuilderCommand(t *testing.T, when spec.G, it spec.S) { }) }) - when("builder is a suggested builder", func() { + when("builder is a known trusted builder", func() { it("does nothing", func() { h.AssertNil(t, os.WriteFile(configPath, []byte(""), os.ModePerm)) @@ -268,7 +268,7 @@ func testTrustedBuilderCommand(t *testing.T, when spec.G, it spec.S) { }) }) - when("builder is a suggested builder", func() { + when("builder is a known trusted builder", func() { it("does nothing and reports that ", func() { builder := "paketobuildpacks/builder-jammy-base" command := commands.ConfigTrustedBuilder(logger, config.Config{}, configPath) diff --git a/internal/commands/list_trusted_builders_test.go b/internal/commands/list_trusted_builders_test.go index f07b447e7..6539d3641 100644 --- a/internal/commands/list_trusted_builders_test.go +++ b/internal/commands/list_trusted_builders_test.go @@ -57,7 +57,7 @@ func testListTrustedBuildersCommand(t *testing.T, when spec.G, it spec.S) { h.AssertContains(t, outBuf.String(), "Trusted Builders:") }) - it("shows suggested builders and locally trusted builder in alphabetical order", func() { + it("shows known and locally trusted builders in alphabetical order", func() { builderName := "great-builder-" + h.RandString(8) h.AssertNil(t, command.Execute()) diff --git a/internal/commands/suggest_builders.go b/internal/commands/suggest_builders.go index b02808616..5deff2258 100644 --- a/internal/commands/suggest_builders.go +++ b/internal/commands/suggest_builders.go @@ -39,16 +39,10 @@ func suggestSettingBuilder(logger logging.Logger, inspector BuilderInspector) { } func suggestBuilders(logger logging.Logger, client BuilderInspector) { - suggestedBuilders := []bldr.KnownBuilder{} - for _, knownBuilder := range bldr.KnownBuilders { - if knownBuilder.Suggested { - suggestedBuilders = append(suggestedBuilders, knownBuilder) - } - } - WriteSuggestedBuilder(logger, client, suggestedBuilders) + WriteSuggestedBuilder(logger, client, bldr.SuggestedBuilders) } -func WriteSuggestedBuilder(logger logging.Logger, inspector BuilderInspector, builders []bldr.KnownBuilder) { +func WriteSuggestedBuilder(logger logging.Logger, inspector BuilderInspector, builders []bldr.SuggestedBuilder) { sort.Slice(builders, func(i, j int) bool { if builders[i].Vendor == builders[j].Vendor { return builders[i].Image < builders[j].Image @@ -66,7 +60,7 @@ func WriteSuggestedBuilder(logger logging.Logger, inspector BuilderInspector, bu wg.Add(len(builders)) for i, builder := range builders { - go func(w *sync.WaitGroup, i int, builder bldr.KnownBuilder) { + go func(w *sync.WaitGroup, i int, builder bldr.SuggestedBuilder) { descriptions[i] = getBuilderDescription(builder, inspector) w.Done() }(&wg, i, builder) @@ -84,7 +78,7 @@ func WriteSuggestedBuilder(logger logging.Logger, inspector BuilderInspector, bu logger.Info("\tpack builder inspect ") } -func getBuilderDescription(builder bldr.KnownBuilder, inspector BuilderInspector) string { +func getBuilderDescription(builder bldr.SuggestedBuilder, inspector BuilderInspector) string { info, err := inspector.InspectBuilder(builder.Image, false) if err == nil && info != nil && info.Description != "" { return info.Description diff --git a/internal/commands/suggest_builders_test.go b/internal/commands/suggest_builders_test.go index 12d38c16e..6759e21de 100644 --- a/internal/commands/suggest_builders_test.go +++ b/internal/commands/suggest_builders_test.go @@ -47,7 +47,7 @@ func testSuggestBuildersCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays descriptions from metadata", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", @@ -65,7 +65,7 @@ func testSuggestBuildersCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays default descriptions", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", @@ -81,7 +81,7 @@ func testSuggestBuildersCommand(t *testing.T, when spec.G, it spec.S) { }) it("displays default descriptions", func() { - commands.WriteSuggestedBuilder(logger, mockClient, []bldr.KnownBuilder{{ + commands.WriteSuggestedBuilder(logger, mockClient, []bldr.SuggestedBuilder{{ Vendor: "Builder", Image: "gcr.io/some/builder:latest", DefaultDescription: "Default description", diff --git a/internal/commands/trust_builder_test.go b/internal/commands/trust_builder_test.go index 747099956..e33e446bb 100644 --- a/internal/commands/trust_builder_test.go +++ b/internal/commands/trust_builder_test.go @@ -83,7 +83,7 @@ func testTrustBuilderCommand(t *testing.T, when spec.G, it spec.S) { }) }) - when("builder is a suggested builder", func() { + when("builder is a known trusted builder", func() { it("does nothing", func() { h.AssertNil(t, os.WriteFile(configPath, []byte(""), os.ModePerm)) diff --git a/internal/commands/untrust_builder_test.go b/internal/commands/untrust_builder_test.go index f903b8b60..587fd50b5 100644 --- a/internal/commands/untrust_builder_test.go +++ b/internal/commands/untrust_builder_test.go @@ -122,7 +122,7 @@ func testUntrustBuilderCommand(t *testing.T, when spec.G, it spec.S) { }) }) - when("builder is a suggested builder", func() { + when("builder is a known trusted builder", func() { it("does nothing and reports that ", func() { builder := "paketobuildpacks/builder-jammy-base" command := commands.UntrustBuilder(logger, config.Config{}, configPath)