github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/client/build_test.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/sha256"
     7  	"encoding/hex"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/Masterminds/semver"
    19  	"github.com/buildpacks/imgutil"
    20  	"github.com/buildpacks/imgutil/fakes"
    21  	"github.com/buildpacks/imgutil/local"
    22  	"github.com/buildpacks/imgutil/remote"
    23  	"github.com/buildpacks/lifecycle/api"
    24  	"github.com/buildpacks/lifecycle/platform/files"
    25  	dockerclient "github.com/docker/docker/client"
    26  	"github.com/google/go-containerregistry/pkg/name"
    27  	"github.com/heroku/color"
    28  	"github.com/onsi/gomega/ghttp"
    29  	"github.com/sclevine/spec"
    30  	"github.com/sclevine/spec/report"
    31  
    32  	"github.com/buildpacks/pack/internal/builder"
    33  	cfg "github.com/buildpacks/pack/internal/config"
    34  	ifakes "github.com/buildpacks/pack/internal/fakes"
    35  	rg "github.com/buildpacks/pack/internal/registry"
    36  	"github.com/buildpacks/pack/internal/style"
    37  	"github.com/buildpacks/pack/pkg/blob"
    38  	"github.com/buildpacks/pack/pkg/buildpack"
    39  	"github.com/buildpacks/pack/pkg/dist"
    40  	"github.com/buildpacks/pack/pkg/image"
    41  	"github.com/buildpacks/pack/pkg/logging"
    42  	projectTypes "github.com/buildpacks/pack/pkg/project/types"
    43  	h "github.com/buildpacks/pack/testhelpers"
    44  )
    45  
    46  func TestBuild(t *testing.T) {
    47  	color.Disable(true)
    48  	defer color.Disable(false)
    49  	spec.Run(t, "build", testBuild, spec.Report(report.Terminal{}))
    50  }
    51  
    52  func testBuild(t *testing.T, when spec.G, it spec.S) {
    53  	var (
    54  		subject                      *Client
    55  		fakeImageFetcher             *ifakes.FakeImageFetcher
    56  		fakeLifecycle                *ifakes.FakeLifecycle
    57  		defaultBuilderStackID        = "some.stack.id"
    58  		defaultWindowsBuilderStackID = "some.windows.stack.id"
    59  		defaultBuilderImage          *fakes.Image
    60  		defaultWindowsBuilderImage   *fakes.Image
    61  		defaultBuilderName           = "example.com/default/builder:tag"
    62  		defaultWindowsBuilderName    = "example.com/windows-default/builder:tag"
    63  		defaultRunImageName          = "default/run"
    64  		defaultWindowsRunImageName   = "default/win-run"
    65  		fakeDefaultRunImage          *fakes.Image
    66  		fakeDefaultWindowsRunImage   *fakes.Image
    67  		fakeMirror1                  *fakes.Image
    68  		fakeMirror2                  *fakes.Image
    69  		tmpDir                       string
    70  		outBuf                       bytes.Buffer
    71  		logger                       *logging.LogWithWriters
    72  		fakeLifecycleImage           *fakes.Image
    73  
    74  		withExtensionsLabel bool
    75  	)
    76  
    77  	it.Before(func() {
    78  		var err error
    79  
    80  		fakeImageFetcher = ifakes.NewFakeImageFetcher()
    81  		fakeLifecycle = &ifakes.FakeLifecycle{}
    82  
    83  		tmpDir, err = os.MkdirTemp("", "build-test")
    84  		h.AssertNil(t, err)
    85  
    86  		defaultBuilderImage = newFakeBuilderImage(t, tmpDir, defaultBuilderName, defaultBuilderStackID, defaultRunImageName, builder.DefaultLifecycleVersion, newLinuxImage)
    87  		h.AssertNil(t, defaultBuilderImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "build:mixinB", "mixinX", "build:mixinY"]`))
    88  		fakeImageFetcher.LocalImages[defaultBuilderImage.Name()] = defaultBuilderImage
    89  		if withExtensionsLabel {
    90  			h.AssertNil(t, defaultBuilderImage.SetLabel("io.buildpacks.buildpack.order-extensions", `[{"group":[{"id":"some-extension-id","version":"some-extension-version"}]}]`))
    91  		}
    92  
    93  		defaultWindowsBuilderImage = newFakeBuilderImage(t, tmpDir, defaultWindowsBuilderName, defaultWindowsBuilderStackID, defaultWindowsRunImageName, builder.DefaultLifecycleVersion, newWindowsImage)
    94  		h.AssertNil(t, defaultWindowsBuilderImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "build:mixinB", "mixinX", "build:mixinY"]`))
    95  		fakeImageFetcher.LocalImages[defaultWindowsBuilderImage.Name()] = defaultWindowsBuilderImage
    96  		if withExtensionsLabel {
    97  			h.AssertNil(t, defaultWindowsBuilderImage.SetLabel("io.buildpacks.buildpack.order-extensions", `[{"group":[{"id":"some-extension-id","version":"some-extension-version"}]}]`))
    98  		}
    99  
   100  		fakeDefaultWindowsRunImage = newWindowsImage("default/win-run", "", nil)
   101  		h.AssertNil(t, fakeDefaultWindowsRunImage.SetLabel("io.buildpacks.stack.id", defaultWindowsBuilderStackID))
   102  		h.AssertNil(t, fakeDefaultWindowsRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "run:mixinC", "mixinX", "run:mixinZ"]`))
   103  		fakeImageFetcher.LocalImages[fakeDefaultWindowsRunImage.Name()] = fakeDefaultWindowsRunImage
   104  
   105  		fakeDefaultRunImage = newLinuxImage("default/run", "", nil)
   106  		h.AssertNil(t, fakeDefaultRunImage.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   107  		h.AssertNil(t, fakeDefaultRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "run:mixinC", "mixinX", "run:mixinZ"]`))
   108  		fakeImageFetcher.LocalImages[fakeDefaultRunImage.Name()] = fakeDefaultRunImage
   109  
   110  		fakeMirror1 = newLinuxImage("registry1.example.com/run/mirror", "", nil)
   111  		h.AssertNil(t, fakeMirror1.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   112  		h.AssertNil(t, fakeMirror1.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   113  		fakeImageFetcher.LocalImages[fakeMirror1.Name()] = fakeMirror1
   114  
   115  		fakeMirror2 = newLinuxImage("registry2.example.com/run/mirror", "", nil)
   116  		h.AssertNil(t, fakeMirror2.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   117  		h.AssertNil(t, fakeMirror2.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   118  		fakeImageFetcher.LocalImages[fakeMirror2.Name()] = fakeMirror2
   119  
   120  		fakeLifecycleImage = newLinuxImage(fmt.Sprintf("%s:%s", cfg.DefaultLifecycleImageRepo, builder.DefaultLifecycleVersion), "", nil)
   121  		fakeImageFetcher.LocalImages[fakeLifecycleImage.Name()] = fakeLifecycleImage
   122  
   123  		docker, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithVersion("1.38"))
   124  		h.AssertNil(t, err)
   125  
   126  		logger = logging.NewLogWithWriters(&outBuf, &outBuf)
   127  
   128  		dlCacheDir, err := os.MkdirTemp(tmpDir, "dl-cache")
   129  		h.AssertNil(t, err)
   130  
   131  		blobDownloader := blob.NewDownloader(logger, dlCacheDir)
   132  		buildpackDownloader := buildpack.NewDownloader(logger, fakeImageFetcher, blobDownloader, &registryResolver{logger: logger})
   133  		subject = &Client{
   134  			logger:              logger,
   135  			imageFetcher:        fakeImageFetcher,
   136  			downloader:          blobDownloader,
   137  			lifecycleExecutor:   fakeLifecycle,
   138  			docker:              docker,
   139  			buildpackDownloader: buildpackDownloader,
   140  		}
   141  	})
   142  
   143  	it.After(func() {
   144  		h.AssertNilE(t, defaultBuilderImage.Cleanup())
   145  		h.AssertNilE(t, fakeDefaultRunImage.Cleanup())
   146  		h.AssertNilE(t, fakeMirror1.Cleanup())
   147  		h.AssertNilE(t, fakeMirror2.Cleanup())
   148  		os.RemoveAll(tmpDir)
   149  		h.AssertNilE(t, fakeLifecycleImage.Cleanup())
   150  	})
   151  
   152  	when("#Build", func() {
   153  		when("Workspace option", func() {
   154  			it("uses the specified dir", func() {
   155  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   156  					Workspace: "app",
   157  					Builder:   defaultBuilderName,
   158  					Image:     "example.com/some/repo:tag",
   159  				}))
   160  				h.AssertEq(t, fakeLifecycle.Opts.Workspace, "app")
   161  			})
   162  		})
   163  
   164  		when("Image option", func() {
   165  			it("is required", func() {
   166  				h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
   167  					Image:   "",
   168  					Builder: defaultBuilderName,
   169  				}),
   170  					"invalid image name ''",
   171  				)
   172  			})
   173  
   174  			it("must be a valid image reference", func() {
   175  				h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
   176  					Image:   "not@valid",
   177  					Builder: defaultBuilderName,
   178  				}),
   179  					"invalid image name 'not@valid'",
   180  				)
   181  			})
   182  
   183  			it("must be a valid tag reference", func() {
   184  				h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
   185  					Image:   "registry.com/my/image@sha256:954e1f01e80ce09d0887ff6ea10b13a812cb01932a0781d6b0cc23f743a874fd",
   186  					Builder: defaultBuilderName,
   187  				}),
   188  					"invalid image name 'registry.com/my/image@sha256:954e1f01e80ce09d0887ff6ea10b13a812cb01932a0781d6b0cc23f743a874fd'",
   189  				)
   190  			})
   191  
   192  			it("lifecycle receives resolved reference", func() {
   193  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   194  					Builder: defaultBuilderName,
   195  					Image:   "example.com/some/repo:tag",
   196  				}))
   197  				h.AssertEq(t, fakeLifecycle.Opts.Image.Context().RegistryStr(), "example.com")
   198  				h.AssertEq(t, fakeLifecycle.Opts.Image.Context().RepositoryStr(), "some/repo")
   199  				h.AssertEq(t, fakeLifecycle.Opts.Image.Identifier(), "tag")
   200  			})
   201  		})
   202  
   203  		when("Quiet mode", func() {
   204  			var builtImage *fakes.Image
   205  
   206  			it.After(func() {
   207  				logger.WantQuiet(false)
   208  			})
   209  
   210  			when("publish", func() {
   211  				var remoteRunImage, builderWithoutLifecycleImageOrCreator *fakes.Image
   212  
   213  				it.Before(func() {
   214  					remoteRunImage = fakes.NewImage("default/run", "", nil)
   215  					h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   216  					h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   217  					fakeImageFetcher.RemoteImages[remoteRunImage.Name()] = remoteRunImage
   218  
   219  					builderWithoutLifecycleImageOrCreator = newFakeBuilderImage(
   220  						t,
   221  						tmpDir,
   222  						"example.com/supportscreator/builder:tag",
   223  						"some.stack.id",
   224  						defaultRunImageName,
   225  						"0.3.0",
   226  						newLinuxImage,
   227  					)
   228  					h.AssertNil(t, builderWithoutLifecycleImageOrCreator.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "build:mixinB", "mixinX", "build:mixinY"]`))
   229  					fakeImageFetcher.LocalImages[builderWithoutLifecycleImageOrCreator.Name()] = builderWithoutLifecycleImageOrCreator
   230  
   231  					digest, err := name.NewDigest("example.io/some/app@sha256:363c754893f0efe22480b4359a5956cf3bd3ce22742fc576973c61348308c2e4", name.WeakValidation)
   232  					h.AssertNil(t, err)
   233  					builtImage = fakes.NewImage("example.io/some/app:latest", "", remote.DigestIdentifier{Digest: digest})
   234  					fakeImageFetcher.RemoteImages[builtImage.Name()] = builtImage
   235  				})
   236  
   237  				it.After(func() {
   238  					h.AssertNilE(t, remoteRunImage.Cleanup())
   239  					h.AssertNilE(t, builderWithoutLifecycleImageOrCreator.Cleanup())
   240  					h.AssertNilE(t, builtImage.Cleanup())
   241  				})
   242  
   243  				it("only prints app name and sha", func() {
   244  					logger.WantQuiet(true)
   245  
   246  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   247  						Image:   "example.io/some/app",
   248  						Builder: defaultBuilderName,
   249  						AppPath: filepath.Join("testdata", "some-app"),
   250  						Publish: true,
   251  					}))
   252  
   253  					h.AssertEq(t, strings.TrimSpace(outBuf.String()), "example.io/some/app@sha256:363c754893f0efe22480b4359a5956cf3bd3ce22742fc576973c61348308c2e4")
   254  				})
   255  			})
   256  
   257  			when("local", func() {
   258  				it.Before(func() {
   259  					builtImage = fakes.NewImage("index.docker.io/some/app:latest", "", local.IDIdentifier{
   260  						ImageID: "363c754893f0efe22480b4359a5956cf3bd3ce22742fc576973c61348308c2e4",
   261  					})
   262  					fakeImageFetcher.LocalImages[builtImage.Name()] = builtImage
   263  				})
   264  
   265  				it.After(func() {
   266  					h.AssertNilE(t, builtImage.Cleanup())
   267  				})
   268  
   269  				it("only prints app name and sha", func() {
   270  					logger.WantQuiet(true)
   271  
   272  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   273  						Image:   "some/app",
   274  						Builder: defaultBuilderName,
   275  						AppPath: filepath.Join("testdata", "some-app"),
   276  					}))
   277  
   278  					h.AssertEq(t, strings.TrimSpace(outBuf.String()), "some/app@sha256:363c754893f0efe22480b4359a5956cf3bd3ce22742fc576973c61348308c2e4")
   279  				})
   280  			})
   281  		})
   282  
   283  		when("AppDir option", func() {
   284  			it("defaults to the current working directory", func() {
   285  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   286  					Image:   "some/app",
   287  					Builder: defaultBuilderName,
   288  				}))
   289  
   290  				wd, err := os.Getwd()
   291  				h.AssertNil(t, err)
   292  				resolvedWd, err := filepath.EvalSymlinks(wd)
   293  				h.AssertNil(t, err)
   294  				h.AssertEq(t, fakeLifecycle.Opts.AppPath, resolvedWd)
   295  			})
   296  			for fileDesc, appPath := range map[string]string{
   297  				"zip": filepath.Join("testdata", "zip-file.zip"),
   298  				"jar": filepath.Join("testdata", "jar-file.jar"),
   299  			} {
   300  				fileDesc := fileDesc
   301  				appPath := appPath
   302  
   303  				it(fmt.Sprintf("supports %s files", fileDesc), func() {
   304  					err := subject.Build(context.TODO(), BuildOptions{
   305  						Image:   "some/app",
   306  						Builder: defaultBuilderName,
   307  						AppPath: appPath,
   308  					})
   309  					h.AssertNil(t, err)
   310  				})
   311  			}
   312  
   313  			for fileDesc, testData := range map[string][]string{
   314  				"non-existent": {"not/exist/path", "does not exist"},
   315  				"empty":        {filepath.Join("testdata", "empty-file"), "app path must be a directory or zip"},
   316  				"non-zip":      {filepath.Join("testdata", "non-zip-file"), "app path must be a directory or zip"},
   317  			} {
   318  				fileDesc := fileDesc
   319  				appPath := testData[0]
   320  				errMessage := testData[0]
   321  
   322  				it(fmt.Sprintf("does NOT support %s files", fileDesc), func() {
   323  					err := subject.Build(context.TODO(), BuildOptions{
   324  						Image:   "some/app",
   325  						Builder: defaultBuilderName,
   326  						AppPath: appPath,
   327  					})
   328  
   329  					h.AssertError(t, err, errMessage)
   330  				})
   331  			}
   332  
   333  			it("resolves the absolute path", func() {
   334  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   335  					Image:   "some/app",
   336  					Builder: defaultBuilderName,
   337  					AppPath: filepath.Join("testdata", "some-app"),
   338  				}))
   339  				absPath, err := filepath.Abs(filepath.Join("testdata", "some-app"))
   340  				h.AssertNil(t, err)
   341  				h.AssertEq(t, fakeLifecycle.Opts.AppPath, absPath)
   342  			})
   343  
   344  			when("appDir is a symlink", func() {
   345  				var (
   346  					appDirName     = "some-app"
   347  					absoluteAppDir string
   348  					tmpDir         string
   349  					err            error
   350  				)
   351  
   352  				it.Before(func() {
   353  					tmpDir, err = os.MkdirTemp("", "build-symlink-test")
   354  					h.AssertNil(t, err)
   355  
   356  					appDirPath := filepath.Join(tmpDir, appDirName)
   357  					h.AssertNil(t, os.MkdirAll(filepath.Join(tmpDir, appDirName), 0666))
   358  
   359  					absoluteAppDir, err = filepath.Abs(appDirPath)
   360  					h.AssertNil(t, err)
   361  
   362  					absoluteAppDir, err = filepath.EvalSymlinks(appDirPath)
   363  					h.AssertNil(t, err)
   364  				})
   365  
   366  				it.After(func() {
   367  					os.RemoveAll(tmpDir)
   368  				})
   369  
   370  				it("resolves relative symbolic links", func() {
   371  					relLink := filepath.Join(tmpDir, "some-app.link")
   372  					h.AssertNil(t, os.Symlink(filepath.Join(".", appDirName), relLink))
   373  
   374  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   375  						Image:   "some/app",
   376  						Builder: defaultBuilderName,
   377  						AppPath: relLink,
   378  					}))
   379  
   380  					h.AssertEq(t, fakeLifecycle.Opts.AppPath, absoluteAppDir)
   381  				})
   382  
   383  				it("resolves absolute symbolic links", func() {
   384  					relLink := filepath.Join(tmpDir, "some-app.link")
   385  					h.AssertNil(t, os.Symlink(absoluteAppDir, relLink))
   386  
   387  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   388  						Image:   "some/app",
   389  						Builder: defaultBuilderName,
   390  						AppPath: relLink,
   391  					}))
   392  
   393  					h.AssertEq(t, fakeLifecycle.Opts.AppPath, absoluteAppDir)
   394  				})
   395  
   396  				it("resolves symbolic links recursively", func() {
   397  					linkRef1 := absoluteAppDir
   398  					absoluteLink1 := filepath.Join(tmpDir, "some-app-abs-1.link")
   399  
   400  					linkRef2 := "some-app-abs-1.link"
   401  					symbolicLink := filepath.Join(tmpDir, "some-app-rel-2.link")
   402  
   403  					h.AssertNil(t, os.Symlink(linkRef1, absoluteLink1))
   404  					h.AssertNil(t, os.Symlink(linkRef2, symbolicLink))
   405  
   406  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   407  						Image:   "some/app",
   408  						Builder: defaultBuilderName,
   409  						AppPath: symbolicLink,
   410  					}))
   411  
   412  					h.AssertEq(t, fakeLifecycle.Opts.AppPath, absoluteAppDir)
   413  				})
   414  			})
   415  		})
   416  
   417  		when("Builder option", func() {
   418  			it("builder is required", func() {
   419  				h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
   420  					Image: "some/app",
   421  				}),
   422  					"invalid builder ''",
   423  				)
   424  			})
   425  
   426  			when("the builder name is provided", func() {
   427  				var (
   428  					customBuilderImage *fakes.Image
   429  					fakeRunImage       *fakes.Image
   430  				)
   431  
   432  				it.Before(func() {
   433  					customBuilderImage = ifakes.NewFakeBuilderImage(t,
   434  						tmpDir,
   435  						defaultBuilderName,
   436  						"some.stack.id",
   437  						"1234",
   438  						"5678",
   439  						builder.Metadata{
   440  							Stack: builder.StackMetadata{
   441  								RunImage: builder.RunImageMetadata{
   442  									Image: "some/run",
   443  								},
   444  							},
   445  							Lifecycle: builder.LifecycleMetadata{
   446  								LifecycleInfo: builder.LifecycleInfo{
   447  									Version: &builder.Version{
   448  										Version: *semver.MustParse(builder.DefaultLifecycleVersion),
   449  									},
   450  								},
   451  								APIs: builder.LifecycleAPIs{
   452  									Buildpack: builder.APIVersions{
   453  										Supported: builder.APISet{api.MustParse("0.2"), api.MustParse("0.3"), api.MustParse("0.4")},
   454  									},
   455  									Platform: builder.APIVersions{
   456  										Supported: builder.APISet{api.MustParse("0.3"), api.MustParse("0.4")},
   457  									},
   458  								},
   459  							},
   460  						},
   461  						nil,
   462  						nil,
   463  						nil,
   464  						nil,
   465  						newLinuxImage,
   466  					)
   467  
   468  					fakeImageFetcher.LocalImages[customBuilderImage.Name()] = customBuilderImage
   469  
   470  					fakeRunImage = fakes.NewImage("some/run", "", nil)
   471  					h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
   472  					fakeImageFetcher.LocalImages[fakeRunImage.Name()] = fakeRunImage
   473  				})
   474  
   475  				it.After(func() {
   476  					h.AssertNilE(t, customBuilderImage.Cleanup())
   477  					h.AssertNilE(t, fakeRunImage.Cleanup())
   478  				})
   479  
   480  				it("it uses the provided builder", func() {
   481  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   482  						Image:   "some/app",
   483  						Builder: defaultBuilderName,
   484  					}))
   485  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), customBuilderImage.Name())
   486  				})
   487  			})
   488  		})
   489  
   490  		when("RunImage option", func() {
   491  			var (
   492  				fakeRunImage *fakes.Image
   493  			)
   494  
   495  			it.Before(func() {
   496  				fakeRunImage = fakes.NewImage("custom/run", "", nil)
   497  				h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   498  				h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   499  				fakeImageFetcher.LocalImages[fakeRunImage.Name()] = fakeRunImage
   500  			})
   501  
   502  			it.After(func() {
   503  				h.AssertNilE(t, fakeRunImage.Cleanup())
   504  			})
   505  
   506  			when("run image stack matches the builder stack", func() {
   507  				it("uses the provided image", func() {
   508  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   509  						Image:    "some/app",
   510  						Builder:  defaultBuilderName,
   511  						RunImage: "custom/run",
   512  					}))
   513  					h.AssertEq(t, fakeLifecycle.Opts.RunImage, "custom/run")
   514  				})
   515  			})
   516  
   517  			when("run image stack does not match the builder stack", func() {
   518  				it.Before(func() {
   519  					h.AssertNil(t, fakeRunImage.SetLabel("io.buildpacks.stack.id", "other.stack"))
   520  				})
   521  
   522  				it("errors", func() {
   523  					h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
   524  						Image:    "some/app",
   525  						Builder:  defaultBuilderName,
   526  						RunImage: "custom/run",
   527  					}),
   528  						"invalid run-image 'custom/run': run-image stack id 'other.stack' does not match builder stack 'some.stack.id'",
   529  					)
   530  				})
   531  			})
   532  
   533  			when("run image is not supplied", func() {
   534  				when("there are no locally configured mirrors", func() {
   535  					when("Publish is true", func() {
   536  						it("chooses the run image mirror matching the local image", func() {
   537  							fakeImageFetcher.RemoteImages[fakeDefaultRunImage.Name()] = fakeDefaultRunImage
   538  
   539  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   540  								Image:   "some/app",
   541  								Builder: defaultBuilderName,
   542  								Publish: true,
   543  							}))
   544  							h.AssertEq(t, fakeLifecycle.Opts.RunImage, "default/run")
   545  						})
   546  
   547  						for _, registry := range []string{"registry1.example.com", "registry2.example.com"} {
   548  							testRegistry := registry
   549  							it("chooses the run image mirror matching the built image", func() {
   550  								runImg := testRegistry + "/run/mirror"
   551  								fakeImageFetcher.RemoteImages[runImg] = fakeDefaultRunImage
   552  								h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   553  									Image:   testRegistry + "/some/app",
   554  									Builder: defaultBuilderName,
   555  									Publish: true,
   556  								}))
   557  								h.AssertEq(t, fakeLifecycle.Opts.RunImage, runImg)
   558  							})
   559  						}
   560  					})
   561  
   562  					when("Publish is false", func() {
   563  						for _, img := range []string{"some/app",
   564  							"registry1.example.com/some/app",
   565  							"registry2.example.com/some/app"} {
   566  							testImg := img
   567  							it("chooses a mirror on the builder registry", func() {
   568  								h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   569  									Image:   testImg,
   570  									Builder: defaultBuilderName,
   571  								}))
   572  								h.AssertEq(t, fakeLifecycle.Opts.RunImage, "default/run")
   573  							})
   574  						}
   575  					})
   576  				})
   577  
   578  				when("there are locally configured mirrors", func() {
   579  					var (
   580  						fakeLocalMirror  *fakes.Image
   581  						fakeLocalMirror1 *fakes.Image
   582  						mirrors          = map[string][]string{
   583  							"default/run": {"local/mirror", "registry1.example.com/local/mirror"},
   584  						}
   585  					)
   586  
   587  					it.Before(func() {
   588  						fakeLocalMirror = fakes.NewImage("local/mirror", "", nil)
   589  						h.AssertNil(t, fakeLocalMirror.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   590  						h.AssertNil(t, fakeLocalMirror.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   591  
   592  						fakeImageFetcher.LocalImages[fakeLocalMirror.Name()] = fakeLocalMirror
   593  
   594  						fakeLocalMirror1 = fakes.NewImage("registry1.example.com/local/mirror", "", nil)
   595  						h.AssertNil(t, fakeLocalMirror1.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
   596  						h.AssertNil(t, fakeLocalMirror1.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
   597  
   598  						fakeImageFetcher.LocalImages[fakeLocalMirror1.Name()] = fakeLocalMirror1
   599  					})
   600  
   601  					it.After(func() {
   602  						h.AssertNilE(t, fakeLocalMirror.Cleanup())
   603  						h.AssertNilE(t, fakeLocalMirror1.Cleanup())
   604  					})
   605  
   606  					when("Publish is true", func() {
   607  						for _, registry := range []string{"", "registry1.example.com"} {
   608  							testRegistry := registry
   609  							it("prefers user provided mirrors for registry "+testRegistry, func() {
   610  								if testRegistry != "" {
   611  									testRegistry += "/"
   612  								}
   613  								runImg := testRegistry + "local/mirror"
   614  								fakeImageFetcher.RemoteImages[runImg] = fakeDefaultRunImage
   615  
   616  								h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   617  									Image:             testRegistry + "some/app",
   618  									Builder:           defaultBuilderName,
   619  									AdditionalMirrors: mirrors,
   620  									Publish:           true,
   621  								}))
   622  								h.AssertEq(t, fakeLifecycle.Opts.RunImage, runImg)
   623  							})
   624  						}
   625  					})
   626  
   627  					when("Publish is false", func() {
   628  						for _, registry := range []string{"", "registry1.example.com", "registry2.example.com"} {
   629  							testRegistry := registry
   630  							it("prefers user provided mirrors", func() {
   631  								h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   632  									Image:             testRegistry + "some/app",
   633  									Builder:           defaultBuilderName,
   634  									AdditionalMirrors: mirrors,
   635  								}))
   636  								h.AssertEq(t, fakeLifecycle.Opts.RunImage, "local/mirror")
   637  							})
   638  						}
   639  					})
   640  				})
   641  			})
   642  		})
   643  
   644  		when("ClearCache option", func() {
   645  			it("passes it through to lifecycle", func() {
   646  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   647  					Image:      "some/app",
   648  					Builder:    defaultBuilderName,
   649  					ClearCache: true,
   650  				}))
   651  				h.AssertEq(t, fakeLifecycle.Opts.ClearCache, true)
   652  			})
   653  
   654  			it("defaults to false", func() {
   655  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   656  					Image:   "some/app",
   657  					Builder: defaultBuilderName,
   658  				}))
   659  				h.AssertEq(t, fakeLifecycle.Opts.ClearCache, false)
   660  			})
   661  		})
   662  
   663  		when("ImageCache option", func() {
   664  			it("passes it through to lifecycle", func() {
   665  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   666  					Image:      "some/app",
   667  					Builder:    defaultBuilderName,
   668  					CacheImage: "some-cache-image",
   669  				}))
   670  				h.AssertEq(t, fakeLifecycle.Opts.CacheImage, "some-cache-image")
   671  			})
   672  
   673  			it("defaults to false", func() {
   674  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   675  					Image:   "some/app",
   676  					Builder: defaultBuilderName,
   677  				}))
   678  				h.AssertEq(t, fakeLifecycle.Opts.CacheImage, "")
   679  			})
   680  		})
   681  
   682  		when("Buildpacks option", func() {
   683  			assertOrderEquals := func(content string) {
   684  				t.Helper()
   685  
   686  				orderLayer, err := defaultBuilderImage.FindLayerWithPath("/cnb/order.toml")
   687  				h.AssertNil(t, err)
   688  				h.AssertOnTarEntry(t, orderLayer, "/cnb/order.toml", h.ContentEquals(content))
   689  			}
   690  
   691  			it("builder order is overwritten", func() {
   692  				additionalBP := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   693  					WithAPI: api.MustParse("0.3"),
   694  					WithInfo: dist.ModuleInfo{
   695  						ID:      "buildpack.add.1.id",
   696  						Version: "buildpack.add.1.version",
   697  					},
   698  					WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   699  					WithOrder:  nil,
   700  				})
   701  
   702  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   703  					Image:      "some/app",
   704  					Builder:    defaultBuilderName,
   705  					ClearCache: true,
   706  					Buildpacks: []string{additionalBP},
   707  				}))
   708  				h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
   709  
   710  				assertOrderEquals(`[[order]]
   711  
   712    [[order.group]]
   713      id = "buildpack.add.1.id"
   714      version = "buildpack.add.1.version"
   715  `)
   716  			})
   717  
   718  			when("id - no version is provided", func() {
   719  				it("resolves version", func() {
   720  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   721  						Image:      "some/app",
   722  						Builder:    defaultBuilderName,
   723  						ClearCache: true,
   724  						Buildpacks: []string{"buildpack.1.id"},
   725  					}))
   726  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
   727  
   728  					assertOrderEquals(`[[order]]
   729  
   730    [[order.group]]
   731      id = "buildpack.1.id"
   732      version = "buildpack.1.version"
   733  `)
   734  				})
   735  			})
   736  
   737  			when("from=builder:id@version", func() {
   738  				it("builder order is prepended", func() {
   739  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   740  						Image:      "some/app",
   741  						Builder:    defaultBuilderName,
   742  						ClearCache: true,
   743  						Buildpacks: []string{
   744  							"from=builder:buildpack.1.id@buildpack.1.version",
   745  						},
   746  					}))
   747  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
   748  
   749  					assertOrderEquals(`[[order]]
   750  
   751    [[order.group]]
   752      id = "buildpack.1.id"
   753      version = "buildpack.1.version"
   754  `)
   755  				})
   756  			})
   757  
   758  			when("from=builder is set first", func() {
   759  				it("builder order is prepended", func() {
   760  					additionalBP1 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   761  						WithAPI: api.MustParse("0.3"),
   762  						WithInfo: dist.ModuleInfo{
   763  							ID:      "buildpack.add.1.id",
   764  							Version: "buildpack.add.1.version",
   765  						},
   766  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   767  						WithOrder:  nil,
   768  					})
   769  
   770  					additionalBP2 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   771  						WithAPI: api.MustParse("0.3"),
   772  						WithInfo: dist.ModuleInfo{
   773  							ID:      "buildpack.add.2.id",
   774  							Version: "buildpack.add.2.version",
   775  						},
   776  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   777  						WithOrder:  nil,
   778  					})
   779  
   780  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   781  						Image:      "some/app",
   782  						Builder:    defaultBuilderName,
   783  						ClearCache: true,
   784  						Buildpacks: []string{
   785  							"from=builder",
   786  							additionalBP1,
   787  							additionalBP2,
   788  						},
   789  					}))
   790  
   791  					assertOrderEquals(`[[order]]
   792  
   793    [[order.group]]
   794      id = "buildpack.1.id"
   795      version = "buildpack.1.version"
   796  
   797    [[order.group]]
   798      id = "buildpack.add.1.id"
   799      version = "buildpack.add.1.version"
   800  
   801    [[order.group]]
   802      id = "buildpack.add.2.id"
   803      version = "buildpack.add.2.version"
   804  
   805  [[order]]
   806  
   807    [[order.group]]
   808      id = "buildpack.2.id"
   809      version = "buildpack.2.version"
   810  
   811    [[order.group]]
   812      id = "buildpack.add.1.id"
   813      version = "buildpack.add.1.version"
   814  
   815    [[order.group]]
   816      id = "buildpack.add.2.id"
   817      version = "buildpack.add.2.version"
   818  `)
   819  				})
   820  			})
   821  
   822  			when("from=builder is set in middle", func() {
   823  				it("builder order is appended", func() {
   824  					additionalBP1 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   825  						WithAPI: api.MustParse("0.3"),
   826  						WithInfo: dist.ModuleInfo{
   827  							ID:      "buildpack.add.1.id",
   828  							Version: "buildpack.add.1.version",
   829  						},
   830  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   831  						WithOrder:  nil,
   832  					})
   833  
   834  					additionalBP2 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   835  						WithAPI: api.MustParse("0.3"),
   836  						WithInfo: dist.ModuleInfo{
   837  							ID:      "buildpack.add.2.id",
   838  							Version: "buildpack.add.2.version",
   839  						},
   840  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   841  						WithOrder:  nil,
   842  					})
   843  
   844  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   845  						Image:      "some/app",
   846  						Builder:    defaultBuilderName,
   847  						ClearCache: true,
   848  						Buildpacks: []string{
   849  							additionalBP1,
   850  							"from=builder",
   851  							additionalBP2,
   852  						},
   853  					}))
   854  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
   855  
   856  					assertOrderEquals(`[[order]]
   857  
   858    [[order.group]]
   859      id = "buildpack.add.1.id"
   860      version = "buildpack.add.1.version"
   861  
   862    [[order.group]]
   863      id = "buildpack.1.id"
   864      version = "buildpack.1.version"
   865  
   866    [[order.group]]
   867      id = "buildpack.add.2.id"
   868      version = "buildpack.add.2.version"
   869  
   870  [[order]]
   871  
   872    [[order.group]]
   873      id = "buildpack.add.1.id"
   874      version = "buildpack.add.1.version"
   875  
   876    [[order.group]]
   877      id = "buildpack.2.id"
   878      version = "buildpack.2.version"
   879  
   880    [[order.group]]
   881      id = "buildpack.add.2.id"
   882      version = "buildpack.add.2.version"
   883  `)
   884  				})
   885  			})
   886  
   887  			when("from=builder is set last", func() {
   888  				it("builder order is appended", func() {
   889  					additionalBP1 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   890  						WithAPI: api.MustParse("0.3"),
   891  						WithInfo: dist.ModuleInfo{
   892  							ID:      "buildpack.add.1.id",
   893  							Version: "buildpack.add.1.version",
   894  						},
   895  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   896  						WithOrder:  nil,
   897  					})
   898  
   899  					additionalBP2 := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   900  						WithAPI: api.MustParse("0.3"),
   901  						WithInfo: dist.ModuleInfo{
   902  							ID:      "buildpack.add.2.id",
   903  							Version: "buildpack.add.2.version",
   904  						},
   905  						WithStacks: []dist.Stack{{ID: defaultBuilderStackID}},
   906  						WithOrder:  nil,
   907  					})
   908  
   909  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
   910  						Image:      "some/app",
   911  						Builder:    defaultBuilderName,
   912  						ClearCache: true,
   913  						Buildpacks: []string{
   914  							additionalBP1,
   915  							additionalBP2,
   916  							"from=builder",
   917  						},
   918  					}))
   919  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
   920  
   921  					assertOrderEquals(`[[order]]
   922  
   923    [[order.group]]
   924      id = "buildpack.add.1.id"
   925      version = "buildpack.add.1.version"
   926  
   927    [[order.group]]
   928      id = "buildpack.add.2.id"
   929      version = "buildpack.add.2.version"
   930  
   931    [[order.group]]
   932      id = "buildpack.1.id"
   933      version = "buildpack.1.version"
   934  
   935  [[order]]
   936  
   937    [[order.group]]
   938      id = "buildpack.add.1.id"
   939      version = "buildpack.add.1.version"
   940  
   941    [[order.group]]
   942      id = "buildpack.add.2.id"
   943      version = "buildpack.add.2.version"
   944  
   945    [[order.group]]
   946      id = "buildpack.2.id"
   947      version = "buildpack.2.version"
   948  `)
   949  				})
   950  			})
   951  
   952  			when("meta-buildpack is used", func() {
   953  				it("resolves buildpack from builder", func() {
   954  					buildpackTar := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
   955  						WithAPI: api.MustParse("0.3"),
   956  						WithInfo: dist.ModuleInfo{
   957  							ID:      "metabuildpack.id",
   958  							Version: "metabuildpack.version",
   959  						},
   960  						WithStacks: nil,
   961  						WithOrder: dist.Order{{
   962  							Group: []dist.ModuleRef{{
   963  								ModuleInfo: dist.ModuleInfo{
   964  									ID:      "buildpack.1.id",
   965  									Version: "buildpack.1.version",
   966  								},
   967  								Optional: false,
   968  							}, {
   969  								ModuleInfo: dist.ModuleInfo{
   970  									ID:      "buildpack.2.id",
   971  									Version: "buildpack.2.version",
   972  								},
   973  								Optional: false,
   974  							}},
   975  						}},
   976  					})
   977  
   978  					err := subject.Build(context.TODO(), BuildOptions{
   979  						Image:      "some/app",
   980  						Builder:    defaultBuilderName,
   981  						ClearCache: true,
   982  						Buildpacks: []string{buildpackTar},
   983  					})
   984  
   985  					h.AssertNil(t, err)
   986  				})
   987  			})
   988  
   989  			when("meta-buildpack folder is used", func() {
   990  				it("resolves buildpack", func() {
   991  					metaBuildpackFolder := filepath.Join(tmpDir, "meta-buildpack")
   992  					err := os.Mkdir(metaBuildpackFolder, os.ModePerm)
   993  					h.AssertNil(t, err)
   994  
   995  					err = os.WriteFile(filepath.Join(metaBuildpackFolder, "buildpack.toml"), []byte(`
   996  api = "0.2"
   997  
   998  [buildpack]
   999    id = "local/meta-bp"
  1000    version = "local-meta-bp-version"
  1001    name = "Local Meta-Buildpack"
  1002  
  1003  [[order]]
  1004  [[order.group]]
  1005  id = "local/meta-bp-dep"
  1006  version = "local-meta-bp-version"
  1007  					`), 0644)
  1008  					h.AssertNil(t, err)
  1009  
  1010  					err = os.WriteFile(filepath.Join(metaBuildpackFolder, "package.toml"), []byte(`
  1011  [buildpack]
  1012  uri = "."
  1013  
  1014  [[dependencies]]
  1015  uri = "../meta-buildpack-dependency"
  1016  					`), 0644)
  1017  					h.AssertNil(t, err)
  1018  
  1019  					metaBuildpackDependencyFolder := filepath.Join(tmpDir, "meta-buildpack-dependency")
  1020  					err = os.Mkdir(metaBuildpackDependencyFolder, os.ModePerm)
  1021  					h.AssertNil(t, err)
  1022  
  1023  					err = os.WriteFile(filepath.Join(metaBuildpackDependencyFolder, "buildpack.toml"), []byte(`
  1024  api = "0.2"
  1025  
  1026  [buildpack]
  1027    id = "local/meta-bp-dep"
  1028    version = "local-meta-bp-version"
  1029    name = "Local Meta-Buildpack Dependency"
  1030  
  1031  [[stacks]]
  1032    id = "*"
  1033  					`), 0644)
  1034  					h.AssertNil(t, err)
  1035  
  1036  					err = subject.Build(context.TODO(), BuildOptions{
  1037  						Image:      "some/app",
  1038  						Builder:    defaultBuilderName,
  1039  						ClearCache: true,
  1040  						Buildpacks: []string{metaBuildpackFolder},
  1041  					})
  1042  
  1043  					h.AssertNil(t, err)
  1044  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1045  
  1046  					bldr, err := builder.FromImage(defaultBuilderImage)
  1047  					h.AssertNil(t, err)
  1048  
  1049  					buildpack1Info := dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}
  1050  					buildpack2Info := dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}
  1051  					metaBuildpackInfo := dist.ModuleInfo{ID: "local/meta-bp", Version: "local-meta-bp-version", Name: "Local Meta-Buildpack"}
  1052  					metaBuildpackDependencyInfo := dist.ModuleInfo{ID: "local/meta-bp-dep", Version: "local-meta-bp-version", Name: "Local Meta-Buildpack Dependency"}
  1053  					h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1054  						buildpack1Info,
  1055  						buildpack2Info,
  1056  						metaBuildpackInfo,
  1057  						metaBuildpackDependencyInfo,
  1058  					})
  1059  				})
  1060  
  1061  				it("fails if buildpack dependency could not be fetched", func() {
  1062  					metaBuildpackFolder := filepath.Join(tmpDir, "meta-buildpack")
  1063  					err := os.Mkdir(metaBuildpackFolder, os.ModePerm)
  1064  					h.AssertNil(t, err)
  1065  
  1066  					err = os.WriteFile(filepath.Join(metaBuildpackFolder, "buildpack.toml"), []byte(`
  1067  api = "0.2"
  1068  
  1069  [buildpack]
  1070    id = "local/meta-bp"
  1071    version = "local-meta-bp-version"
  1072    name = "Local Meta-Buildpack"
  1073  
  1074  [[order]]
  1075  [[order.group]]
  1076  id = "local/meta-bp-dep"
  1077  version = "local-meta-bp-version"
  1078  					`), 0644)
  1079  					h.AssertNil(t, err)
  1080  
  1081  					err = os.WriteFile(filepath.Join(metaBuildpackFolder, "package.toml"), []byte(`
  1082  [buildpack]
  1083  uri = "."
  1084  
  1085  [[dependencies]]
  1086  uri = "../meta-buildpack-dependency"
  1087  
  1088  [[dependencies]]
  1089  uri = "../not-a-valid-dependency"
  1090  					`), 0644)
  1091  					h.AssertNil(t, err)
  1092  
  1093  					metaBuildpackDependencyFolder := filepath.Join(tmpDir, "meta-buildpack-dependency")
  1094  					err = os.Mkdir(metaBuildpackDependencyFolder, os.ModePerm)
  1095  					h.AssertNil(t, err)
  1096  
  1097  					err = os.WriteFile(filepath.Join(metaBuildpackDependencyFolder, "buildpack.toml"), []byte(`
  1098  api = "0.2"
  1099  
  1100  [buildpack]
  1101    id = "local/meta-bp-dep"
  1102    version = "local-meta-bp-version"
  1103    name = "Local Meta-Buildpack Dependency"
  1104  
  1105  [[stacks]]
  1106    id = "*"
  1107  					`), 0644)
  1108  					h.AssertNil(t, err)
  1109  
  1110  					err = subject.Build(context.TODO(), BuildOptions{
  1111  						Image:      "some/app",
  1112  						Builder:    defaultBuilderName,
  1113  						ClearCache: true,
  1114  						Buildpacks: []string{metaBuildpackFolder},
  1115  					})
  1116  					h.AssertError(t, err, fmt.Sprintf("fetching package.toml dependencies (path='%s')", filepath.Join(metaBuildpackFolder, "package.toml")))
  1117  					h.AssertError(t, err, "fetching dependencies (uri='../not-a-valid-dependency',image='')")
  1118  				})
  1119  			})
  1120  
  1121  			when("buildpackage image is used", func() {
  1122  				var fakePackage *fakes.Image
  1123  
  1124  				it.Before(func() {
  1125  					metaBuildpackTar := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
  1126  						WithAPI: api.MustParse("0.3"),
  1127  						WithInfo: dist.ModuleInfo{
  1128  							ID:       "meta.buildpack.id",
  1129  							Version:  "meta.buildpack.version",
  1130  							Homepage: "http://meta.buildpack",
  1131  						},
  1132  						WithStacks: nil,
  1133  						WithOrder: dist.Order{{
  1134  							Group: []dist.ModuleRef{{
  1135  								ModuleInfo: dist.ModuleInfo{
  1136  									ID:      "child.buildpack.id",
  1137  									Version: "child.buildpack.version",
  1138  								},
  1139  								Optional: false,
  1140  							}},
  1141  						}},
  1142  					})
  1143  
  1144  					childBuildpackTar := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
  1145  						WithAPI: api.MustParse("0.3"),
  1146  						WithInfo: dist.ModuleInfo{
  1147  							ID:       "child.buildpack.id",
  1148  							Version:  "child.buildpack.version",
  1149  							Homepage: "http://child.buildpack",
  1150  						},
  1151  						WithStacks: []dist.Stack{
  1152  							{ID: defaultBuilderStackID},
  1153  						},
  1154  					})
  1155  
  1156  					bpLayers := dist.ModuleLayers{
  1157  						"meta.buildpack.id": {
  1158  							"meta.buildpack.version": {
  1159  								API: api.MustParse("0.3"),
  1160  								Order: dist.Order{{
  1161  									Group: []dist.ModuleRef{{
  1162  										ModuleInfo: dist.ModuleInfo{
  1163  											ID:      "child.buildpack.id",
  1164  											Version: "child.buildpack.version",
  1165  										},
  1166  										Optional: false,
  1167  									}},
  1168  								}},
  1169  								LayerDiffID: diffIDForFile(t, metaBuildpackTar),
  1170  							},
  1171  						},
  1172  						"child.buildpack.id": {
  1173  							"child.buildpack.version": {
  1174  								API: api.MustParse("0.3"),
  1175  								Stacks: []dist.Stack{
  1176  									{ID: defaultBuilderStackID},
  1177  								},
  1178  								LayerDiffID: diffIDForFile(t, childBuildpackTar),
  1179  							},
  1180  						},
  1181  					}
  1182  
  1183  					md := buildpack.Metadata{
  1184  						ModuleInfo: dist.ModuleInfo{
  1185  							ID:      "meta.buildpack.id",
  1186  							Version: "meta.buildpack.version",
  1187  						},
  1188  						Stacks: []dist.Stack{
  1189  							{ID: defaultBuilderStackID},
  1190  						},
  1191  					}
  1192  
  1193  					fakePackage = fakes.NewImage("example.com/some/package", "", nil)
  1194  					h.AssertNil(t, dist.SetLabel(fakePackage, "io.buildpacks.buildpack.layers", bpLayers))
  1195  					h.AssertNil(t, dist.SetLabel(fakePackage, "io.buildpacks.buildpackage.metadata", md))
  1196  
  1197  					h.AssertNil(t, fakePackage.AddLayer(metaBuildpackTar))
  1198  					h.AssertNil(t, fakePackage.AddLayer(childBuildpackTar))
  1199  
  1200  					fakeImageFetcher.LocalImages[fakePackage.Name()] = fakePackage
  1201  				})
  1202  
  1203  				it("all buildpacks are added to ephemeral builder", func() {
  1204  					err := subject.Build(context.TODO(), BuildOptions{
  1205  						Image:      "some/app",
  1206  						Builder:    defaultBuilderName,
  1207  						ClearCache: true,
  1208  						Buildpacks: []string{
  1209  							"example.com/some/package",
  1210  						},
  1211  					})
  1212  
  1213  					h.AssertNil(t, err)
  1214  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1215  					bldr, err := builder.FromImage(defaultBuilderImage)
  1216  					h.AssertNil(t, err)
  1217  					h.AssertEq(t, bldr.Order(), dist.Order{
  1218  						{Group: []dist.ModuleRef{
  1219  							{ModuleInfo: dist.ModuleInfo{ID: "meta.buildpack.id", Version: "meta.buildpack.version"}},
  1220  						}},
  1221  						// Child buildpacks should not be added to order
  1222  					})
  1223  					h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1224  						{
  1225  							ID:      "buildpack.1.id",
  1226  							Version: "buildpack.1.version",
  1227  						},
  1228  						{
  1229  							ID:      "buildpack.2.id",
  1230  							Version: "buildpack.2.version",
  1231  						},
  1232  						{
  1233  							ID:      "meta.buildpack.id",
  1234  							Version: "meta.buildpack.version",
  1235  						},
  1236  						{
  1237  							ID:      "child.buildpack.id",
  1238  							Version: "child.buildpack.version",
  1239  						},
  1240  					})
  1241  					args := fakeImageFetcher.FetchCalls[fakePackage.Name()]
  1242  					h.AssertEq(t, args.Platform, "linux/amd64")
  1243  				})
  1244  
  1245  				it("fails when no metadata label on package", func() {
  1246  					h.AssertNil(t, fakePackage.SetLabel("io.buildpacks.buildpackage.metadata", ""))
  1247  
  1248  					err := subject.Build(context.TODO(), BuildOptions{
  1249  						Image:      "some/app",
  1250  						Builder:    defaultBuilderName,
  1251  						ClearCache: true,
  1252  						Buildpacks: []string{
  1253  							"example.com/some/package",
  1254  						},
  1255  					})
  1256  
  1257  					h.AssertError(t, err, "extracting buildpacks from 'example.com/some/package': could not find label 'io.buildpacks.buildpackage.metadata'")
  1258  				})
  1259  
  1260  				it("fails when no bp layers label is on package", func() {
  1261  					h.AssertNil(t, fakePackage.SetLabel("io.buildpacks.buildpack.layers", ""))
  1262  
  1263  					err := subject.Build(context.TODO(), BuildOptions{
  1264  						Image:      "some/app",
  1265  						Builder:    defaultBuilderName,
  1266  						ClearCache: true,
  1267  						Buildpacks: []string{
  1268  							"example.com/some/package",
  1269  						},
  1270  					})
  1271  
  1272  					h.AssertError(t, err, "extracting buildpacks from 'example.com/some/package': could not find label 'io.buildpacks.buildpack.layers'")
  1273  				})
  1274  			})
  1275  
  1276  			it("ensures buildpacks exist on builder", func() {
  1277  				h.AssertError(t, subject.Build(context.TODO(), BuildOptions{
  1278  					Image:      "some/app",
  1279  					Builder:    defaultBuilderName,
  1280  					ClearCache: true,
  1281  					Buildpacks: []string{"missing.bp@version"},
  1282  				}),
  1283  					"downloading buildpack: error reading missing.bp@version: invalid locator: InvalidLocator",
  1284  				)
  1285  			})
  1286  
  1287  			when("from project descriptor", func() {
  1288  				when("id - no version is provided", func() {
  1289  					it("resolves version", func() {
  1290  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  1291  							Image:      "some/app",
  1292  							Builder:    defaultBuilderName,
  1293  							ClearCache: true,
  1294  							ProjectDescriptor: projectTypes.Descriptor{
  1295  								Build: projectTypes.Build{Buildpacks: []projectTypes.Buildpack{{ID: "buildpack.1.id"}}},
  1296  							},
  1297  						}))
  1298  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1299  
  1300  						assertOrderEquals(`[[order]]
  1301  
  1302    [[order.group]]
  1303      id = "buildpack.1.id"
  1304      version = "buildpack.1.version"
  1305  `)
  1306  					})
  1307  				})
  1308  			})
  1309  
  1310  			when("buildpacks include URIs", func() {
  1311  				var buildpackTgz string
  1312  
  1313  				it.Before(func() {
  1314  					buildpackTgz = h.CreateTGZ(t, filepath.Join("testdata", "buildpack2"), "./", 0755)
  1315  				})
  1316  
  1317  				it.After(func() {
  1318  					h.AssertNilE(t, os.Remove(buildpackTgz))
  1319  				})
  1320  
  1321  				it("buildpacks are added to ephemeral builder", func() {
  1322  					err := subject.Build(context.TODO(), BuildOptions{
  1323  						Image:      "some/app",
  1324  						Builder:    defaultBuilderName,
  1325  						ClearCache: true,
  1326  						Buildpacks: []string{
  1327  							"buildpack.1.id@buildpack.1.version",
  1328  							"buildpack.2.id@buildpack.2.version",
  1329  							filepath.Join("testdata", "buildpack"),
  1330  							buildpackTgz,
  1331  						},
  1332  					})
  1333  
  1334  					h.AssertNil(t, err)
  1335  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1336  					bldr, err := builder.FromImage(defaultBuilderImage)
  1337  					h.AssertNil(t, err)
  1338  					buildpack1Info := dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}
  1339  					buildpack2Info := dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}
  1340  					dirBuildpackInfo := dist.ModuleInfo{ID: "bp.one", Version: "1.2.3", Homepage: "http://one.buildpack"}
  1341  					tgzBuildpackInfo := dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}
  1342  					h.AssertEq(t, bldr.Order(), dist.Order{
  1343  						{Group: []dist.ModuleRef{
  1344  							{ModuleInfo: buildpack1Info},
  1345  							{ModuleInfo: buildpack2Info},
  1346  							{ModuleInfo: dirBuildpackInfo},
  1347  							{ModuleInfo: tgzBuildpackInfo},
  1348  						}},
  1349  					})
  1350  					h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1351  						buildpack1Info,
  1352  						buildpack2Info,
  1353  						dirBuildpackInfo,
  1354  						tgzBuildpackInfo,
  1355  					})
  1356  				})
  1357  
  1358  				when("uri is an http url", func() {
  1359  					var server *ghttp.Server
  1360  
  1361  					it.Before(func() {
  1362  						server = ghttp.NewServer()
  1363  						server.AppendHandlers(func(w http.ResponseWriter, r *http.Request) {
  1364  							http.ServeFile(w, r, buildpackTgz)
  1365  						})
  1366  					})
  1367  
  1368  					it.After(func() {
  1369  						server.Close()
  1370  					})
  1371  
  1372  					it("adds the buildpack", func() {
  1373  						err := subject.Build(context.TODO(), BuildOptions{
  1374  							Image:      "some/app",
  1375  							Builder:    defaultBuilderName,
  1376  							ClearCache: true,
  1377  							Buildpacks: []string{
  1378  								"buildpack.1.id@buildpack.1.version",
  1379  								"buildpack.2.id@buildpack.2.version",
  1380  								server.URL(),
  1381  							},
  1382  						})
  1383  
  1384  						h.AssertNil(t, err)
  1385  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1386  						bldr, err := builder.FromImage(defaultBuilderImage)
  1387  						h.AssertNil(t, err)
  1388  						h.AssertEq(t, bldr.Order(), dist.Order{
  1389  							{Group: []dist.ModuleRef{
  1390  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}},
  1391  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1392  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1393  							}},
  1394  						})
  1395  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1396  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1397  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1398  							{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"},
  1399  						})
  1400  					})
  1401  
  1402  					it("adds the buildpack from the project descriptor", func() {
  1403  						err := subject.Build(context.TODO(), BuildOptions{
  1404  							Image:      "some/app",
  1405  							Builder:    defaultBuilderName,
  1406  							ClearCache: true,
  1407  							ProjectDescriptor: projectTypes.Descriptor{
  1408  								Build: projectTypes.Build{
  1409  									Buildpacks: []projectTypes.Buildpack{{
  1410  										URI: server.URL(),
  1411  									}},
  1412  								},
  1413  							},
  1414  						})
  1415  
  1416  						h.AssertNil(t, err)
  1417  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1418  						bldr, err := builder.FromImage(defaultBuilderImage)
  1419  						h.AssertNil(t, err)
  1420  						h.AssertEq(t, bldr.Order(), dist.Order{
  1421  							{Group: []dist.ModuleRef{
  1422  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1423  							}},
  1424  						})
  1425  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1426  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1427  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1428  							{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"},
  1429  						})
  1430  					})
  1431  
  1432  					it("adds the pre buildpack from the project descriptor", func() {
  1433  						err := subject.Build(context.TODO(), BuildOptions{
  1434  							Image:      "some/app",
  1435  							Builder:    defaultBuilderName,
  1436  							ClearCache: true,
  1437  							ProjectDescriptor: projectTypes.Descriptor{
  1438  								Build: projectTypes.Build{
  1439  									Pre: projectTypes.GroupAddition{
  1440  										Buildpacks: []projectTypes.Buildpack{{
  1441  											URI: server.URL(),
  1442  										}},
  1443  									},
  1444  								},
  1445  							},
  1446  						})
  1447  
  1448  						h.AssertNil(t, err)
  1449  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1450  						bldr, err := builder.FromImage(defaultBuilderImage)
  1451  						h.AssertNil(t, err)
  1452  						h.AssertEq(t, bldr.Order(), dist.Order{
  1453  							{Group: []dist.ModuleRef{
  1454  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1455  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}},
  1456  							}},
  1457  							{Group: []dist.ModuleRef{
  1458  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1459  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1460  							}},
  1461  						})
  1462  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1463  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1464  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1465  							{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"},
  1466  						})
  1467  					})
  1468  
  1469  					it("adds the post buildpack from the project descriptor", func() {
  1470  						err := subject.Build(context.TODO(), BuildOptions{
  1471  							Image:      "some/app",
  1472  							Builder:    defaultBuilderName,
  1473  							ClearCache: true,
  1474  							ProjectDescriptor: projectTypes.Descriptor{
  1475  								Build: projectTypes.Build{
  1476  									Post: projectTypes.GroupAddition{
  1477  										Buildpacks: []projectTypes.Buildpack{{
  1478  											URI: server.URL(),
  1479  										}},
  1480  									},
  1481  								},
  1482  							},
  1483  						})
  1484  
  1485  						h.AssertNil(t, err)
  1486  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1487  						bldr, err := builder.FromImage(defaultBuilderImage)
  1488  						h.AssertNil(t, err)
  1489  						h.AssertEq(t, bldr.Order(), dist.Order{
  1490  							{Group: []dist.ModuleRef{
  1491  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}},
  1492  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1493  							}},
  1494  							{Group: []dist.ModuleRef{
  1495  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1496  								{ModuleInfo: dist.ModuleInfo{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1497  							}},
  1498  						})
  1499  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1500  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1501  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1502  							{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"},
  1503  						})
  1504  					})
  1505  				})
  1506  
  1507  				when("pre and post buildpacks", func() {
  1508  					it("added from the project descriptor", func() {
  1509  						err := subject.Build(context.TODO(), BuildOptions{
  1510  							Image:      "some/app",
  1511  							Builder:    defaultBuilderName,
  1512  							ClearCache: true,
  1513  							ProjectDescriptor: projectTypes.Descriptor{
  1514  								Build: projectTypes.Build{
  1515  									Pre: projectTypes.GroupAddition{
  1516  										Buildpacks: []projectTypes.Buildpack{{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1517  									},
  1518  									Post: projectTypes.GroupAddition{
  1519  										Buildpacks: []projectTypes.Buildpack{{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1520  									},
  1521  								},
  1522  							},
  1523  						})
  1524  
  1525  						h.AssertNil(t, err)
  1526  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1527  						bldr, err := builder.FromImage(defaultBuilderImage)
  1528  						h.AssertNil(t, err)
  1529  						h.AssertEq(t, bldr.Order(), dist.Order{
  1530  							{Group: []dist.ModuleRef{
  1531  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1532  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}},
  1533  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1534  							}},
  1535  							{Group: []dist.ModuleRef{
  1536  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1537  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1538  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.2.id", Version: "buildpack.2.version"}},
  1539  							}},
  1540  						})
  1541  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1542  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1543  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1544  						})
  1545  					})
  1546  
  1547  					it("not added from the project descriptor", func() {
  1548  						err := subject.Build(context.TODO(), BuildOptions{
  1549  							Image:      "some/app",
  1550  							Builder:    defaultBuilderName,
  1551  							ClearCache: true,
  1552  							Buildpacks: []string{
  1553  								"buildpack.1.id@buildpack.1.version",
  1554  							},
  1555  							ProjectDescriptor: projectTypes.Descriptor{
  1556  								Build: projectTypes.Build{
  1557  									Pre: projectTypes.GroupAddition{
  1558  										Buildpacks: []projectTypes.Buildpack{{ID: "some-other-buildpack-id", Version: "some-other-buildpack-version"}},
  1559  									},
  1560  									Post: projectTypes.GroupAddition{
  1561  										Buildpacks: []projectTypes.Buildpack{{ID: "yet-other-buildpack-id", Version: "yet-other-buildpack-version"}},
  1562  									},
  1563  								},
  1564  							},
  1565  						})
  1566  
  1567  						h.AssertNil(t, err)
  1568  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1569  						bldr, err := builder.FromImage(defaultBuilderImage)
  1570  						h.AssertNil(t, err)
  1571  						h.AssertEq(t, bldr.Order(), dist.Order{
  1572  							{Group: []dist.ModuleRef{
  1573  								{ModuleInfo: dist.ModuleInfo{ID: "buildpack.1.id", Version: "buildpack.1.version"}},
  1574  							}},
  1575  						})
  1576  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1577  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1578  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1579  						})
  1580  					})
  1581  				})
  1582  
  1583  				when("added buildpack's mixins are not satisfied", func() {
  1584  					it.Before(func() {
  1585  						h.AssertNil(t, defaultBuilderImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "build:mixinY"]`))
  1586  						h.AssertNil(t, fakeDefaultRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "run:mixinZ"]`))
  1587  					})
  1588  
  1589  					it("succeeds", func() {
  1590  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  1591  							Image:   "some/app",
  1592  							Builder: defaultBuilderName,
  1593  							Buildpacks: []string{
  1594  								buildpackTgz, // requires mixinA, build:mixinB, run:mixinC
  1595  							},
  1596  						}))
  1597  					})
  1598  
  1599  					when("platform API < 0.12", func() {
  1600  						it.Before(func() {
  1601  							setAPIs(t, defaultBuilderImage, []string{"0.8"}, []string{"0.11"})
  1602  						})
  1603  
  1604  						it("returns an error", func() {
  1605  							err := subject.Build(context.TODO(), BuildOptions{
  1606  								Image:   "some/app",
  1607  								Builder: defaultBuilderName,
  1608  								Buildpacks: []string{
  1609  									buildpackTgz, // requires mixinA, build:mixinB, run:mixinC
  1610  								},
  1611  							})
  1612  
  1613  							h.AssertError(t, err, "validating stack mixins: buildpack 'some-other-buildpack-id@some-other-buildpack-version' requires missing mixin(s): build:mixinB, mixinA, run:mixinC")
  1614  						})
  1615  					})
  1616  				})
  1617  
  1618  				when("buildpack is inline", func() {
  1619  					var (
  1620  						tmpDir string
  1621  					)
  1622  
  1623  					it.Before(func() {
  1624  						var err error
  1625  						tmpDir, err = os.MkdirTemp("", "project-desc")
  1626  						h.AssertNil(t, err)
  1627  					})
  1628  
  1629  					it.After(func() {
  1630  						err := os.RemoveAll(tmpDir)
  1631  						h.AssertNil(t, err)
  1632  					})
  1633  
  1634  					it("all buildpacks are added to ephemeral builder", func() {
  1635  						err := subject.Build(context.TODO(), BuildOptions{
  1636  							Image:      "some/app",
  1637  							Builder:    defaultBuilderName,
  1638  							ClearCache: true,
  1639  							ProjectDescriptor: projectTypes.Descriptor{
  1640  								Build: projectTypes.Build{
  1641  									Buildpacks: []projectTypes.Buildpack{{
  1642  										ID: "my/inline",
  1643  										Script: projectTypes.Script{
  1644  											API:    "0.4",
  1645  											Inline: "touch foo.txt",
  1646  										},
  1647  									}},
  1648  								},
  1649  							},
  1650  							ProjectDescriptorBaseDir: tmpDir,
  1651  						})
  1652  
  1653  						h.AssertNil(t, err)
  1654  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1655  						bldr, err := builder.FromImage(defaultBuilderImage)
  1656  						h.AssertNil(t, err)
  1657  						h.AssertEq(t, bldr.Order(), dist.Order{
  1658  							{Group: []dist.ModuleRef{
  1659  								{ModuleInfo: dist.ModuleInfo{ID: "my/inline", Version: "0.0.0"}},
  1660  							}},
  1661  						})
  1662  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1663  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1664  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1665  							{ID: "my/inline", Version: "0.0.0"},
  1666  						})
  1667  					})
  1668  
  1669  					it("sets version if version is set", func() {
  1670  						err := subject.Build(context.TODO(), BuildOptions{
  1671  							Image:      "some/app",
  1672  							Builder:    defaultBuilderName,
  1673  							ClearCache: true,
  1674  							ProjectDescriptor: projectTypes.Descriptor{
  1675  								Build: projectTypes.Build{
  1676  									Buildpacks: []projectTypes.Buildpack{{
  1677  										ID:      "my/inline",
  1678  										Version: "1.0.0-my-version",
  1679  										Script: projectTypes.Script{
  1680  											API:    "0.4",
  1681  											Inline: "touch foo.txt",
  1682  										},
  1683  									}},
  1684  								},
  1685  							},
  1686  							ProjectDescriptorBaseDir: tmpDir,
  1687  						})
  1688  
  1689  						h.AssertNil(t, err)
  1690  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1691  						bldr, err := builder.FromImage(defaultBuilderImage)
  1692  						h.AssertNil(t, err)
  1693  						h.AssertEq(t, bldr.Order(), dist.Order{
  1694  							{Group: []dist.ModuleRef{
  1695  								{ModuleInfo: dist.ModuleInfo{ID: "my/inline", Version: "1.0.0-my-version"}},
  1696  							}},
  1697  						})
  1698  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1699  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1700  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1701  							{ID: "my/inline", Version: "1.0.0-my-version"},
  1702  						})
  1703  					})
  1704  
  1705  					it("fails if there is no API", func() {
  1706  						err := subject.Build(context.TODO(), BuildOptions{
  1707  							Image:      "some/app",
  1708  							Builder:    defaultBuilderName,
  1709  							ClearCache: true,
  1710  							ProjectDescriptor: projectTypes.Descriptor{
  1711  								Build: projectTypes.Build{
  1712  									Buildpacks: []projectTypes.Buildpack{{
  1713  										ID: "my/inline",
  1714  										Script: projectTypes.Script{
  1715  											Inline: "touch foo.txt",
  1716  										},
  1717  									}},
  1718  								},
  1719  							},
  1720  							ProjectDescriptorBaseDir: tmpDir,
  1721  						})
  1722  
  1723  						h.AssertEq(t, "Missing API version for inline buildpack", err.Error())
  1724  					})
  1725  
  1726  					it("fails if there is no ID", func() {
  1727  						err := subject.Build(context.TODO(), BuildOptions{
  1728  							Image:      "some/app",
  1729  							Builder:    defaultBuilderName,
  1730  							ClearCache: true,
  1731  							ProjectDescriptor: projectTypes.Descriptor{
  1732  								Build: projectTypes.Build{
  1733  									Buildpacks: []projectTypes.Buildpack{{
  1734  										Script: projectTypes.Script{
  1735  											API:    "0.4",
  1736  											Inline: "touch foo.txt",
  1737  										},
  1738  									}},
  1739  								},
  1740  							},
  1741  							ProjectDescriptorBaseDir: tmpDir,
  1742  						})
  1743  
  1744  						h.AssertEq(t, "Invalid buildpack definition", err.Error())
  1745  					})
  1746  
  1747  					it("ignores script if there is a URI", func() {
  1748  						err := subject.Build(context.TODO(), BuildOptions{
  1749  							Image:      "some/app",
  1750  							Builder:    defaultBuilderName,
  1751  							ClearCache: true,
  1752  							ProjectDescriptor: projectTypes.Descriptor{
  1753  								Build: projectTypes.Build{
  1754  									Buildpacks: []projectTypes.Buildpack{{
  1755  										ID:      "buildpack.1.id",
  1756  										URI:     "some-uri",
  1757  										Version: "buildpack.1.version",
  1758  										Script: projectTypes.Script{
  1759  											Inline: "touch foo.txt",
  1760  										},
  1761  									}},
  1762  								},
  1763  							},
  1764  							ProjectDescriptorBaseDir: tmpDir,
  1765  						})
  1766  
  1767  						h.AssertContains(t, err.Error(), "extracting from registry 'some-uri'")
  1768  					})
  1769  				})
  1770  
  1771  				when("buildpack is from a registry", func() {
  1772  					var (
  1773  						fakePackage     *fakes.Image
  1774  						tmpDir          string
  1775  						registryFixture string
  1776  						packHome        string
  1777  
  1778  						configPath string
  1779  					)
  1780  
  1781  					it.Before(func() {
  1782  						var err error
  1783  						tmpDir, err = os.MkdirTemp("", "registry")
  1784  						h.AssertNil(t, err)
  1785  
  1786  						packHome = filepath.Join(tmpDir, ".pack")
  1787  						err = os.MkdirAll(packHome, 0755)
  1788  						h.AssertNil(t, err)
  1789  						os.Setenv("PACK_HOME", packHome)
  1790  
  1791  						registryFixture = h.CreateRegistryFixture(t, tmpDir, filepath.Join("testdata", "registry"))
  1792  
  1793  						configPath = filepath.Join(packHome, "config.toml")
  1794  						h.AssertNil(t, cfg.Write(cfg.Config{
  1795  							Registries: []cfg.Registry{
  1796  								{
  1797  									Name: "some-registry",
  1798  									Type: "github",
  1799  									URL:  registryFixture,
  1800  								},
  1801  							},
  1802  						}, configPath))
  1803  
  1804  						_, err = rg.NewRegistryCache(logger, tmpDir, registryFixture)
  1805  						h.AssertNil(t, err)
  1806  
  1807  						childBuildpackTar := ifakes.CreateBuildpackTar(t, tmpDir, dist.BuildpackDescriptor{
  1808  							WithAPI: api.MustParse("0.3"),
  1809  							WithInfo: dist.ModuleInfo{
  1810  								ID:      "example/foo",
  1811  								Version: "1.0.0",
  1812  							},
  1813  							WithStacks: []dist.Stack{
  1814  								{ID: defaultBuilderStackID},
  1815  							},
  1816  						})
  1817  
  1818  						bpLayers := dist.ModuleLayers{
  1819  							"example/foo": {
  1820  								"1.0.0": {
  1821  									API: api.MustParse("0.3"),
  1822  									Stacks: []dist.Stack{
  1823  										{ID: defaultBuilderStackID},
  1824  									},
  1825  									LayerDiffID: diffIDForFile(t, childBuildpackTar),
  1826  								},
  1827  							},
  1828  						}
  1829  
  1830  						md := buildpack.Metadata{
  1831  							ModuleInfo: dist.ModuleInfo{
  1832  								ID:      "example/foo",
  1833  								Version: "1.0.0",
  1834  							},
  1835  							Stacks: []dist.Stack{
  1836  								{ID: defaultBuilderStackID},
  1837  							},
  1838  						}
  1839  
  1840  						fakePackage = fakes.NewImage("example.com/some/package@sha256:8c27fe111c11b722081701dfed3bd55e039b9ce92865473cf4cdfa918071c566", "", nil)
  1841  						h.AssertNil(t, dist.SetLabel(fakePackage, "io.buildpacks.buildpack.layers", bpLayers))
  1842  						h.AssertNil(t, dist.SetLabel(fakePackage, "io.buildpacks.buildpackage.metadata", md))
  1843  
  1844  						h.AssertNil(t, fakePackage.AddLayer(childBuildpackTar))
  1845  
  1846  						fakeImageFetcher.LocalImages[fakePackage.Name()] = fakePackage
  1847  					})
  1848  
  1849  					it.After(func() {
  1850  						os.Unsetenv("PACK_HOME")
  1851  						h.AssertNil(t, os.RemoveAll(tmpDir))
  1852  					})
  1853  
  1854  					it("all buildpacks are added to ephemeral builder", func() {
  1855  						err := subject.Build(context.TODO(), BuildOptions{
  1856  							Image:      "some/app",
  1857  							Builder:    defaultBuilderName,
  1858  							ClearCache: true,
  1859  							Buildpacks: []string{
  1860  								"urn:cnb:registry:example/foo@1.0.0",
  1861  							},
  1862  							Registry: "some-registry",
  1863  						})
  1864  
  1865  						h.AssertNil(t, err)
  1866  						h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1867  						bldr, err := builder.FromImage(defaultBuilderImage)
  1868  						h.AssertNil(t, err)
  1869  						h.AssertEq(t, bldr.Order(), dist.Order{
  1870  							{Group: []dist.ModuleRef{
  1871  								{ModuleInfo: dist.ModuleInfo{ID: "example/foo", Version: "1.0.0"}},
  1872  							}},
  1873  						})
  1874  						h.AssertEq(t, bldr.Buildpacks(), []dist.ModuleInfo{
  1875  							{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  1876  							{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  1877  							{ID: "example/foo", Version: "1.0.0"},
  1878  						})
  1879  					})
  1880  				})
  1881  			})
  1882  		})
  1883  
  1884  		when("Extensions option", func() {
  1885  			it.Before(func() {
  1886  				subject.experimental = true
  1887  				defaultBuilderImage.SetLabel("io.buildpacks.buildpack.order-extensions", `[{"group":[{"id":"extension.1.id","version":"extension.1.version"}]}, {"group":[{"id":"extension.2.id","version":"extension.2.version"}]}]`)
  1888  				defaultWindowsBuilderImage.SetLabel("io.buildpacks.buildpack.order-extensions", `[{"group":[{"id":"extension.1.id","version":"extension.1.version"}]}, {"group":[{"id":"extension.2.id","version":"extension.2.version"}]}]`)
  1889  			})
  1890  
  1891  			assertOrderEquals := func(content string) {
  1892  				t.Helper()
  1893  
  1894  				orderLayer, err := defaultBuilderImage.FindLayerWithPath("/cnb/order.toml")
  1895  				h.AssertNil(t, err)
  1896  				h.AssertOnTarEntry(t, orderLayer, "/cnb/order.toml", h.ContentEquals(content))
  1897  			}
  1898  
  1899  			it("builder order-extensions is overwritten", func() {
  1900  				additionalEx := ifakes.CreateExtensionTar(t, tmpDir, dist.ExtensionDescriptor{
  1901  					WithAPI: api.MustParse("0.7"),
  1902  					WithInfo: dist.ModuleInfo{
  1903  						ID:      "extension.add.1.id",
  1904  						Version: "extension.add.1.version",
  1905  					},
  1906  				})
  1907  
  1908  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  1909  					Image:      "some/app",
  1910  					Builder:    defaultBuilderName,
  1911  					ClearCache: true,
  1912  					Extensions: []string{additionalEx},
  1913  				}))
  1914  				h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1915  
  1916  				assertOrderEquals(`[[order]]
  1917  
  1918    [[order.group]]
  1919      id = "buildpack.1.id"
  1920      version = "buildpack.1.version"
  1921  
  1922  [[order]]
  1923  
  1924    [[order.group]]
  1925      id = "buildpack.2.id"
  1926      version = "buildpack.2.version"
  1927  
  1928  [[order-extensions]]
  1929  
  1930    [[order-extensions.group]]
  1931      id = "extension.add.1.id"
  1932      version = "extension.add.1.version"
  1933  `)
  1934  			})
  1935  
  1936  			when("id - no version is provided", func() {
  1937  				it("resolves version", func() {
  1938  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  1939  						Image:      "some/app",
  1940  						Builder:    defaultBuilderName,
  1941  						ClearCache: true,
  1942  						Extensions: []string{"extension.1.id"},
  1943  					}))
  1944  					h.AssertEq(t, fakeLifecycle.Opts.Builder.Name(), defaultBuilderImage.Name())
  1945  
  1946  					assertOrderEquals(`[[order]]
  1947  
  1948    [[order.group]]
  1949      id = "buildpack.1.id"
  1950      version = "buildpack.1.version"
  1951  
  1952  [[order]]
  1953  
  1954    [[order.group]]
  1955      id = "buildpack.2.id"
  1956      version = "buildpack.2.version"
  1957  
  1958  [[order-extensions]]
  1959  
  1960    [[order-extensions.group]]
  1961      id = "extension.1.id"
  1962      version = "extension.1.version"
  1963  `)
  1964  				})
  1965  			})
  1966  		})
  1967  
  1968  		//TODO: "all buildpacks are added to ephemeral builder" test after extractPackaged() is completed.
  1969  
  1970  		when("ProjectDescriptor", func() {
  1971  			when("project metadata", func() {
  1972  				when("not experimental", func() {
  1973  					it("does not set project source", func() {
  1974  						err := subject.Build(context.TODO(), BuildOptions{
  1975  							Image:      "some/app",
  1976  							Builder:    defaultBuilderName,
  1977  							ClearCache: true,
  1978  							ProjectDescriptor: projectTypes.Descriptor{
  1979  								Project: projectTypes.Project{
  1980  									Version:   "1.2.3",
  1981  									SourceURL: "https://example.com",
  1982  								},
  1983  							},
  1984  						})
  1985  
  1986  						h.AssertNil(t, err)
  1987  						h.AssertNil(t, fakeLifecycle.Opts.ProjectMetadata.Source)
  1988  					})
  1989  				})
  1990  
  1991  				when("is experimental", func() {
  1992  					it.Before(func() {
  1993  						subject.experimental = true
  1994  					})
  1995  
  1996  					when("missing information", func() {
  1997  						it("does not set project source", func() {
  1998  							err := subject.Build(context.TODO(), BuildOptions{
  1999  								Image:             "some/app",
  2000  								Builder:           defaultBuilderName,
  2001  								ClearCache:        true,
  2002  								ProjectDescriptor: projectTypes.Descriptor{},
  2003  							})
  2004  
  2005  							h.AssertNil(t, err)
  2006  							h.AssertNil(t, fakeLifecycle.Opts.ProjectMetadata.Source)
  2007  						})
  2008  					})
  2009  
  2010  					it("sets project source", func() {
  2011  						err := subject.Build(context.TODO(), BuildOptions{
  2012  							Image:      "some/app",
  2013  							Builder:    defaultBuilderName,
  2014  							ClearCache: true,
  2015  							ProjectDescriptor: projectTypes.Descriptor{
  2016  								Project: projectTypes.Project{
  2017  									Version:   "1.2.3",
  2018  									SourceURL: "https://example.com",
  2019  								},
  2020  							},
  2021  						})
  2022  
  2023  						h.AssertNil(t, err)
  2024  						h.AssertNotNil(t, fakeLifecycle.Opts.ProjectMetadata.Source)
  2025  						h.AssertEq(t, fakeLifecycle.Opts.ProjectMetadata.Source, &files.ProjectSource{
  2026  							Type:     "project",
  2027  							Version:  map[string]interface{}{"declared": "1.2.3"},
  2028  							Metadata: map[string]interface{}{"url": "https://example.com"},
  2029  						})
  2030  					})
  2031  				})
  2032  			})
  2033  		})
  2034  
  2035  		when("Env option", func() {
  2036  			it("should set the env on the ephemeral builder", func() {
  2037  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2038  					Image:   "some/app",
  2039  					Builder: defaultBuilderName,
  2040  					Env: map[string]string{
  2041  						"key1": "value1",
  2042  						"key2": "value2",
  2043  					},
  2044  				}))
  2045  				layerTar, err := defaultBuilderImage.FindLayerWithPath("/platform/env/key1")
  2046  				h.AssertNil(t, err)
  2047  				h.AssertTarFileContents(t, layerTar, "/platform/env/key1", `value1`)
  2048  				h.AssertTarFileContents(t, layerTar, "/platform/env/key2", `value2`)
  2049  			})
  2050  		})
  2051  
  2052  		when("Publish option", func() {
  2053  			var remoteRunImage, builderWithoutLifecycleImageOrCreator *fakes.Image
  2054  
  2055  			it.Before(func() {
  2056  				remoteRunImage = fakes.NewImage("default/run", "", nil)
  2057  				h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
  2058  				h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
  2059  				fakeImageFetcher.RemoteImages[remoteRunImage.Name()] = remoteRunImage
  2060  
  2061  				builderWithoutLifecycleImageOrCreator = newFakeBuilderImage(
  2062  					t,
  2063  					tmpDir,
  2064  					"example.com/supportscreator/builder:tag",
  2065  					"some.stack.id",
  2066  					defaultRunImageName,
  2067  					"0.3.0",
  2068  					newLinuxImage,
  2069  				)
  2070  				h.AssertNil(t, builderWithoutLifecycleImageOrCreator.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "build:mixinB", "mixinX", "build:mixinY"]`))
  2071  				fakeImageFetcher.LocalImages[builderWithoutLifecycleImageOrCreator.Name()] = builderWithoutLifecycleImageOrCreator
  2072  			})
  2073  
  2074  			it.After(func() {
  2075  				remoteRunImage.Cleanup()
  2076  				builderWithoutLifecycleImageOrCreator.Cleanup()
  2077  			})
  2078  
  2079  			when("true", func() {
  2080  				it("uses a remote run image", func() {
  2081  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2082  						Image:   "some/app",
  2083  						Builder: defaultBuilderName,
  2084  						Publish: true,
  2085  					}))
  2086  					h.AssertEq(t, fakeLifecycle.Opts.Publish, true)
  2087  
  2088  					args := fakeImageFetcher.FetchCalls[defaultBuilderName]
  2089  					h.AssertEq(t, args.Daemon, true)
  2090  
  2091  					args = fakeImageFetcher.FetchCalls["default/run"]
  2092  					h.AssertEq(t, args.Daemon, false)
  2093  					h.AssertEq(t, args.Platform, "linux/amd64")
  2094  				})
  2095  
  2096  				when("builder is untrusted", func() {
  2097  					when("lifecycle image is available", func() {
  2098  						it("uses the 5 phases with the lifecycle image", func() {
  2099  							origLifecyleName := fakeLifecycleImage.Name()
  2100  
  2101  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2102  								Image:        "some/app",
  2103  								Builder:      defaultBuilderName,
  2104  								Publish:      true,
  2105  								TrustBuilder: func(string) bool { return false },
  2106  							}))
  2107  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, false)
  2108  							h.AssertContains(t, fakeLifecycle.Opts.LifecycleImage, "pack.local/lifecycle")
  2109  							args := fakeImageFetcher.FetchCalls[origLifecyleName]
  2110  							h.AssertNotNil(t, args)
  2111  							h.AssertEq(t, args.Daemon, true)
  2112  							h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2113  							h.AssertEq(t, args.Platform, "linux/amd64")
  2114  						})
  2115  						it("uses the api versions of the lifecycle image", func() {
  2116  							h.AssertTrue(t, true)
  2117  						})
  2118  						it("parses the versions correctly", func() {
  2119  							fakeLifecycleImage.SetLabel("io.buildpacks.lifecycle.apis", "{\"platform\":{\"deprecated\":[\"0.1\",\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\"],\"supported\":[\"0.7\",\"0.8\",\"0.9\",\"0.10\",\"0.11\",\"0.12\"]}}")
  2120  
  2121  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2122  								Image:        "some/app",
  2123  								Builder:      defaultBuilderName,
  2124  								Publish:      true,
  2125  								TrustBuilder: func(string) bool { return false },
  2126  							}))
  2127  							h.AssertSliceContainsInOrder(t, fakeLifecycle.Opts.LifecycleApis, "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "0.10", "0.11", "0.12")
  2128  						})
  2129  					})
  2130  
  2131  					when("lifecycle image is not available", func() {
  2132  						it("errors", func() {
  2133  							h.AssertNotNil(t, subject.Build(context.TODO(), BuildOptions{
  2134  								Image:        "some/app",
  2135  								Builder:      builderWithoutLifecycleImageOrCreator.Name(),
  2136  								Publish:      true,
  2137  								TrustBuilder: func(string) bool { return false },
  2138  							}))
  2139  						})
  2140  					})
  2141  				})
  2142  
  2143  				when("builder is trusted", func() {
  2144  					when("lifecycle supports creator", func() {
  2145  						it("uses the creator with the provided builder", func() {
  2146  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2147  								Image:        "some/app",
  2148  								Builder:      defaultBuilderName,
  2149  								Publish:      true,
  2150  								TrustBuilder: func(string) bool { return true },
  2151  							}))
  2152  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, true)
  2153  
  2154  							args := fakeImageFetcher.FetchCalls[fakeLifecycleImage.Name()]
  2155  							h.AssertNil(t, args)
  2156  						})
  2157  					})
  2158  
  2159  					when("lifecycle doesn't support creator", func() {
  2160  						// the default test builder (example.com/default/builder:tag) has lifecycle version 0.3.0, so creator is not supported
  2161  						it("uses the 5 phases with the provided builder", func() {
  2162  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2163  								Image:        "some/app",
  2164  								Builder:      builderWithoutLifecycleImageOrCreator.Name(),
  2165  								Publish:      true,
  2166  								TrustBuilder: func(string) bool { return true },
  2167  							}))
  2168  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, false)
  2169  							h.AssertEq(t, fakeLifecycle.Opts.LifecycleImage, builderWithoutLifecycleImageOrCreator.Name())
  2170  
  2171  							args := fakeImageFetcher.FetchCalls[fakeLifecycleImage.Name()]
  2172  							h.AssertNil(t, args)
  2173  						})
  2174  					})
  2175  				})
  2176  			})
  2177  
  2178  			when("false", func() {
  2179  				it("uses a local run image", func() {
  2180  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2181  						Image:   "some/app",
  2182  						Builder: defaultBuilderName,
  2183  						Publish: false,
  2184  					}))
  2185  					h.AssertEq(t, fakeLifecycle.Opts.Publish, false)
  2186  
  2187  					args := fakeImageFetcher.FetchCalls["default/run"]
  2188  					h.AssertEq(t, args.Daemon, true)
  2189  					h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2190  
  2191  					args = fakeImageFetcher.FetchCalls[defaultBuilderName]
  2192  					h.AssertEq(t, args.Daemon, true)
  2193  					h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2194  				})
  2195  
  2196  				when("builder is untrusted", func() {
  2197  					when("lifecycle image is available", func() {
  2198  						it("uses the 5 phases with the lifecycle image", func() {
  2199  							origLifecyleName := fakeLifecycleImage.Name()
  2200  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2201  								Image:        "some/app",
  2202  								Builder:      defaultBuilderName,
  2203  								Publish:      false,
  2204  								TrustBuilder: func(string) bool { return false },
  2205  							}))
  2206  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, false)
  2207  							h.AssertContains(t, fakeLifecycle.Opts.LifecycleImage, "pack.local/lifecycle")
  2208  							args := fakeImageFetcher.FetchCalls[origLifecyleName]
  2209  							h.AssertNotNil(t, args)
  2210  							h.AssertEq(t, args.Daemon, true)
  2211  							h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2212  							h.AssertEq(t, args.Platform, "linux/amd64")
  2213  						})
  2214  					})
  2215  
  2216  					when("lifecycle image is not available", func() {
  2217  						it("errors", func() {
  2218  							h.AssertNotNil(t, subject.Build(context.TODO(), BuildOptions{
  2219  								Image:        "some/app",
  2220  								Builder:      builderWithoutLifecycleImageOrCreator.Name(),
  2221  								Publish:      false,
  2222  								TrustBuilder: func(string) bool { return false },
  2223  							}))
  2224  						})
  2225  					})
  2226  				})
  2227  
  2228  				when("builder is trusted", func() {
  2229  					when("lifecycle supports creator", func() {
  2230  						it("uses the creator with the provided builder", func() {
  2231  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2232  								Image:        "some/app",
  2233  								Builder:      defaultBuilderName,
  2234  								Publish:      false,
  2235  								TrustBuilder: func(string) bool { return true },
  2236  							}))
  2237  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, true)
  2238  
  2239  							args := fakeImageFetcher.FetchCalls[fakeLifecycleImage.Name()]
  2240  							h.AssertNil(t, args)
  2241  						})
  2242  					})
  2243  
  2244  					when("lifecycle doesn't support creator", func() {
  2245  						// the default test builder (example.com/default/builder:tag) has lifecycle version 0.3.0, so creator is not supported
  2246  						it("uses the 5 phases with the provided builder", func() {
  2247  							h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2248  								Image:        "some/app",
  2249  								Builder:      builderWithoutLifecycleImageOrCreator.Name(),
  2250  								Publish:      false,
  2251  								TrustBuilder: func(string) bool { return true },
  2252  							}))
  2253  							h.AssertEq(t, fakeLifecycle.Opts.UseCreator, false)
  2254  							h.AssertEq(t, fakeLifecycle.Opts.LifecycleImage, builderWithoutLifecycleImageOrCreator.Name())
  2255  
  2256  							args := fakeImageFetcher.FetchCalls[fakeLifecycleImage.Name()]
  2257  							h.AssertNil(t, args)
  2258  						})
  2259  					})
  2260  				})
  2261  			})
  2262  		})
  2263  
  2264  		when("PullPolicy", func() {
  2265  			when("never", func() {
  2266  				it("uses the local builder and run images without updating", func() {
  2267  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2268  						Image:      "some/app",
  2269  						Builder:    defaultBuilderName,
  2270  						PullPolicy: image.PullNever,
  2271  					}))
  2272  
  2273  					args := fakeImageFetcher.FetchCalls["default/run"]
  2274  					h.AssertEq(t, args.Daemon, true)
  2275  					h.AssertEq(t, args.PullPolicy, image.PullNever)
  2276  
  2277  					args = fakeImageFetcher.FetchCalls[defaultBuilderName]
  2278  					h.AssertEq(t, args.Daemon, true)
  2279  					h.AssertEq(t, args.PullPolicy, image.PullNever)
  2280  
  2281  					args = fakeImageFetcher.FetchCalls[fmt.Sprintf("%s:%s", cfg.DefaultLifecycleImageRepo, builder.DefaultLifecycleVersion)]
  2282  					h.AssertEq(t, args.Daemon, true)
  2283  					h.AssertEq(t, args.PullPolicy, image.PullNever)
  2284  					h.AssertEq(t, args.Platform, "linux/amd64")
  2285  				})
  2286  			})
  2287  
  2288  			when("always", func() {
  2289  				it("uses pulls the builder and run image before using them", func() {
  2290  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2291  						Image:      "some/app",
  2292  						Builder:    defaultBuilderName,
  2293  						PullPolicy: image.PullAlways,
  2294  					}))
  2295  
  2296  					args := fakeImageFetcher.FetchCalls["default/run"]
  2297  					h.AssertEq(t, args.Daemon, true)
  2298  					h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2299  
  2300  					args = fakeImageFetcher.FetchCalls[defaultBuilderName]
  2301  					h.AssertEq(t, args.Daemon, true)
  2302  					h.AssertEq(t, args.PullPolicy, image.PullAlways)
  2303  				})
  2304  			})
  2305  		})
  2306  
  2307  		when("ProxyConfig option", func() {
  2308  			when("ProxyConfig is nil", func() {
  2309  				it.Before(func() {
  2310  					h.AssertNil(t, os.Setenv("http_proxy", "other-http-proxy"))
  2311  					h.AssertNil(t, os.Setenv("https_proxy", "other-https-proxy"))
  2312  					h.AssertNil(t, os.Setenv("no_proxy", "other-no-proxy"))
  2313  				})
  2314  
  2315  				when("*_PROXY env vars are set", func() {
  2316  					it.Before(func() {
  2317  						h.AssertNil(t, os.Setenv("HTTP_PROXY", "some-http-proxy"))
  2318  						h.AssertNil(t, os.Setenv("HTTPS_PROXY", "some-https-proxy"))
  2319  						h.AssertNil(t, os.Setenv("NO_PROXY", "some-no-proxy"))
  2320  					})
  2321  
  2322  					it.After(func() {
  2323  						h.AssertNilE(t, os.Unsetenv("HTTP_PROXY"))
  2324  						h.AssertNilE(t, os.Unsetenv("HTTPS_PROXY"))
  2325  						h.AssertNilE(t, os.Unsetenv("NO_PROXY"))
  2326  					})
  2327  
  2328  					it("defaults to the *_PROXY environment variables", func() {
  2329  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2330  							Image:   "some/app",
  2331  							Builder: defaultBuilderName,
  2332  						}))
  2333  						h.AssertEq(t, fakeLifecycle.Opts.HTTPProxy, "some-http-proxy")
  2334  						h.AssertEq(t, fakeLifecycle.Opts.HTTPSProxy, "some-https-proxy")
  2335  						h.AssertEq(t, fakeLifecycle.Opts.NoProxy, "some-no-proxy")
  2336  					})
  2337  				})
  2338  
  2339  				it("falls back to the *_proxy environment variables", func() {
  2340  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2341  						Image:   "some/app",
  2342  						Builder: defaultBuilderName,
  2343  					}))
  2344  					h.AssertEq(t, fakeLifecycle.Opts.HTTPProxy, "other-http-proxy")
  2345  					h.AssertEq(t, fakeLifecycle.Opts.HTTPSProxy, "other-https-proxy")
  2346  					h.AssertEq(t, fakeLifecycle.Opts.NoProxy, "other-no-proxy")
  2347  				})
  2348  			}, spec.Sequential())
  2349  
  2350  			when("ProxyConfig is not nil", func() {
  2351  				it("passes the values through", func() {
  2352  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2353  						Image:   "some/app",
  2354  						Builder: defaultBuilderName,
  2355  						ProxyConfig: &ProxyConfig{
  2356  							HTTPProxy:  "custom-http-proxy",
  2357  							HTTPSProxy: "custom-https-proxy",
  2358  							NoProxy:    "custom-no-proxy",
  2359  						},
  2360  					}))
  2361  					h.AssertEq(t, fakeLifecycle.Opts.HTTPProxy, "custom-http-proxy")
  2362  					h.AssertEq(t, fakeLifecycle.Opts.HTTPSProxy, "custom-https-proxy")
  2363  					h.AssertEq(t, fakeLifecycle.Opts.NoProxy, "custom-no-proxy")
  2364  				})
  2365  			})
  2366  		})
  2367  
  2368  		when("Network option", func() {
  2369  			it("passes the value through", func() {
  2370  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2371  					Image:   "some/app",
  2372  					Builder: defaultBuilderName,
  2373  					ContainerConfig: ContainerConfig{
  2374  						Network: "some-network",
  2375  					},
  2376  				}))
  2377  				h.AssertEq(t, fakeLifecycle.Opts.Network, "some-network")
  2378  			})
  2379  		})
  2380  
  2381  		when("Lifecycle option", func() {
  2382  			when("Platform API", func() {
  2383  				for _, supportedPlatformAPI := range []string{"0.3", "0.4"} {
  2384  					var (
  2385  						supportedPlatformAPI = supportedPlatformAPI
  2386  						compatibleBuilder    *fakes.Image
  2387  					)
  2388  
  2389  					when(fmt.Sprintf("lifecycle platform API is compatible (%s)", supportedPlatformAPI), func() {
  2390  						it.Before(func() {
  2391  							compatibleBuilder = ifakes.NewFakeBuilderImage(t,
  2392  								tmpDir,
  2393  								"compatible-"+defaultBuilderName,
  2394  								defaultBuilderStackID,
  2395  								"1234",
  2396  								"5678",
  2397  								builder.Metadata{
  2398  									Stack: builder.StackMetadata{
  2399  										RunImage: builder.RunImageMetadata{
  2400  											Image: "default/run",
  2401  											Mirrors: []string{
  2402  												"registry1.example.com/run/mirror",
  2403  												"registry2.example.com/run/mirror",
  2404  											},
  2405  										},
  2406  									},
  2407  									Lifecycle: builder.LifecycleMetadata{
  2408  										LifecycleInfo: builder.LifecycleInfo{
  2409  											Version: &builder.Version{
  2410  												Version: *semver.MustParse(builder.DefaultLifecycleVersion),
  2411  											},
  2412  										},
  2413  										APIs: builder.LifecycleAPIs{
  2414  											Buildpack: builder.APIVersions{
  2415  												Supported: builder.APISet{api.MustParse("0.2"), api.MustParse("0.3"), api.MustParse("0.4")},
  2416  											},
  2417  											Platform: builder.APIVersions{
  2418  												Supported: builder.APISet{api.MustParse(supportedPlatformAPI)},
  2419  											},
  2420  										},
  2421  									},
  2422  								},
  2423  								nil,
  2424  								nil,
  2425  								nil,
  2426  								nil,
  2427  								newLinuxImage,
  2428  							)
  2429  
  2430  							fakeImageFetcher.LocalImages[compatibleBuilder.Name()] = compatibleBuilder
  2431  						})
  2432  
  2433  						it("should succeed", func() {
  2434  							err := subject.Build(context.TODO(), BuildOptions{
  2435  								Image:   "some/app",
  2436  								Builder: compatibleBuilder.Name(),
  2437  							})
  2438  
  2439  							h.AssertNil(t, err)
  2440  						})
  2441  					})
  2442  				}
  2443  
  2444  				when("lifecycle Platform API is not compatible", func() {
  2445  					var incompatibleBuilderImage *fakes.Image
  2446  					it.Before(func() {
  2447  						incompatibleBuilderImage = ifakes.NewFakeBuilderImage(t,
  2448  							tmpDir,
  2449  							"incompatible-"+defaultBuilderName,
  2450  							defaultBuilderStackID,
  2451  							"1234",
  2452  							"5678",
  2453  							builder.Metadata{
  2454  								Stack: builder.StackMetadata{
  2455  									RunImage: builder.RunImageMetadata{
  2456  										Image: "default/run",
  2457  										Mirrors: []string{
  2458  											"registry1.example.com/run/mirror",
  2459  											"registry2.example.com/run/mirror",
  2460  										},
  2461  									},
  2462  								},
  2463  								Lifecycle: builder.LifecycleMetadata{
  2464  									LifecycleInfo: builder.LifecycleInfo{
  2465  										Version: &builder.Version{
  2466  											Version: *semver.MustParse(builder.DefaultLifecycleVersion),
  2467  										},
  2468  									},
  2469  									API: builder.LifecycleAPI{
  2470  										BuildpackVersion: api.MustParse("0.3"),
  2471  										PlatformVersion:  api.MustParse("0.1"),
  2472  									},
  2473  								},
  2474  							},
  2475  							nil,
  2476  							nil,
  2477  							nil,
  2478  							nil,
  2479  							newLinuxImage,
  2480  						)
  2481  
  2482  						fakeImageFetcher.LocalImages[incompatibleBuilderImage.Name()] = incompatibleBuilderImage
  2483  					})
  2484  
  2485  					it.After(func() {
  2486  						incompatibleBuilderImage.Cleanup()
  2487  					})
  2488  
  2489  					it("should error", func() {
  2490  						builderName := incompatibleBuilderImage.Name()
  2491  
  2492  						err := subject.Build(context.TODO(), BuildOptions{
  2493  							Image:   "some/app",
  2494  							Builder: builderName,
  2495  						})
  2496  
  2497  						h.AssertError(t, err, fmt.Sprintf("Builder %s is incompatible with this version of pack", style.Symbol(builderName)))
  2498  					})
  2499  				})
  2500  
  2501  				when("supported Platform APIs not specified", func() {
  2502  					var badBuilderImage *fakes.Image
  2503  					it.Before(func() {
  2504  						badBuilderImage = ifakes.NewFakeBuilderImage(t,
  2505  							tmpDir,
  2506  							"incompatible-"+defaultBuilderName,
  2507  							defaultBuilderStackID,
  2508  							"1234",
  2509  							"5678",
  2510  							builder.Metadata{
  2511  								Stack: builder.StackMetadata{
  2512  									RunImage: builder.RunImageMetadata{
  2513  										Image: "default/run",
  2514  										Mirrors: []string{
  2515  											"registry1.example.com/run/mirror",
  2516  											"registry2.example.com/run/mirror",
  2517  										},
  2518  									},
  2519  								},
  2520  								Lifecycle: builder.LifecycleMetadata{
  2521  									LifecycleInfo: builder.LifecycleInfo{
  2522  										Version: &builder.Version{
  2523  											Version: *semver.MustParse(builder.DefaultLifecycleVersion),
  2524  										},
  2525  									},
  2526  									APIs: builder.LifecycleAPIs{
  2527  										Buildpack: builder.APIVersions{Supported: builder.APISet{api.MustParse("0.2")}},
  2528  									},
  2529  								},
  2530  							},
  2531  							nil,
  2532  							nil,
  2533  							nil,
  2534  							nil,
  2535  							newLinuxImage,
  2536  						)
  2537  
  2538  						fakeImageFetcher.LocalImages[badBuilderImage.Name()] = badBuilderImage
  2539  					})
  2540  
  2541  					it.After(func() {
  2542  						badBuilderImage.Cleanup()
  2543  					})
  2544  
  2545  					it("should error", func() {
  2546  						builderName := badBuilderImage.Name()
  2547  
  2548  						err := subject.Build(context.TODO(), BuildOptions{
  2549  							Image:   "some/app",
  2550  							Builder: builderName,
  2551  						})
  2552  
  2553  						h.AssertError(t, err, "supported Lifecycle Platform APIs not specified")
  2554  					})
  2555  				})
  2556  			})
  2557  
  2558  			when("Buildpack API", func() {
  2559  				when("supported Buildpack APIs not specified", func() {
  2560  					var badBuilderImage *fakes.Image
  2561  					it.Before(func() {
  2562  						badBuilderImage = ifakes.NewFakeBuilderImage(t,
  2563  							tmpDir,
  2564  							"incompatible-"+defaultBuilderName,
  2565  							defaultBuilderStackID,
  2566  							"1234",
  2567  							"5678",
  2568  							builder.Metadata{
  2569  								Stack: builder.StackMetadata{
  2570  									RunImage: builder.RunImageMetadata{
  2571  										Image: "default/run",
  2572  										Mirrors: []string{
  2573  											"registry1.example.com/run/mirror",
  2574  											"registry2.example.com/run/mirror",
  2575  										},
  2576  									},
  2577  								},
  2578  								Lifecycle: builder.LifecycleMetadata{
  2579  									LifecycleInfo: builder.LifecycleInfo{
  2580  										Version: &builder.Version{
  2581  											Version: *semver.MustParse(builder.DefaultLifecycleVersion),
  2582  										},
  2583  									},
  2584  									APIs: builder.LifecycleAPIs{
  2585  										Platform: builder.APIVersions{Supported: builder.APISet{api.MustParse("0.4")}},
  2586  									},
  2587  								},
  2588  							},
  2589  							nil,
  2590  							nil,
  2591  							nil,
  2592  							nil,
  2593  							newLinuxImage,
  2594  						)
  2595  
  2596  						fakeImageFetcher.LocalImages[badBuilderImage.Name()] = badBuilderImage
  2597  					})
  2598  
  2599  					it.After(func() {
  2600  						badBuilderImage.Cleanup()
  2601  					})
  2602  
  2603  					it("should error", func() {
  2604  						builderName := badBuilderImage.Name()
  2605  
  2606  						err := subject.Build(context.TODO(), BuildOptions{
  2607  							Image:   "some/app",
  2608  							Builder: builderName,
  2609  						})
  2610  
  2611  						h.AssertError(t, err, "supported Lifecycle Buildpack APIs not specified")
  2612  					})
  2613  				})
  2614  			})
  2615  
  2616  			when("use creator with extensions", func() {
  2617  				when("lifecycle is old", func() {
  2618  					it("false", func() {
  2619  						oldLifecycleBuilder := newFakeBuilderImage(t, tmpDir, "example.com/old-lifecycle-builder:tag", defaultBuilderStackID, defaultRunImageName, "0.18.0", newLinuxImage)
  2620  						defer oldLifecycleBuilder.Cleanup()
  2621  						fakeImageFetcher.LocalImages[oldLifecycleBuilder.Name()] = oldLifecycleBuilder
  2622  
  2623  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2624  							Image:        "some/app",
  2625  							Builder:      oldLifecycleBuilder.Name(),
  2626  							TrustBuilder: func(string) bool { return true },
  2627  						}))
  2628  
  2629  						h.AssertEq(t, fakeLifecycle.Opts.UseCreatorWithExtensions, false)
  2630  					})
  2631  				})
  2632  
  2633  				when("lifecycle is new", func() {
  2634  					it("true", func() {
  2635  						newLifecycleBuilder := newFakeBuilderImage(t, tmpDir, "example.com/new-lifecycle-builder:tag", defaultBuilderStackID, defaultRunImageName, "0.19.0", newLinuxImage)
  2636  						defer newLifecycleBuilder.Cleanup()
  2637  						fakeImageFetcher.LocalImages[newLifecycleBuilder.Name()] = newLifecycleBuilder
  2638  
  2639  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2640  							Image:        "some/app",
  2641  							Builder:      newLifecycleBuilder.Name(),
  2642  							TrustBuilder: func(string) bool { return true },
  2643  						}))
  2644  
  2645  						h.AssertEq(t, fakeLifecycle.Opts.UseCreatorWithExtensions, true)
  2646  					})
  2647  				})
  2648  			})
  2649  		})
  2650  
  2651  		when("validating mixins", func() {
  2652  			when("stack image mixins disagree", func() {
  2653  				it.Before(func() {
  2654  					h.AssertNil(t, defaultBuilderImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA"]`))
  2655  					h.AssertNil(t, fakeDefaultRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinB"]`))
  2656  				})
  2657  
  2658  				it("succeeds", func() {
  2659  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2660  						Image:   "some/app",
  2661  						Builder: defaultBuilderName,
  2662  					}))
  2663  				})
  2664  
  2665  				when("platform API < 0.12", func() {
  2666  					it.Before(func() {
  2667  						setAPIs(t, defaultBuilderImage, []string{"0.8"}, []string{"0.11"})
  2668  					})
  2669  
  2670  					it("returns an error", func() {
  2671  						err := subject.Build(context.TODO(), BuildOptions{
  2672  							Image:   "some/app",
  2673  							Builder: defaultBuilderName,
  2674  						})
  2675  
  2676  						h.AssertError(t, err, "validating stack mixins: 'default/run' missing required mixin(s): mixinA")
  2677  					})
  2678  				})
  2679  			})
  2680  
  2681  			when("builder buildpack mixins are not satisfied", func() {
  2682  				it.Before(func() {
  2683  					h.AssertNil(t, defaultBuilderImage.SetLabel("io.buildpacks.stack.mixins", ""))
  2684  					h.AssertNil(t, fakeDefaultRunImage.SetLabel("io.buildpacks.stack.mixins", ""))
  2685  				})
  2686  
  2687  				it("succeeds", func() {
  2688  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2689  						Image:   "some/app",
  2690  						Builder: defaultBuilderName,
  2691  					}))
  2692  				})
  2693  
  2694  				when("platform API < 0.12", func() {
  2695  					it.Before(func() {
  2696  						setAPIs(t, defaultBuilderImage, []string{"0.8"}, []string{"0.11"})
  2697  					})
  2698  
  2699  					it("returns an error", func() {
  2700  						err := subject.Build(context.TODO(), BuildOptions{
  2701  							Image:   "some/app",
  2702  							Builder: defaultBuilderName,
  2703  						})
  2704  
  2705  						h.AssertError(t, err, "validating stack mixins: buildpack 'buildpack.1.id@buildpack.1.version' requires missing mixin(s): build:mixinY, mixinX, run:mixinZ")
  2706  					})
  2707  				})
  2708  			})
  2709  		})
  2710  
  2711  		when("Volumes option", func() {
  2712  			when("on posix", func() {
  2713  				it.Before(func() {
  2714  					h.SkipIf(t, runtime.GOOS == "windows", "Skipped on windows")
  2715  				})
  2716  
  2717  				for _, test := range []struct {
  2718  					name        string
  2719  					volume      string
  2720  					expectation string
  2721  				}{
  2722  					{"defaults to read-only", "/a:/x", "/a:/x:ro"},
  2723  					{"defaults to read-only (nested)", "/a:/some/path/y", "/a:/some/path/y:ro"},
  2724  					{"supports rw mode", "/a:/x:rw", "/a:/x:rw"},
  2725  				} {
  2726  					volume := test.volume
  2727  					expectation := test.expectation
  2728  
  2729  					it(test.name, func() {
  2730  						err := subject.Build(context.TODO(), BuildOptions{
  2731  							Image:   "some/app",
  2732  							Builder: defaultBuilderName,
  2733  							ContainerConfig: ContainerConfig{
  2734  								Volumes: []string{volume},
  2735  							},
  2736  						})
  2737  						h.AssertNil(t, err)
  2738  						h.AssertEq(t, fakeLifecycle.Opts.Volumes, []string{expectation})
  2739  					})
  2740  				}
  2741  
  2742  				when("volume mode is invalid", func() {
  2743  					it("returns an error", func() {
  2744  						err := subject.Build(context.TODO(), BuildOptions{
  2745  							Image:   "some/app",
  2746  							Builder: defaultBuilderName,
  2747  							ContainerConfig: ContainerConfig{
  2748  								Volumes: []string{"/a:/x:invalid"},
  2749  							},
  2750  						})
  2751  						h.AssertError(t, err, `platform volume "/a:/x:invalid" has invalid format: invalid mode: invalid`)
  2752  					})
  2753  				})
  2754  
  2755  				when("volume specification is invalid", func() {
  2756  					it("returns an error", func() {
  2757  						err := subject.Build(context.TODO(), BuildOptions{
  2758  							Image:   "some/app",
  2759  							Builder: defaultBuilderName,
  2760  							ContainerConfig: ContainerConfig{
  2761  								Volumes: []string{":::"},
  2762  							},
  2763  						})
  2764  						if runtime.GOOS == "darwin" {
  2765  							h.AssertError(t, err, `platform volume ":::" has invalid format: invalid spec: :::: empty section between colons`)
  2766  						} else {
  2767  							h.AssertError(t, err, `platform volume ":::" has invalid format: invalid volume specification: ':::'`)
  2768  						}
  2769  					})
  2770  				})
  2771  
  2772  				when("mounting onto cnb spec'd dir", func() {
  2773  					for _, p := range []string{
  2774  						"/cnb/buildpacks",
  2775  						"/cnb/buildpacks/nested",
  2776  						"/cnb",
  2777  						"/cnb/nested",
  2778  						"/layers",
  2779  						"/layers/nested",
  2780  					} {
  2781  						p := p
  2782  						it(fmt.Sprintf("warns when mounting to '%s'", p), func() {
  2783  							err := subject.Build(context.TODO(), BuildOptions{
  2784  								Image:   "some/app",
  2785  								Builder: defaultBuilderName,
  2786  								ContainerConfig: ContainerConfig{
  2787  									Volumes: []string{fmt.Sprintf("/tmp/path:%s", p)},
  2788  								},
  2789  							})
  2790  
  2791  							h.AssertNil(t, err)
  2792  							h.AssertContains(t, outBuf.String(), fmt.Sprintf("Warning: Mounting to a sensitive directory '%s'", p))
  2793  						})
  2794  					}
  2795  				})
  2796  			})
  2797  
  2798  			when("on windows", func() {
  2799  				it.Before(func() {
  2800  					h.SkipIf(t, runtime.GOOS != "windows", "Skipped on non-windows")
  2801  				})
  2802  				when("linux container", func() {
  2803  					it("drive is transformed", func() {
  2804  						dir, _ := os.MkdirTemp("", "pack-test-mount")
  2805  						volume := fmt.Sprintf("%v:/x", dir)
  2806  						err := subject.Build(context.TODO(), BuildOptions{
  2807  							Image:   "some/app",
  2808  							Builder: defaultBuilderName,
  2809  							ContainerConfig: ContainerConfig{
  2810  								Volumes: []string{volume},
  2811  							},
  2812  							TrustBuilder: func(string) bool { return true },
  2813  						})
  2814  						expected := []string{
  2815  							fmt.Sprintf("%s:/x:ro", strings.ToLower(dir)),
  2816  						}
  2817  						h.AssertNil(t, err)
  2818  						h.AssertEq(t, fakeLifecycle.Opts.Volumes, expected)
  2819  					})
  2820  
  2821  					// May not fail as mode is not used on Windows
  2822  					when("volume mode is invalid", func() {
  2823  						it("returns an error", func() {
  2824  							err := subject.Build(context.TODO(), BuildOptions{
  2825  								Image:   "some/app",
  2826  								Builder: defaultBuilderName,
  2827  								ContainerConfig: ContainerConfig{
  2828  									Volumes: []string{"/a:/x:invalid"},
  2829  								},
  2830  								TrustBuilder: func(string) bool { return true },
  2831  							})
  2832  							h.AssertError(t, err, `platform volume "/a:/x:invalid" has invalid format: invalid volume specification: '/a:/x:invalid'`)
  2833  						})
  2834  					})
  2835  
  2836  					when("volume specification is invalid", func() {
  2837  						it("returns an error", func() {
  2838  							err := subject.Build(context.TODO(), BuildOptions{
  2839  								Image:   "some/app",
  2840  								Builder: defaultBuilderName,
  2841  								ContainerConfig: ContainerConfig{
  2842  									Volumes: []string{":::"},
  2843  								},
  2844  								TrustBuilder: func(string) bool { return true },
  2845  							})
  2846  							h.AssertError(t, err, `platform volume ":::" has invalid format: invalid volume specification: ':::'`)
  2847  						})
  2848  					})
  2849  
  2850  					when("mounting onto cnb spec'd dir", func() {
  2851  						for _, p := range []string{
  2852  							`/cnb`, `/cnb/buildpacks`, `/layers`,
  2853  						} {
  2854  							p := p
  2855  							it(fmt.Sprintf("warns when mounting to '%s'", p), func() {
  2856  								err := subject.Build(context.TODO(), BuildOptions{
  2857  									Image:   "some/app",
  2858  									Builder: defaultBuilderName,
  2859  									ContainerConfig: ContainerConfig{
  2860  										Volumes: []string{fmt.Sprintf("c:/Users:%s", p)},
  2861  									},
  2862  									TrustBuilder: func(string) bool { return true },
  2863  								})
  2864  
  2865  								h.AssertNil(t, err)
  2866  								h.AssertContains(t, outBuf.String(), fmt.Sprintf("Warning: Mounting to a sensitive directory '%s'", p))
  2867  							})
  2868  						}
  2869  					})
  2870  				})
  2871  				when("windows container", func() {
  2872  					it("drive is mounted", func() {
  2873  						dir, _ := os.MkdirTemp("", "pack-test-mount")
  2874  						volume := fmt.Sprintf("%v:c:\\x", dir)
  2875  						err := subject.Build(context.TODO(), BuildOptions{
  2876  							Image:   "some/app",
  2877  							Builder: defaultWindowsBuilderName,
  2878  							ContainerConfig: ContainerConfig{
  2879  								Volumes: []string{volume},
  2880  							},
  2881  							TrustBuilder: func(string) bool { return true },
  2882  						})
  2883  						expected := []string{
  2884  							fmt.Sprintf("%s:c:\\x:ro", strings.ToLower(dir)),
  2885  						}
  2886  						h.AssertNil(t, err)
  2887  						h.AssertEq(t, fakeLifecycle.Opts.Volumes, expected)
  2888  					})
  2889  
  2890  					// May not fail as mode is not used on Windows
  2891  					when("volume mode is invalid", func() {
  2892  						it("returns an error", func() {
  2893  							err := subject.Build(context.TODO(), BuildOptions{
  2894  								Image:   "some/app",
  2895  								Builder: defaultWindowsBuilderName,
  2896  								ContainerConfig: ContainerConfig{
  2897  									Volumes: []string{"/a:/x:invalid"},
  2898  								},
  2899  								TrustBuilder: func(string) bool { return true },
  2900  							})
  2901  							h.AssertError(t, err, `platform volume "/a:/x:invalid" has invalid format: invalid volume specification: '/a:/x:invalid'`)
  2902  						})
  2903  					})
  2904  
  2905  					// Should fail even on windows
  2906  					when("volume specification is invalid", func() {
  2907  						it("returns an error", func() {
  2908  							err := subject.Build(context.TODO(), BuildOptions{
  2909  								Image:   "some/app",
  2910  								Builder: defaultWindowsBuilderName,
  2911  								ContainerConfig: ContainerConfig{
  2912  									Volumes: []string{":::"},
  2913  								},
  2914  								TrustBuilder: func(string) bool { return true },
  2915  							})
  2916  							h.AssertError(t, err, `platform volume ":::" has invalid format: invalid volume specification: ':::'`)
  2917  						})
  2918  					})
  2919  
  2920  					when("mounting onto cnb spec'd dir", func() {
  2921  						for _, p := range []string{
  2922  							`c:\cnb`, `c:\cnb\buildpacks`, `c:\layers`,
  2923  						} {
  2924  							p := p
  2925  							it(fmt.Sprintf("warns when mounting to '%s'", p), func() {
  2926  								err := subject.Build(context.TODO(), BuildOptions{
  2927  									Image:   "some/app",
  2928  									Builder: defaultWindowsBuilderName,
  2929  									ContainerConfig: ContainerConfig{
  2930  										Volumes: []string{fmt.Sprintf("c:/Users:%s", p)},
  2931  									},
  2932  									TrustBuilder: func(string) bool { return true },
  2933  								})
  2934  
  2935  								h.AssertNil(t, err)
  2936  								h.AssertContains(t, outBuf.String(), fmt.Sprintf("Warning: Mounting to a sensitive directory '%s'", p))
  2937  							})
  2938  						}
  2939  					})
  2940  				})
  2941  			})
  2942  		})
  2943  
  2944  		when("gid option", func() {
  2945  			it("gid is passthroughs to lifecycle", func() {
  2946  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2947  					Workspace: "app",
  2948  					Builder:   defaultBuilderName,
  2949  					Image:     "example.com/some/repo:tag",
  2950  					GroupID:   2,
  2951  				}))
  2952  				h.AssertEq(t, fakeLifecycle.Opts.GID, 2)
  2953  			})
  2954  		})
  2955  
  2956  		when("RegistryMirrors option", func() {
  2957  			it("translates run image before passing to lifecycle", func() {
  2958  				subject.registryMirrors = map[string]string{
  2959  					"index.docker.io": "10.0.0.1",
  2960  				}
  2961  
  2962  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2963  					Builder: defaultBuilderName,
  2964  					Image:   "example.com/some/repo:tag",
  2965  				}))
  2966  				h.AssertEq(t, fakeLifecycle.Opts.RunImage, "10.0.0.1/default/run:latest")
  2967  			})
  2968  		})
  2969  
  2970  		when("previous-image option", func() {
  2971  			it("previous-image is passed to lifecycle", func() {
  2972  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2973  					Workspace:     "app",
  2974  					Builder:       defaultBuilderName,
  2975  					Image:         "example.com/some/repo:tag",
  2976  					PreviousImage: "example.com/some/new:tag",
  2977  				}))
  2978  				h.AssertEq(t, fakeLifecycle.Opts.PreviousImage, "example.com/some/new:tag")
  2979  			})
  2980  		})
  2981  
  2982  		when("interactive option", func() {
  2983  			it("passthroughs to lifecycle", func() {
  2984  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2985  					Builder:     defaultBuilderName,
  2986  					Image:       "example.com/some/repo:tag",
  2987  					Interactive: true,
  2988  				}))
  2989  				h.AssertEq(t, fakeLifecycle.Opts.Interactive, true)
  2990  			})
  2991  		})
  2992  
  2993  		when("sbom destination dir option", func() {
  2994  			it("passthroughs to lifecycle", func() {
  2995  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  2996  					Builder:            defaultBuilderName,
  2997  					Image:              "example.com/some/repo:tag",
  2998  					SBOMDestinationDir: "some-destination-dir",
  2999  				}))
  3000  				h.AssertEq(t, fakeLifecycle.Opts.SBOMDestinationDir, "some-destination-dir")
  3001  			})
  3002  		})
  3003  
  3004  		when("report destination dir option", func() {
  3005  			it("passthroughs to lifecycle", func() {
  3006  				h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  3007  					Builder:              defaultBuilderName,
  3008  					Image:                "example.com/some/repo:tag",
  3009  					ReportDestinationDir: "a-destination-dir",
  3010  				}))
  3011  				h.AssertEq(t, fakeLifecycle.Opts.ReportDestinationDir, "a-destination-dir")
  3012  			})
  3013  		})
  3014  
  3015  		when("there are extensions", func() {
  3016  			withExtensionsLabel = true
  3017  
  3018  			when("default configuration", func() {
  3019  				it("succeeds", func() {
  3020  					err := subject.Build(context.TODO(), BuildOptions{
  3021  						Image:   "some/app",
  3022  						Builder: defaultBuilderName,
  3023  					})
  3024  
  3025  					h.AssertNil(t, err)
  3026  					h.AssertEq(t, fakeLifecycle.Opts.BuilderImage, defaultBuilderName)
  3027  				})
  3028  			})
  3029  
  3030  			when("os", func() {
  3031  				when("windows", func() {
  3032  					it.Before(func() {
  3033  						h.SkipIf(t, runtime.GOOS != "windows", "Skipped on non-windows")
  3034  					})
  3035  
  3036  					it("errors", func() {
  3037  						err := subject.Build(context.TODO(), BuildOptions{
  3038  							Image:   "some/app",
  3039  							Builder: defaultWindowsBuilderName,
  3040  						})
  3041  
  3042  						h.AssertNotNil(t, err)
  3043  					})
  3044  				})
  3045  
  3046  				when("linux", func() {
  3047  					it("succeeds", func() {
  3048  						err := subject.Build(context.TODO(), BuildOptions{
  3049  							Image:   "some/app",
  3050  							Builder: defaultBuilderName,
  3051  						})
  3052  
  3053  						h.AssertNil(t, err)
  3054  						h.AssertEq(t, fakeLifecycle.Opts.BuilderImage, defaultBuilderName)
  3055  					})
  3056  				})
  3057  			})
  3058  
  3059  			when("pull policy", func() {
  3060  				when("always", func() {
  3061  					it("succeeds", func() {
  3062  						err := subject.Build(context.TODO(), BuildOptions{
  3063  							Image:      "some/app",
  3064  							Builder:    defaultBuilderName,
  3065  							PullPolicy: image.PullAlways,
  3066  						})
  3067  
  3068  						h.AssertNil(t, err)
  3069  						h.AssertEq(t, fakeLifecycle.Opts.BuilderImage, defaultBuilderName)
  3070  					})
  3071  				})
  3072  
  3073  				when("other", func() {
  3074  					it("errors", func() {
  3075  						err := subject.Build(context.TODO(), BuildOptions{
  3076  							Image:      "some/app",
  3077  							Builder:    defaultBuilderName,
  3078  							PullPolicy: image.PullNever,
  3079  						})
  3080  
  3081  						h.AssertNotNil(t, err)
  3082  					})
  3083  				})
  3084  			})
  3085  		})
  3086  
  3087  		when("export to OCI layout", func() {
  3088  			var (
  3089  				inputImageReference, inputPreviousImageReference       InputImageReference
  3090  				layoutConfig                                           *LayoutConfig
  3091  				hostImagePath, hostPreviousImagePath, hostRunImagePath string
  3092  			)
  3093  
  3094  			it.Before(func() {
  3095  				h.SkipIf(t, runtime.GOOS == "windows", "skip on windows")
  3096  
  3097  				remoteRunImage := fakes.NewImage("default/run", "", nil)
  3098  				h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.id", defaultBuilderStackID))
  3099  				h.AssertNil(t, remoteRunImage.SetLabel("io.buildpacks.stack.mixins", `["mixinA", "mixinX", "run:mixinZ"]`))
  3100  				fakeImageFetcher.RemoteImages[remoteRunImage.Name()] = remoteRunImage
  3101  
  3102  				hostImagePath = filepath.Join(tmpDir, "my-app")
  3103  				inputImageReference = ParseInputImageReference(fmt.Sprintf("oci:%s", hostImagePath))
  3104  				layoutConfig = &LayoutConfig{
  3105  					InputImage:    inputImageReference,
  3106  					LayoutRepoDir: filepath.Join(tmpDir, "local-repo"),
  3107  				}
  3108  			})
  3109  
  3110  			when("previous image is not provided", func() {
  3111  				when("sparse is false", func() {
  3112  					it("saves run-image locally in oci layout and mount volumes", func() {
  3113  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  3114  							Image:        inputImageReference.Name(),
  3115  							Builder:      defaultBuilderName,
  3116  							LayoutConfig: layoutConfig,
  3117  						}))
  3118  
  3119  						args := fakeImageFetcher.FetchCalls["default/run"]
  3120  						h.AssertEq(t, args.LayoutOption.Sparse, false)
  3121  						h.AssertContains(t, args.LayoutOption.Path, layoutConfig.LayoutRepoDir)
  3122  
  3123  						h.AssertEq(t, fakeLifecycle.Opts.Layout, true)
  3124  						// verify the host path are mounted as volumes
  3125  						h.AssertSliceContainsMatch(t, fakeLifecycle.Opts.Volumes, hostImagePath, hostRunImagePath)
  3126  					})
  3127  				})
  3128  
  3129  				when("sparse is true", func() {
  3130  					it.Before(func() {
  3131  						layoutConfig.Sparse = true
  3132  					})
  3133  
  3134  					it("saves run-image locally (no layers) in oci layout and mount volumes", func() {
  3135  						h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  3136  							Image:        inputImageReference.Name(),
  3137  							Builder:      defaultBuilderName,
  3138  							LayoutConfig: layoutConfig,
  3139  						}))
  3140  
  3141  						args := fakeImageFetcher.FetchCalls["default/run"]
  3142  						h.AssertEq(t, args.LayoutOption.Sparse, true)
  3143  						h.AssertContains(t, args.LayoutOption.Path, layoutConfig.LayoutRepoDir)
  3144  
  3145  						h.AssertEq(t, fakeLifecycle.Opts.Layout, true)
  3146  						// verify the host path are mounted as volumes
  3147  						h.AssertSliceContainsMatch(t, fakeLifecycle.Opts.Volumes, hostImagePath, hostRunImagePath)
  3148  					})
  3149  				})
  3150  			})
  3151  
  3152  			when("previous image is provided", func() {
  3153  				it.Before(func() {
  3154  					hostPreviousImagePath = filepath.Join(tmpDir, "my-previous-app")
  3155  					inputPreviousImageReference = ParseInputImageReference(fmt.Sprintf("oci:%s", hostPreviousImagePath))
  3156  					layoutConfig.PreviousInputImage = inputPreviousImageReference
  3157  				})
  3158  
  3159  				it("mount previous image volume", func() {
  3160  					h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
  3161  						Image:         inputImageReference.Name(),
  3162  						PreviousImage: inputPreviousImageReference.Name(),
  3163  						Builder:       defaultBuilderName,
  3164  						LayoutConfig:  layoutConfig,
  3165  					}))
  3166  
  3167  					h.AssertEq(t, fakeLifecycle.Opts.Layout, true)
  3168  					// verify the host path are mounted as volumes
  3169  					h.AssertSliceContainsMatch(t, fakeLifecycle.Opts.Volumes, hostImagePath, hostPreviousImagePath, hostRunImagePath)
  3170  				})
  3171  			})
  3172  		})
  3173  	})
  3174  }
  3175  
  3176  func diffIDForFile(t *testing.T, path string) string {
  3177  	file, err := os.Open(path)
  3178  	h.AssertNil(t, err)
  3179  
  3180  	hasher := sha256.New()
  3181  	_, err = io.Copy(hasher, file)
  3182  	h.AssertNil(t, err)
  3183  
  3184  	return "sha256:" + hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size())))
  3185  }
  3186  
  3187  func newLinuxImage(name, topLayerSha string, identifier imgutil.Identifier) *fakes.Image {
  3188  	return fakes.NewImage(name, topLayerSha, identifier)
  3189  }
  3190  
  3191  func newWindowsImage(name, topLayerSha string, identifier imgutil.Identifier) *fakes.Image {
  3192  	result := fakes.NewImage(name, topLayerSha, identifier)
  3193  	arch, _ := result.Architecture()
  3194  	osVersion, _ := result.OSVersion()
  3195  	result.SetOS("windows")
  3196  	result.SetOSVersion(osVersion)
  3197  	result.SetArchitecture(arch)
  3198  	return result
  3199  }
  3200  
  3201  func newFakeBuilderImage(t *testing.T, tmpDir, builderName, defaultBuilderStackID, runImageName, lifecycleVersion string, osImageCreator ifakes.FakeImageCreator) *fakes.Image {
  3202  	var supportedBuildpackAPIs builder.APISet
  3203  	for _, v := range api.Buildpack.Supported {
  3204  		supportedBuildpackAPIs = append(supportedBuildpackAPIs, v)
  3205  	}
  3206  	var supportedPlatformAPIs builder.APISet
  3207  	for _, v := range api.Platform.Supported {
  3208  		supportedPlatformAPIs = append(supportedPlatformAPIs, v)
  3209  	}
  3210  	return ifakes.NewFakeBuilderImage(t,
  3211  		tmpDir,
  3212  		builderName,
  3213  		defaultBuilderStackID,
  3214  		"1234",
  3215  		"5678",
  3216  		builder.Metadata{
  3217  			Buildpacks: []dist.ModuleInfo{
  3218  				{ID: "buildpack.1.id", Version: "buildpack.1.version"},
  3219  				{ID: "buildpack.2.id", Version: "buildpack.2.version"},
  3220  			},
  3221  			Extensions: []dist.ModuleInfo{
  3222  				{ID: "extension.1.id", Version: "extension.1.version"},
  3223  				{ID: "extension.2.id", Version: "extension.2.version"},
  3224  			},
  3225  			Stack: builder.StackMetadata{
  3226  				RunImage: builder.RunImageMetadata{
  3227  					Image: runImageName,
  3228  					Mirrors: []string{
  3229  						"registry1.example.com/run/mirror",
  3230  						"registry2.example.com/run/mirror",
  3231  					},
  3232  				},
  3233  			},
  3234  			Lifecycle: builder.LifecycleMetadata{
  3235  				LifecycleInfo: builder.LifecycleInfo{
  3236  					Version: &builder.Version{
  3237  						Version: *semver.MustParse(lifecycleVersion),
  3238  					},
  3239  				},
  3240  				APIs: builder.LifecycleAPIs{
  3241  					Buildpack: builder.APIVersions{
  3242  						Supported: supportedBuildpackAPIs,
  3243  					},
  3244  					Platform: builder.APIVersions{
  3245  						Supported: supportedPlatformAPIs,
  3246  					},
  3247  				},
  3248  			},
  3249  		},
  3250  		dist.ModuleLayers{
  3251  			"buildpack.1.id": {
  3252  				"buildpack.1.version": {
  3253  					API: api.MustParse("0.3"),
  3254  					Stacks: []dist.Stack{
  3255  						{
  3256  							ID:     defaultBuilderStackID,
  3257  							Mixins: []string{"mixinX", "build:mixinY", "run:mixinZ"},
  3258  						},
  3259  					},
  3260  				},
  3261  			},
  3262  			"buildpack.2.id": {
  3263  				"buildpack.2.version": {
  3264  					API: api.MustParse("0.3"),
  3265  					Stacks: []dist.Stack{
  3266  						{
  3267  							ID:     defaultBuilderStackID,
  3268  							Mixins: []string{"mixinX", "build:mixinY"},
  3269  						},
  3270  					},
  3271  				},
  3272  			},
  3273  		},
  3274  		dist.Order{{
  3275  			Group: []dist.ModuleRef{{
  3276  				ModuleInfo: dist.ModuleInfo{
  3277  					ID:      "buildpack.1.id",
  3278  					Version: "buildpack.1.version",
  3279  				},
  3280  			}},
  3281  		}, {
  3282  			Group: []dist.ModuleRef{{
  3283  				ModuleInfo: dist.ModuleInfo{
  3284  					ID:      "buildpack.2.id",
  3285  					Version: "buildpack.2.version",
  3286  				},
  3287  			}},
  3288  		}},
  3289  		dist.ModuleLayers{
  3290  			"extension.1.id": {
  3291  				"extension.1.version": {
  3292  					API: api.MustParse("0.3"),
  3293  				},
  3294  			},
  3295  			"extension.2.id": {
  3296  				"extension.2.version": {
  3297  					API: api.MustParse("0.3"),
  3298  				},
  3299  			},
  3300  		},
  3301  		dist.Order{{
  3302  			Group: []dist.ModuleRef{{
  3303  				ModuleInfo: dist.ModuleInfo{
  3304  					ID:      "extension.1.id",
  3305  					Version: "extension.1.version",
  3306  				},
  3307  			}},
  3308  		}, {
  3309  			Group: []dist.ModuleRef{{
  3310  				ModuleInfo: dist.ModuleInfo{
  3311  					ID:      "extension.2.id",
  3312  					Version: "extension.2.version",
  3313  				},
  3314  			}},
  3315  		}},
  3316  		osImageCreator,
  3317  	)
  3318  }
  3319  
  3320  func setAPIs(t *testing.T, image *fakes.Image, buildpackAPIs []string, platformAPIs []string) {
  3321  	builderMDLabelName := "io.buildpacks.builder.metadata"
  3322  	var supportedBuildpackAPIs builder.APISet
  3323  	for _, v := range buildpackAPIs {
  3324  		supportedBuildpackAPIs = append(supportedBuildpackAPIs, api.MustParse(v))
  3325  	}
  3326  	var supportedPlatformAPIs builder.APISet
  3327  	for _, v := range platformAPIs {
  3328  		supportedPlatformAPIs = append(supportedPlatformAPIs, api.MustParse(v))
  3329  	}
  3330  	builderMDLabel, err := image.Label(builderMDLabelName)
  3331  	h.AssertNil(t, err)
  3332  	var builderMD builder.Metadata
  3333  	h.AssertNil(t, json.Unmarshal([]byte(builderMDLabel), &builderMD))
  3334  	builderMD.Lifecycle.APIs = builder.LifecycleAPIs{
  3335  		Buildpack: builder.APIVersions{
  3336  			Supported: supportedBuildpackAPIs,
  3337  		},
  3338  		Platform: builder.APIVersions{
  3339  			Supported: supportedPlatformAPIs,
  3340  		},
  3341  	}
  3342  	builderMDLabelBytes, err := json.Marshal(&builderMD)
  3343  	h.AssertNil(t, err)
  3344  	h.AssertNil(t, image.SetLabel(builderMDLabelName, string(builderMDLabelBytes)))
  3345  }