diff --git a/internal/commands/build.go b/internal/commands/build.go index 2118a0444..c80123d77 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -27,6 +27,7 @@ type BuildFlags struct { Publish bool ClearCache bool TrustBuilder bool + TrustExtraBuildpacks bool Interactive bool Sparse bool DockerHost string @@ -180,8 +181,9 @@ func Build(logger logging.Logger, cfg config.Config, packClient PackClient) *cob TrustBuilder: func(string) bool { return trustBuilder }, - Buildpacks: buildpacks, - Extensions: extensions, + TrustExtraBuildpacks: flags.TrustExtraBuildpacks, + Buildpacks: buildpacks, + Extensions: extensions, ContainerConfig: client.ContainerConfig{ Network: flags.Network, Volumes: flags.Volumes, @@ -273,6 +275,7 @@ This option may set DOCKER_HOST environment variable for the build container if cmd.Flags().StringVar(&buildFlags.RunImage, "run-image", "", "Run image (defaults to default stack's run image)") cmd.Flags().StringSliceVarP(&buildFlags.AdditionalTags, "tag", "t", nil, "Additional tags to push the output image to.\nTags should be in the format 'image:tag' or 'repository/image:tag'."+stringSliceHelp("tag")) cmd.Flags().BoolVar(&buildFlags.TrustBuilder, "trust-builder", false, "Trust the provided builder.\nAll lifecycle phases will be run in a single container.\nFor more on trusted builders, and when to trust or untrust a builder, check out our docs here: https://buildpacks.io/docs/tools/pack/concepts/trusted_builders") + cmd.Flags().BoolVar(&buildFlags.TrustExtraBuildpacks, "trust-extra-buildpacks", false, "Trust buildpacks that are provided in addition to the buildpacks on the builder") cmd.Flags().StringArrayVar(&buildFlags.Volumes, "volume", nil, "Mount host volume into the build container, in the form ':[:]'.\n- 'host path': Name of the volume or absolute directory path to mount.\n- 'target path': The path where the file or directory is available in the container.\n- 'options' (default \"ro\"): An optional comma separated list of mount options.\n - \"ro\", volume contents are read-only.\n - \"rw\", volume contents are readable and writeable.\n - \"volume-opt==\", can be specified more than once, takes a key-value pair consisting of the option name and its value."+stringArrayHelp("volume")) cmd.Flags().StringVar(&buildFlags.Workspace, "workspace", "", "Location at which to mount the app dir in the build image") cmd.Flags().IntVar(&buildFlags.GID, "gid", 0, `Override GID of user's group in the stack's build and run images. The provided value must be a positive number`) diff --git a/pkg/client/build.go b/pkg/client/build.go index 73b1f455b..901c0865a 100644 --- a/pkg/client/build.go +++ b/pkg/client/build.go @@ -203,9 +203,17 @@ type BuildOptions struct { // TrustBuilder when true optimizes builds by running // all lifecycle phases in a single container. // This places registry credentials on the builder's build image. - // Only trust builders from reputable sources. + // Only trust builders from reputable sources. The optimized + // build happens only when both builder and buildpacks are + // trusted TrustBuilder IsTrustedBuilder + // TrustExtraBuildpacks when true optimizes builds by running + // all lifecycle phases in a single container. The optimized + // build happens only when both builder and buildpacks are + // trusted + TrustExtraBuildpacks bool + // Directory to output any SBOM artifacts SBOMDestinationDir string @@ -430,16 +438,17 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error { // Get the platform API version to use lifecycleVersion := bldr.LifecycleDescriptor().Info.Version useCreator := supportsCreator(lifecycleVersion) && opts.TrustBuilder(opts.Builder) - hasAdditionalModules := func() bool { - if len(fetchedBPs) == 0 && len(fetchedExs) == 0 { - return false - } - if len(fetchedBPs) == nInlineBPs && len(fetchedExs) == 0 { - return false - } - return true + hasAdditionalBuildpacks := func() bool { + return len(fetchedBPs) != nInlineBPs }() - if useCreator && hasAdditionalModules { + hasExtensions := func() bool { + return len(fetchedExs) != 0 + }() + if hasExtensions { + c.logger.Warnf("Builder is trusted but additional modules were added; using the untrusted (5 phases) build flow") + useCreator = false + } + if hasAdditionalBuildpacks && !opts.TrustExtraBuildpacks { c.logger.Warnf("Builder is trusted but additional modules were added; using the untrusted (5 phases) build flow") useCreator = false } diff --git a/pkg/client/build_test.go b/pkg/client/build_test.go index 9b2dcd798..b645421f8 100644 --- a/pkg/client/build_test.go +++ b/pkg/client/build_test.go @@ -2091,6 +2091,28 @@ api = "0.2" }) when("additional buildpacks were added", func() { + it("uses creator when additional buildpacks are provided and TrustExtraBuildpacks is set", func() { + additionalBP := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{ + WithAPI: api.MustParse("0.3"), + WithInfo: dist.ModuleInfo{ + ID: "buildpack.add.1.id", + Version: "buildpack.add.1.version", + }, + WithStacks: []dist.Stack{{ID: defaultBuilderStackID}}, + WithOrder: nil, + }) + + h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{ + Image: "some/app", + Builder: defaultBuilderName, + Publish: true, + TrustBuilder: func(string) bool { return true }, + TrustExtraBuildpacks: true, + Buildpacks: []string{additionalBP}, + })) + h.AssertEq(t, fakeLifecycle.Opts.UseCreator, true) + }) + it("uses the 5 phases with the lifecycle image", func() { additionalBP := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{ WithAPI: api.MustParse("0.3"),