Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 38 additions & 52 deletions internal/builder/trusted_builder.go
Original file line number Diff line number Diff line change
@@ -1,132 +1,117 @@
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:26",
"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:26",
DefaultDescription: "Ubuntu 26.04 AMD64+ARM64 base image with buildpacks for .NET, Go, Java, Node.js, Python & Scala",
Suggested: false,
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) {
builderReference, err := name.ParseReference(builderName, name.WithDefaultTag(""))
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
Expand All @@ -137,5 +122,6 @@ func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) {
}
}
}

return false, nil
}
27 changes: 27 additions & 0 deletions internal/builder/trusted_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand All @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
6 changes: 3 additions & 3 deletions internal/commands/builder_suggest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
10 changes: 3 additions & 7 deletions internal/commands/config_trusted_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions internal/commands/config_trusted_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,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)
Expand Down Expand Up @@ -175,7 +175,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))

Expand Down Expand Up @@ -272,7 +272,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)
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/list_trusted_builders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
14 changes: 4 additions & 10 deletions internal/commands/suggest_builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -84,7 +78,7 @@ func WriteSuggestedBuilder(logger logging.Logger, inspector BuilderInspector, bu
logger.Info("\tpack builder inspect <builder-image>")
}

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
Expand Down
6 changes: 3 additions & 3 deletions internal/commands/suggest_builders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/trust_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
2 changes: 1 addition & 1 deletion internal/commands/untrust_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading