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

     1  package client
     2  
     3  import (
     4  	"archive/tar"
     5  	"context"
     6  	"crypto/rand"
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/Masterminds/semver"
    20  	"github.com/buildpacks/imgutil"
    21  	"github.com/buildpacks/imgutil/layout"
    22  	"github.com/buildpacks/imgutil/local"
    23  	"github.com/buildpacks/imgutil/remote"
    24  	"github.com/buildpacks/lifecycle/platform/files"
    25  	types "github.com/docker/docker/api/types/image"
    26  	"github.com/google/go-containerregistry/pkg/name"
    27  	"github.com/pkg/errors"
    28  	ignore "github.com/sabhiram/go-gitignore"
    29  
    30  	"github.com/buildpacks/pack/buildpackage"
    31  	"github.com/buildpacks/pack/internal/build"
    32  	"github.com/buildpacks/pack/internal/builder"
    33  	internalConfig "github.com/buildpacks/pack/internal/config"
    34  	"github.com/buildpacks/pack/internal/layer"
    35  	pname "github.com/buildpacks/pack/internal/name"
    36  	"github.com/buildpacks/pack/internal/paths"
    37  	"github.com/buildpacks/pack/internal/stack"
    38  	"github.com/buildpacks/pack/internal/stringset"
    39  	"github.com/buildpacks/pack/internal/style"
    40  	"github.com/buildpacks/pack/internal/termui"
    41  	"github.com/buildpacks/pack/pkg/archive"
    42  	"github.com/buildpacks/pack/pkg/buildpack"
    43  	"github.com/buildpacks/pack/pkg/cache"
    44  	"github.com/buildpacks/pack/pkg/dist"
    45  	"github.com/buildpacks/pack/pkg/image"
    46  	"github.com/buildpacks/pack/pkg/logging"
    47  	projectTypes "github.com/buildpacks/pack/pkg/project/types"
    48  	v02 "github.com/buildpacks/pack/pkg/project/v02"
    49  )
    50  
    51  const (
    52  	minLifecycleVersionSupportingCreator               = "0.7.4"
    53  	prevLifecycleVersionSupportingImage                = "0.6.1"
    54  	minLifecycleVersionSupportingImage                 = "0.7.5"
    55  	minLifecycleVersionSupportingCreatorWithExtensions = "0.19.0"
    56  )
    57  
    58  // LifecycleExecutor executes the lifecycle which satisfies the Cloud Native Buildpacks Lifecycle specification.
    59  // Implementations of the Lifecycle must execute the following phases by calling the
    60  // phase-specific lifecycle binary in order:
    61  //
    62  //	Detection:         /cnb/lifecycle/detector
    63  //	Analysis:          /cnb/lifecycle/analyzer
    64  //	Cache Restoration: /cnb/lifecycle/restorer
    65  //	Build:             /cnb/lifecycle/builder
    66  //	Export:            /cnb/lifecycle/exporter
    67  //
    68  // or invoke the single creator binary:
    69  //
    70  //	Creator:            /cnb/lifecycle/creator
    71  type LifecycleExecutor interface {
    72  	// Execute is responsible for invoking each of these binaries
    73  	// with the desired configuration.
    74  	Execute(ctx context.Context, opts build.LifecycleOptions) error
    75  }
    76  
    77  type IsTrustedBuilder func(string) bool
    78  
    79  // BuildOptions defines configuration settings for a Build.
    80  type BuildOptions struct {
    81  	// The base directory to use to resolve relative assets
    82  	RelativeBaseDir string
    83  
    84  	// required. Name of output image.
    85  	Image string
    86  
    87  	// required. Builder image name.
    88  	Builder string
    89  
    90  	// Name of the buildpack registry. Used to
    91  	// add buildpacks to a build.
    92  	Registry string
    93  
    94  	// AppPath is the path to application bits.
    95  	// If unset it defaults to current working directory.
    96  	AppPath string
    97  
    98  	// Specify the run image the Image will be
    99  	// built atop.
   100  	RunImage string
   101  
   102  	// Address of docker daemon exposed to build container
   103  	// e.g. tcp://example.com:1234, unix:///run/user/1000/podman/podman.sock
   104  	DockerHost string
   105  
   106  	// Used to determine a run-image mirror if Run Image is empty.
   107  	// Used in combination with Builder metadata to determine to the 'best' mirror.
   108  	// 'best' is defined as:
   109  	//  - if Publish is true, the best mirror matches registry we are publishing to.
   110  	//  - if Publish is false, the best mirror matches a registry specified in Image.
   111  	//  - otherwise if both of the above did not match, use mirror specified in
   112  	//    the builder metadata
   113  	AdditionalMirrors map[string][]string
   114  
   115  	// User provided environment variables to the buildpacks.
   116  	// Buildpacks may both read and overwrite these values.
   117  	Env map[string]string
   118  
   119  	// Used to configure various cache available options
   120  	Cache cache.CacheOpts
   121  
   122  	// Option only valid if Publish is true
   123  	// Create an additional image that contains cache=true layers and push it to the registry.
   124  	CacheImage string
   125  
   126  	// Option passed directly to the lifecycle.
   127  	// If true, publishes Image directly to a registry.
   128  	// Assumes Image contains a valid registry with credentials
   129  	// provided by the docker client.
   130  	Publish bool
   131  
   132  	// Clear the build cache from previous builds.
   133  	ClearCache bool
   134  
   135  	// Launch a terminal UI to depict the build process
   136  	Interactive bool
   137  
   138  	// List of buildpack images or archives to add to a builder.
   139  	// These buildpacks may overwrite those on the builder if they
   140  	// share both an ID and Version with a buildpack on the builder.
   141  	Buildpacks []string
   142  
   143  	// List of extension images or archives to add to a builder.
   144  	// These extensions may overwrite those on the builder if they
   145  	// share both an ID and Version with an extension on the builder.
   146  	Extensions []string
   147  
   148  	// Additional image tags to push to, each will contain contents identical to Image
   149  	AdditionalTags []string
   150  
   151  	// Configure the proxy environment variables,
   152  	// These variables will only be set in the build image
   153  	// and will not be used if proxy env vars are already set.
   154  	ProxyConfig *ProxyConfig
   155  
   156  	// Configure network and volume mounts for the build containers.
   157  	ContainerConfig ContainerConfig
   158  
   159  	// Process type that will be used when setting container start command.
   160  	DefaultProcessType string
   161  
   162  	// Strategy for updating local images before a build.
   163  	PullPolicy image.PullPolicy
   164  
   165  	// ProjectDescriptorBaseDir is the base directory to find relative resources referenced by the ProjectDescriptor
   166  	ProjectDescriptorBaseDir string
   167  
   168  	// ProjectDescriptor describes the project and any configuration specific to the project
   169  	ProjectDescriptor projectTypes.Descriptor
   170  
   171  	// List of buildpack images or archives to add to a builder.
   172  	// these buildpacks will be prepended to the builder's order
   173  	PreBuildpacks []string
   174  
   175  	// List of buildpack images or archives to add to a builder.
   176  	// these buildpacks will be appended to the builder's order
   177  	PostBuildpacks []string
   178  
   179  	// The lifecycle image that will be used for the analysis, restore and export phases
   180  	// when using an untrusted builder.
   181  	LifecycleImage string
   182  
   183  	// The location at which to mount the AppDir in the build image.
   184  	Workspace string
   185  
   186  	// User's group id used to build the image
   187  	GroupID int
   188  
   189  	// User's user id used to build the image
   190  	UserID int
   191  
   192  	// A previous image to set to a particular tag reference, digest reference, or (when performing a daemon build) image ID;
   193  	PreviousImage string
   194  
   195  	// TrustBuilder when true optimizes builds by running
   196  	// all lifecycle phases in a single container.
   197  	// This places registry credentials on the builder's build image.
   198  	// Only trust builders from reputable sources.
   199  	TrustBuilder IsTrustedBuilder
   200  
   201  	// Directory to output any SBOM artifacts
   202  	SBOMDestinationDir string
   203  
   204  	// Directory to output the report.toml metadata artifact
   205  	ReportDestinationDir string
   206  
   207  	// Desired create time in the output image config
   208  	CreationTime *time.Time
   209  
   210  	// Configuration to export to OCI layout format
   211  	LayoutConfig *LayoutConfig
   212  }
   213  
   214  func (b *BuildOptions) Layout() bool {
   215  	if b.LayoutConfig != nil {
   216  		return b.LayoutConfig.Enable()
   217  	}
   218  	return false
   219  }
   220  
   221  // ProxyConfig specifies proxy setting to be set as environment variables in a container.
   222  type ProxyConfig struct {
   223  	HTTPProxy  string // Used to set HTTP_PROXY env var.
   224  	HTTPSProxy string // Used to set HTTPS_PROXY env var.
   225  	NoProxy    string // Used to set NO_PROXY env var.
   226  }
   227  
   228  // ContainerConfig is additional configuration of the docker container that all build steps
   229  // occur within.
   230  type ContainerConfig struct {
   231  	// Configure network settings of the build containers.
   232  	// The value of Network is handed directly to the docker client.
   233  	// For valid values of this field see:
   234  	// https://docs.docker.com/network/#network-drivers
   235  	Network string
   236  
   237  	// Volumes are accessible during both detect build phases
   238  	// should have the form: /path/in/host:/path/in/container.
   239  	// For more about volume mounts, and their permissions see:
   240  	// https://docs.docker.com/storage/volumes/
   241  	//
   242  	// It is strongly recommended you do not override any of the
   243  	// paths with volume mounts at the following locations:
   244  	// - /cnb
   245  	// - /layers
   246  	// - anything below /cnb/**
   247  	Volumes []string
   248  }
   249  
   250  type LayoutConfig struct {
   251  	// Application image reference provided by the user
   252  	InputImage InputImageReference
   253  
   254  	// Previous image reference provided by the user
   255  	PreviousInputImage InputImageReference
   256  
   257  	// Local root path to save the run-image in OCI layout format
   258  	LayoutRepoDir string
   259  
   260  	// Configure the OCI layout fetch mode to avoid saving layers on disk
   261  	Sparse bool
   262  }
   263  
   264  func (l *LayoutConfig) Enable() bool {
   265  	return l.InputImage.Layout()
   266  }
   267  
   268  type layoutPathConfig struct {
   269  	hostImagePath           string
   270  	hostPreviousImagePath   string
   271  	hostRunImagePath        string
   272  	targetImagePath         string
   273  	targetPreviousImagePath string
   274  	targetRunImagePath      string
   275  }
   276  
   277  var IsTrustedBuilderFunc = func(b string) bool {
   278  	for _, knownBuilder := range builder.KnownBuilders {
   279  		if b == knownBuilder.Image && knownBuilder.Trusted {
   280  			return true
   281  		}
   282  	}
   283  	return false
   284  }
   285  
   286  // Build configures settings for the build container(s) and lifecycle.
   287  // It then invokes the lifecycle to build an app image.
   288  // If any configuration is deemed invalid, or if any lifecycle phases fail,
   289  // an error will be returned and no image produced.
   290  func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
   291  	var pathsConfig layoutPathConfig
   292  
   293  	imageRef, err := c.parseReference(opts)
   294  	if err != nil {
   295  		return errors.Wrapf(err, "invalid image name '%s'", opts.Image)
   296  	}
   297  	imgRegistry := imageRef.Context().RegistryStr()
   298  	imageName := imageRef.Name()
   299  
   300  	if opts.Layout() {
   301  		pathsConfig, err = c.processLayoutPath(opts.LayoutConfig.InputImage, opts.LayoutConfig.PreviousInputImage)
   302  		if err != nil {
   303  			if opts.LayoutConfig.PreviousInputImage != nil {
   304  				return errors.Wrapf(err, "invalid layout paths image name '%s' or previous-image name '%s'", opts.LayoutConfig.InputImage.Name(),
   305  					opts.LayoutConfig.PreviousInputImage.Name())
   306  			}
   307  			return errors.Wrapf(err, "invalid layout paths image name '%s'", opts.LayoutConfig.InputImage.Name())
   308  		}
   309  	}
   310  
   311  	appPath, err := c.processAppPath(opts.AppPath)
   312  	if err != nil {
   313  		return errors.Wrapf(err, "invalid app path '%s'", opts.AppPath)
   314  	}
   315  
   316  	proxyConfig := c.processProxyConfig(opts.ProxyConfig)
   317  
   318  	builderRef, err := c.processBuilderName(opts.Builder)
   319  	if err != nil {
   320  		return errors.Wrapf(err, "invalid builder '%s'", opts.Builder)
   321  	}
   322  
   323  	rawBuilderImage, err := c.imageFetcher.Fetch(ctx, builderRef.Name(), image.FetchOptions{Daemon: true, PullPolicy: opts.PullPolicy})
   324  	if err != nil {
   325  		return errors.Wrapf(err, "failed to fetch builder image '%s'", builderRef.Name())
   326  	}
   327  
   328  	builderOS, err := rawBuilderImage.OS()
   329  	if err != nil {
   330  		return errors.Wrapf(err, "getting builder OS")
   331  	}
   332  
   333  	builderArch, err := rawBuilderImage.Architecture()
   334  	if err != nil {
   335  		return errors.Wrapf(err, "getting builder architecture")
   336  	}
   337  
   338  	bldr, err := c.getBuilder(rawBuilderImage)
   339  	if err != nil {
   340  		return errors.Wrapf(err, "invalid builder %s", style.Symbol(opts.Builder))
   341  	}
   342  
   343  	fetchOptions := image.FetchOptions{
   344  		Daemon:     !opts.Publish,
   345  		PullPolicy: opts.PullPolicy,
   346  		Platform:   fmt.Sprintf("%s/%s", builderOS, builderArch),
   347  	}
   348  	runImageName := c.resolveRunImage(opts.RunImage, imgRegistry, builderRef.Context().RegistryStr(), bldr.DefaultRunImage(), opts.AdditionalMirrors, opts.Publish, fetchOptions)
   349  
   350  	if opts.Layout() {
   351  		targetRunImagePath, err := layout.ParseRefToPath(runImageName)
   352  		if err != nil {
   353  			return err
   354  		}
   355  		hostRunImagePath := filepath.Join(opts.LayoutConfig.LayoutRepoDir, targetRunImagePath)
   356  		targetRunImagePath = filepath.Join(paths.RootDir, "layout-repo", targetRunImagePath)
   357  		fetchOptions.LayoutOption = image.LayoutOption{
   358  			Path:   hostRunImagePath,
   359  			Sparse: opts.LayoutConfig.Sparse,
   360  		}
   361  		fetchOptions.Daemon = false
   362  		pathsConfig.targetRunImagePath = targetRunImagePath
   363  		pathsConfig.hostRunImagePath = hostRunImagePath
   364  	}
   365  	runImage, err := c.validateRunImage(ctx, runImageName, fetchOptions, bldr.StackID)
   366  	if err != nil {
   367  		return errors.Wrapf(err, "invalid run-image '%s'", runImageName)
   368  	}
   369  
   370  	var runMixins []string
   371  	if _, err := dist.GetLabel(runImage, stack.MixinsLabel, &runMixins); err != nil {
   372  		return err
   373  	}
   374  
   375  	fetchedBPs, order, err := c.processBuildpacks(ctx, bldr.Image(), bldr.Buildpacks(), bldr.Order(), bldr.StackID, opts)
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	fetchedExs, orderExtensions, err := c.processExtensions(ctx, bldr.Image(), bldr.Extensions(), bldr.OrderExtensions(), bldr.StackID, opts)
   381  	if err != nil {
   382  		return err
   383  	}
   384  
   385  	// Default mode: if the TrustBuilder option is not set, trust the suggested builders.
   386  	if opts.TrustBuilder == nil {
   387  		opts.TrustBuilder = IsTrustedBuilderFunc
   388  	}
   389  
   390  	// Ensure the builder's platform APIs are supported
   391  	var builderPlatformAPIs builder.APISet
   392  	builderPlatformAPIs = append(builderPlatformAPIs, bldr.LifecycleDescriptor().APIs.Platform.Deprecated...)
   393  	builderPlatformAPIs = append(builderPlatformAPIs, bldr.LifecycleDescriptor().APIs.Platform.Supported...)
   394  	if !supportsPlatformAPI(builderPlatformAPIs) {
   395  		c.logger.Debugf("pack %s supports Platform API(s): %s", c.version, strings.Join(build.SupportedPlatformAPIVersions.AsStrings(), ", "))
   396  		c.logger.Debugf("Builder %s supports Platform API(s): %s", style.Symbol(opts.Builder), strings.Join(builderPlatformAPIs.AsStrings(), ", "))
   397  		return errors.Errorf("Builder %s is incompatible with this version of pack", style.Symbol(opts.Builder))
   398  	}
   399  
   400  	// Get the platform API version to use
   401  	lifecycleVersion := bldr.LifecycleDescriptor().Info.Version
   402  	useCreator := supportsCreator(lifecycleVersion) && opts.TrustBuilder(opts.Builder)
   403  	var (
   404  		lifecycleOptsLifecycleImage string
   405  		lifecycleAPIs               []string
   406  	)
   407  	if !(useCreator) {
   408  		// fetch the lifecycle image
   409  		if supportsLifecycleImage(lifecycleVersion) {
   410  			lifecycleImageName := opts.LifecycleImage
   411  			if lifecycleImageName == "" {
   412  				lifecycleImageName = fmt.Sprintf("%s:%s", internalConfig.DefaultLifecycleImageRepo, lifecycleVersion.String())
   413  			}
   414  
   415  			lifecycleImage, err := c.imageFetcher.Fetch(
   416  				ctx,
   417  				lifecycleImageName,
   418  				image.FetchOptions{
   419  					Daemon:     true,
   420  					PullPolicy: opts.PullPolicy,
   421  					Platform:   fmt.Sprintf("%s/%s", builderOS, builderArch),
   422  				},
   423  			)
   424  			if err != nil {
   425  				return fmt.Errorf("fetching lifecycle image: %w", err)
   426  			}
   427  
   428  			// if lifecyle container os isn't windows, use ephemeral lifecycle to add /workspace with correct ownership
   429  			imageOS, err := lifecycleImage.OS()
   430  			if err != nil {
   431  				return errors.Wrap(err, "getting lifecycle image OS")
   432  			}
   433  			if imageOS != "windows" {
   434  				// obtain uid/gid from builder to use when extending lifecycle image
   435  				uid, gid, err := userAndGroupIDs(rawBuilderImage)
   436  				if err != nil {
   437  					return fmt.Errorf("obtaining build uid/gid from builder image: %w", err)
   438  				}
   439  
   440  				c.logger.Debugf("Creating ephemeral lifecycle from %s with uid %d and gid %d. With workspace dir %s", lifecycleImage.Name(), uid, gid, opts.Workspace)
   441  				// extend lifecycle image with mountpoints, and use it instead of current lifecycle image
   442  				lifecycleImage, err = c.createEphemeralLifecycle(lifecycleImage, opts.Workspace, uid, gid)
   443  				if err != nil {
   444  					return err
   445  				}
   446  				c.logger.Debugf("Selecting ephemeral lifecycle image %s for build", lifecycleImage.Name())
   447  				// cleanup the extended lifecycle image when done
   448  				defer c.docker.ImageRemove(context.Background(), lifecycleImage.Name(), types.RemoveOptions{Force: true})
   449  			}
   450  
   451  			lifecycleOptsLifecycleImage = lifecycleImage.Name()
   452  			labels, err := lifecycleImage.Labels()
   453  			if err != nil {
   454  				return fmt.Errorf("reading labels of lifecycle image: %w", err)
   455  			}
   456  
   457  			lifecycleAPIs, err = extractSupportedLifecycleApis(labels)
   458  			if err != nil {
   459  				return fmt.Errorf("reading api versions of lifecycle image: %w", err)
   460  			}
   461  		}
   462  	}
   463  
   464  	usingPlatformAPI, err := build.FindLatestSupported(append(
   465  		bldr.LifecycleDescriptor().APIs.Platform.Deprecated,
   466  		bldr.LifecycleDescriptor().APIs.Platform.Supported...),
   467  		lifecycleAPIs)
   468  	if err != nil {
   469  		return fmt.Errorf("finding latest supported Platform API: %w", err)
   470  	}
   471  	if usingPlatformAPI.LessThan("0.12") {
   472  		if err = c.validateMixins(fetchedBPs, bldr, runImageName, runMixins); err != nil {
   473  			return fmt.Errorf("validating stack mixins: %w", err)
   474  		}
   475  	}
   476  
   477  	buildEnvs := map[string]string{}
   478  	for _, envVar := range opts.ProjectDescriptor.Build.Env {
   479  		buildEnvs[envVar.Name] = envVar.Value
   480  	}
   481  
   482  	for k, v := range opts.Env {
   483  		buildEnvs[k] = v
   484  	}
   485  
   486  	ephemeralBuilder, err := c.createEphemeralBuilder(rawBuilderImage, buildEnvs, order, fetchedBPs, orderExtensions, fetchedExs, usingPlatformAPI.LessThan("0.12"), opts.RunImage)
   487  	if err != nil {
   488  		return err
   489  	}
   490  	defer c.docker.ImageRemove(context.Background(), ephemeralBuilder.Name(), types.RemoveOptions{Force: true})
   491  
   492  	if len(bldr.OrderExtensions()) > 0 || len(ephemeralBuilder.OrderExtensions()) > 0 {
   493  		if builderOS == "windows" {
   494  			return fmt.Errorf("builder contains image extensions which are not supported for Windows builds")
   495  		}
   496  		if !(opts.PullPolicy == image.PullAlways) {
   497  			return fmt.Errorf("pull policy must be 'always' when builder contains image extensions")
   498  		}
   499  	}
   500  
   501  	if opts.Layout() {
   502  		opts.ContainerConfig.Volumes = appendLayoutVolumes(opts.ContainerConfig.Volumes, pathsConfig)
   503  	}
   504  
   505  	processedVolumes, warnings, err := processVolumes(builderOS, opts.ContainerConfig.Volumes)
   506  	if err != nil {
   507  		return err
   508  	}
   509  
   510  	for _, warning := range warnings {
   511  		c.logger.Warn(warning)
   512  	}
   513  
   514  	fileFilter, err := getFileFilter(opts.ProjectDescriptor)
   515  	if err != nil {
   516  		return err
   517  	}
   518  
   519  	runImageName, err = pname.TranslateRegistry(runImageName, c.registryMirrors, c.logger)
   520  	if err != nil {
   521  		return err
   522  	}
   523  
   524  	projectMetadata := files.ProjectMetadata{}
   525  	if c.experimental {
   526  		version := opts.ProjectDescriptor.Project.Version
   527  		sourceURL := opts.ProjectDescriptor.Project.SourceURL
   528  		if version != "" || sourceURL != "" {
   529  			projectMetadata.Source = &files.ProjectSource{
   530  				Type:     "project",
   531  				Version:  map[string]interface{}{"declared": version},
   532  				Metadata: map[string]interface{}{"url": sourceURL},
   533  			}
   534  		} else {
   535  			projectMetadata.Source = v02.GitMetadata(opts.AppPath)
   536  		}
   537  	}
   538  
   539  	lifecycleOpts := build.LifecycleOptions{
   540  		AppPath:                  appPath,
   541  		Image:                    imageRef,
   542  		Builder:                  ephemeralBuilder,
   543  		BuilderImage:             builderRef.Name(),
   544  		LifecycleImage:           ephemeralBuilder.Name(),
   545  		RunImage:                 runImageName,
   546  		ProjectMetadata:          projectMetadata,
   547  		ClearCache:               opts.ClearCache,
   548  		Publish:                  opts.Publish,
   549  		TrustBuilder:             opts.TrustBuilder(opts.Builder),
   550  		UseCreator:               useCreator,
   551  		UseCreatorWithExtensions: supportsCreatorWithExtensions(lifecycleVersion),
   552  		DockerHost:               opts.DockerHost,
   553  		Cache:                    opts.Cache,
   554  		CacheImage:               opts.CacheImage,
   555  		HTTPProxy:                proxyConfig.HTTPProxy,
   556  		HTTPSProxy:               proxyConfig.HTTPSProxy,
   557  		NoProxy:                  proxyConfig.NoProxy,
   558  		Network:                  opts.ContainerConfig.Network,
   559  		AdditionalTags:           opts.AdditionalTags,
   560  		Volumes:                  processedVolumes,
   561  		DefaultProcessType:       opts.DefaultProcessType,
   562  		FileFilter:               fileFilter,
   563  		Workspace:                opts.Workspace,
   564  		GID:                      opts.GroupID,
   565  		UID:                      opts.UserID,
   566  		PreviousImage:            opts.PreviousImage,
   567  		Interactive:              opts.Interactive,
   568  		Termui:                   termui.NewTermui(imageName, ephemeralBuilder, runImageName),
   569  		ReportDestinationDir:     opts.ReportDestinationDir,
   570  		SBOMDestinationDir:       opts.SBOMDestinationDir,
   571  		CreationTime:             opts.CreationTime,
   572  		Layout:                   opts.Layout(),
   573  		Keychain:                 c.keychain,
   574  	}
   575  
   576  	switch {
   577  	case useCreator:
   578  		lifecycleOpts.UseCreator = true
   579  	case supportsLifecycleImage(lifecycleVersion):
   580  		lifecycleOpts.LifecycleImage = lifecycleOptsLifecycleImage
   581  		lifecycleOpts.LifecycleApis = lifecycleAPIs
   582  	case !opts.TrustBuilder(opts.Builder):
   583  		return errors.Errorf("Lifecycle %s does not have an associated lifecycle image. Builder must be trusted.", lifecycleVersion.String())
   584  	}
   585  
   586  	lifecycleOpts.FetchRunImageWithLifecycleLayer = func(runImageName string) (string, error) {
   587  		ephemeralRunImageName := fmt.Sprintf("pack.local/run-image/%x:latest", randString(10))
   588  		runImage, err := c.imageFetcher.Fetch(ctx, runImageName, fetchOptions)
   589  		if err != nil {
   590  			return "", err
   591  		}
   592  		ephemeralRunImage, err := local.NewImage(ephemeralRunImageName, c.docker, local.FromBaseImage(runImage.Name()))
   593  		if err != nil {
   594  			return "", err
   595  		}
   596  		tmpDir, err := os.MkdirTemp("", "extend-run-image-scratch") // we need to write to disk because manifest.json is last in the tar
   597  		if err != nil {
   598  			return "", err
   599  		}
   600  		defer os.RemoveAll(tmpDir)
   601  		lifecycleImageTar, err := func() (string, error) {
   602  			lifecycleImageTar := filepath.Join(tmpDir, "lifecycle-image.tar")
   603  			lifecycleImageReader, err := c.docker.ImageSave(context.Background(), []string{lifecycleOpts.LifecycleImage}) // this is fast because the lifecycle image is based on distroless static
   604  			if err != nil {
   605  				return "", err
   606  			}
   607  			defer lifecycleImageReader.Close()
   608  			lifecycleImageWriter, err := os.Create(lifecycleImageTar)
   609  			if err != nil {
   610  				return "", err
   611  			}
   612  			defer lifecycleImageWriter.Close()
   613  			if _, err = io.Copy(lifecycleImageWriter, lifecycleImageReader); err != nil {
   614  				return "", err
   615  			}
   616  			return lifecycleImageTar, nil
   617  		}()
   618  		if err != nil {
   619  			return "", err
   620  		}
   621  		advanceTarToEntryWithName := func(tarReader *tar.Reader, wantName string) (*tar.Header, error) {
   622  			var (
   623  				header *tar.Header
   624  				err    error
   625  			)
   626  			for {
   627  				header, err = tarReader.Next()
   628  				if err == io.EOF {
   629  					break
   630  				}
   631  				if err != nil {
   632  					return nil, err
   633  				}
   634  				if header.Name != wantName {
   635  					continue
   636  				}
   637  				return header, nil
   638  			}
   639  			return nil, fmt.Errorf("failed to find header with name: %s", wantName)
   640  		}
   641  		lifecycleLayerName, err := func() (string, error) {
   642  			lifecycleImageReader, err := os.Open(lifecycleImageTar)
   643  			if err != nil {
   644  				return "", err
   645  			}
   646  			defer lifecycleImageReader.Close()
   647  			tarReader := tar.NewReader(lifecycleImageReader)
   648  			if _, err = advanceTarToEntryWithName(tarReader, "manifest.json"); err != nil {
   649  				return "", err
   650  			}
   651  			type descriptor struct {
   652  				Layers []string
   653  			}
   654  			type manifestJSON []descriptor
   655  			var manifestContents manifestJSON
   656  			if err = json.NewDecoder(tarReader).Decode(&manifestContents); err != nil {
   657  				return "", err
   658  			}
   659  			if len(manifestContents) < 1 {
   660  				return "", errors.New("missing manifest entries")
   661  			}
   662  			// we can assume the lifecycle layer is the last in the tar, except if the lifecycle has been extended as an ephemeral lifecycle
   663  			layerOffset := 1
   664  			if strings.Contains(lifecycleOpts.LifecycleImage, "pack.local/lifecycle") {
   665  				layerOffset = 2
   666  			}
   667  
   668  			if (len(manifestContents[0].Layers) - layerOffset) < 0 {
   669  				return "", errors.New("Lifecycle image did not contain expected layer count")
   670  			}
   671  
   672  			return manifestContents[0].Layers[len(manifestContents[0].Layers)-layerOffset], nil
   673  		}()
   674  		if err != nil {
   675  			return "", err
   676  		}
   677  		if lifecycleLayerName == "" {
   678  			return "", errors.New("failed to find lifecycle layer")
   679  		}
   680  		lifecycleLayerTar, err := func() (string, error) {
   681  			lifecycleImageReader, err := os.Open(lifecycleImageTar)
   682  			if err != nil {
   683  				return "", err
   684  			}
   685  			defer lifecycleImageReader.Close()
   686  			tarReader := tar.NewReader(lifecycleImageReader)
   687  			var header *tar.Header
   688  			if header, err = advanceTarToEntryWithName(tarReader, lifecycleLayerName); err != nil {
   689  				return "", err
   690  			}
   691  			lifecycleLayerTar := filepath.Join(filepath.Dir(lifecycleImageTar), filepath.Dir(lifecycleLayerName)+".tar")
   692  			lifecycleLayerWriter, err := os.OpenFile(lifecycleLayerTar, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
   693  			if err != nil {
   694  				return "", err
   695  			}
   696  			defer lifecycleLayerWriter.Close()
   697  			if _, err = io.Copy(lifecycleLayerWriter, tarReader); err != nil {
   698  				return "", err
   699  			}
   700  			return lifecycleLayerTar, nil
   701  		}()
   702  		if err != nil {
   703  			return "", err
   704  		}
   705  		diffID, err := func() (string, error) {
   706  			lifecycleLayerReader, err := os.Open(lifecycleLayerTar)
   707  			if err != nil {
   708  				return "", err
   709  			}
   710  			defer lifecycleLayerReader.Close()
   711  			hasher := sha256.New()
   712  			if _, err = io.Copy(hasher, lifecycleLayerReader); err != nil {
   713  				return "", err
   714  			}
   715  			// it's weird that this doesn't match lifecycleLayerTar
   716  			return hex.EncodeToString(hasher.Sum(nil)), nil
   717  		}()
   718  		if err != nil {
   719  			return "", err
   720  		}
   721  		if err = ephemeralRunImage.AddLayerWithDiffID(lifecycleLayerTar, "sha256:"+diffID); err != nil {
   722  			return "", err
   723  		}
   724  		if err = ephemeralRunImage.Save(); err != nil {
   725  			return "", err
   726  		}
   727  		return ephemeralRunImageName, nil
   728  	}
   729  
   730  	if err = c.lifecycleExecutor.Execute(ctx, lifecycleOpts); err != nil {
   731  		return fmt.Errorf("executing lifecycle: %w", err)
   732  	}
   733  	return c.logImageNameAndSha(ctx, opts.Publish, imageRef)
   734  }
   735  
   736  func extractSupportedLifecycleApis(labels map[string]string) ([]string, error) {
   737  	// sample contents of labels:
   738  	//    {io.buildpacks.builder.metadata:\"{\"lifecycle\":{\"version\":\"0.15.3\"},\"api\":{\"buildpack\":\"0.2\",\"platform\":\"0.3\"}}",
   739  	//     io.buildpacks.lifecycle.apis":"{\"buildpack\":{\"deprecated\":[],\"supported\":[\"0.2\",\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\"]},\"platform\":{\"deprecated\":[],\"supported\":[\"0.3\",\"0.4\",\"0.5\",\"0.6\",\"0.7\",\"0.8\",\"0.9\",\"0.10\"]}}\",\"io.buildpacks.lifecycle.version\":\"0.15.3\"}")
   740  
   741  	// This struct is defined in lifecycle-repository/tools/image/main.go#Descriptor -- we could consider moving it from the main package to an importable location.
   742  	var bpPlatformAPI struct {
   743  		Platform struct {
   744  			Deprecated []string
   745  			Supported  []string
   746  		}
   747  	}
   748  	if len(labels["io.buildpacks.lifecycle.apis"]) > 0 {
   749  		err := json.Unmarshal([]byte(labels["io.buildpacks.lifecycle.apis"]), &bpPlatformAPI)
   750  		if err != nil {
   751  			return nil, err
   752  		}
   753  		return append(bpPlatformAPI.Platform.Deprecated, bpPlatformAPI.Platform.Supported...), nil
   754  	}
   755  	return []string{}, nil
   756  }
   757  
   758  func getFileFilter(descriptor projectTypes.Descriptor) (func(string) bool, error) {
   759  	if len(descriptor.Build.Exclude) > 0 {
   760  		excludes := ignore.CompileIgnoreLines(descriptor.Build.Exclude...)
   761  		return func(fileName string) bool {
   762  			return !excludes.MatchesPath(fileName)
   763  		}, nil
   764  	}
   765  	if len(descriptor.Build.Include) > 0 {
   766  		includes := ignore.CompileIgnoreLines(descriptor.Build.Include...)
   767  		return includes.MatchesPath, nil
   768  	}
   769  
   770  	return nil, nil
   771  }
   772  
   773  func supportsCreator(lifecycleVersion *builder.Version) bool {
   774  	// Technically the creator is supported as of platform API version 0.3 (lifecycle version 0.7.0+) but earlier versions
   775  	// have bugs that make using the creator problematic.
   776  	return !lifecycleVersion.LessThan(semver.MustParse(minLifecycleVersionSupportingCreator))
   777  }
   778  
   779  func supportsCreatorWithExtensions(lifecycleVersion *builder.Version) bool {
   780  	return !lifecycleVersion.LessThan(semver.MustParse(minLifecycleVersionSupportingCreatorWithExtensions))
   781  }
   782  
   783  func supportsLifecycleImage(lifecycleVersion *builder.Version) bool {
   784  	return lifecycleVersion.Equal(builder.VersionMustParse(prevLifecycleVersionSupportingImage)) ||
   785  		!lifecycleVersion.LessThan(semver.MustParse(minLifecycleVersionSupportingImage))
   786  }
   787  
   788  // supportsPlatformAPI determines whether pack can build using the builder based on the builder's supported Platform API versions.
   789  func supportsPlatformAPI(builderPlatformAPIs builder.APISet) bool {
   790  	for _, packSupportedAPI := range build.SupportedPlatformAPIVersions {
   791  		for _, builderSupportedAPI := range builderPlatformAPIs {
   792  			supportsPlatform := packSupportedAPI.Compare(builderSupportedAPI) == 0
   793  			if supportsPlatform {
   794  				return true
   795  			}
   796  		}
   797  	}
   798  
   799  	return false
   800  }
   801  
   802  func (c *Client) processBuilderName(builderName string) (name.Reference, error) {
   803  	if builderName == "" {
   804  		return nil, errors.New("builder is a required parameter if the client has no default builder")
   805  	}
   806  	return name.ParseReference(builderName, name.WeakValidation)
   807  }
   808  
   809  func (c *Client) getBuilder(img imgutil.Image) (*builder.Builder, error) {
   810  	bldr, err := builder.FromImage(img)
   811  	if err != nil {
   812  		return nil, err
   813  	}
   814  	if bldr.Stack().RunImage.Image == "" && len(bldr.RunImages()) == 0 {
   815  		return nil, errors.New("builder metadata is missing run-image")
   816  	}
   817  
   818  	lifecycleDescriptor := bldr.LifecycleDescriptor()
   819  	if lifecycleDescriptor.Info.Version == nil {
   820  		return nil, errors.New("lifecycle version must be specified in builder")
   821  	}
   822  	if len(lifecycleDescriptor.APIs.Buildpack.Supported) == 0 {
   823  		return nil, errors.New("supported Lifecycle Buildpack APIs not specified")
   824  	}
   825  	if len(lifecycleDescriptor.APIs.Platform.Supported) == 0 {
   826  		return nil, errors.New("supported Lifecycle Platform APIs not specified")
   827  	}
   828  
   829  	return bldr, nil
   830  }
   831  
   832  func (c *Client) validateRunImage(context context.Context, name string, opts image.FetchOptions, expectedStack string) (imgutil.Image, error) {
   833  	if name == "" {
   834  		return nil, errors.New("run image must be specified")
   835  	}
   836  	img, err := c.imageFetcher.Fetch(context, name, opts)
   837  	if err != nil {
   838  		return nil, err
   839  	}
   840  	stackID, err := img.Label("io.buildpacks.stack.id")
   841  	if err != nil {
   842  		return nil, err
   843  	}
   844  	if stackID != expectedStack {
   845  		return nil, fmt.Errorf("run-image stack id '%s' does not match builder stack '%s'", stackID, expectedStack)
   846  	}
   847  	return img, nil
   848  }
   849  
   850  func (c *Client) validateMixins(additionalBuildpacks []buildpack.BuildModule, bldr *builder.Builder, runImageName string, runMixins []string) error {
   851  	if err := stack.ValidateMixins(bldr.Image().Name(), bldr.Mixins(), runImageName, runMixins); err != nil {
   852  		return err
   853  	}
   854  
   855  	bps, err := allBuildpacks(bldr.Image(), additionalBuildpacks)
   856  	if err != nil {
   857  		return err
   858  	}
   859  	mixins := assembleAvailableMixins(bldr.Mixins(), runMixins)
   860  
   861  	for _, bp := range bps {
   862  		if err := bp.EnsureStackSupport(bldr.StackID, mixins, true); err != nil {
   863  			return err
   864  		}
   865  	}
   866  	return nil
   867  }
   868  
   869  // assembleAvailableMixins returns the set of mixins that are common between the two provided sets, plus build-only mixins and run-only mixins.
   870  func assembleAvailableMixins(buildMixins, runMixins []string) []string {
   871  	// NOTE: We cannot simply union the two mixin sets, as this could introduce a mixin that is only present on one stack
   872  	// image but not the other. A buildpack that happens to require the mixin would fail to run properly, even though validation
   873  	// would pass.
   874  	//
   875  	// For example:
   876  	//
   877  	//  Incorrect:
   878  	//    Run image mixins:   [A, B]
   879  	//    Build image mixins: [A]
   880  	//    Merged: [A, B]
   881  	//    Buildpack requires: [A, B]
   882  	//    Match? Yes
   883  	//
   884  	//  Correct:
   885  	//    Run image mixins:   [A, B]
   886  	//    Build image mixins: [A]
   887  	//    Merged: [A]
   888  	//    Buildpack requires: [A, B]
   889  	//    Match? No
   890  
   891  	buildOnly := stack.FindStageMixins(buildMixins, "build")
   892  	runOnly := stack.FindStageMixins(runMixins, "run")
   893  	_, _, common := stringset.Compare(buildMixins, runMixins)
   894  
   895  	return append(common, append(buildOnly, runOnly...)...)
   896  }
   897  
   898  // allBuildpacks aggregates all buildpacks declared on the image with additional buildpacks passed in. They are sorted
   899  // by ID then Version.
   900  func allBuildpacks(builderImage imgutil.Image, additionalBuildpacks []buildpack.BuildModule) ([]buildpack.Descriptor, error) {
   901  	var all []buildpack.Descriptor
   902  	var bpLayers dist.ModuleLayers
   903  	if _, err := dist.GetLabel(builderImage, dist.BuildpackLayersLabel, &bpLayers); err != nil {
   904  		return nil, err
   905  	}
   906  	for id, bps := range bpLayers {
   907  		for ver, bp := range bps {
   908  			desc := dist.BuildpackDescriptor{
   909  				WithInfo: dist.ModuleInfo{
   910  					ID:      id,
   911  					Version: ver,
   912  				},
   913  				WithStacks:  bp.Stacks,
   914  				WithTargets: bp.Targets,
   915  				WithOrder:   bp.Order,
   916  			}
   917  			all = append(all, &desc)
   918  		}
   919  	}
   920  	for _, bp := range additionalBuildpacks {
   921  		all = append(all, bp.Descriptor())
   922  	}
   923  
   924  	sort.Slice(all, func(i, j int) bool {
   925  		if all[i].Info().ID != all[j].Info().ID {
   926  			return all[i].Info().ID < all[j].Info().ID
   927  		}
   928  		return all[i].Info().Version < all[j].Info().Version
   929  	})
   930  
   931  	return all, nil
   932  }
   933  
   934  func (c *Client) processAppPath(appPath string) (string, error) {
   935  	var (
   936  		resolvedAppPath string
   937  		err             error
   938  	)
   939  
   940  	if appPath == "" {
   941  		if appPath, err = os.Getwd(); err != nil {
   942  			return "", errors.Wrap(err, "get working dir")
   943  		}
   944  	}
   945  
   946  	if resolvedAppPath, err = filepath.EvalSymlinks(appPath); err != nil {
   947  		return "", errors.Wrap(err, "evaluate symlink")
   948  	}
   949  
   950  	if resolvedAppPath, err = filepath.Abs(resolvedAppPath); err != nil {
   951  		return "", errors.Wrap(err, "resolve absolute path")
   952  	}
   953  
   954  	fi, err := os.Stat(resolvedAppPath)
   955  	if err != nil {
   956  		return "", errors.Wrap(err, "stat file")
   957  	}
   958  
   959  	if !fi.IsDir() {
   960  		isZip, err := archive.IsZip(filepath.Clean(resolvedAppPath))
   961  		if err != nil {
   962  			return "", errors.Wrap(err, "check zip")
   963  		}
   964  
   965  		if !isZip {
   966  			return "", errors.New("app path must be a directory or zip")
   967  		}
   968  	}
   969  
   970  	return resolvedAppPath, nil
   971  }
   972  
   973  // processLayoutPath given an image reference and a previous image reference this method calculates the
   974  // local full path and the expected path in the lifecycle container for both images provides. Those values
   975  // can be used to mount the correct volumes
   976  func (c *Client) processLayoutPath(inputImageRef, previousImageRef InputImageReference) (layoutPathConfig, error) {
   977  	var (
   978  		hostImagePath, hostPreviousImagePath, targetImagePath, targetPreviousImagePath string
   979  		err                                                                            error
   980  	)
   981  	hostImagePath, err = fullImagePath(inputImageRef, true)
   982  	if err != nil {
   983  		return layoutPathConfig{}, err
   984  	}
   985  	targetImagePath, err = layout.ParseRefToPath(inputImageRef.Name())
   986  	if err != nil {
   987  		return layoutPathConfig{}, err
   988  	}
   989  	targetImagePath = filepath.Join(paths.RootDir, "layout-repo", targetImagePath)
   990  	c.logger.Debugf("local image path %s will be mounted into the container at path %s", hostImagePath, targetImagePath)
   991  
   992  	if previousImageRef != nil && previousImageRef.Name() != "" {
   993  		hostPreviousImagePath, err = fullImagePath(previousImageRef, false)
   994  		if err != nil {
   995  			return layoutPathConfig{}, err
   996  		}
   997  		targetPreviousImagePath, err = layout.ParseRefToPath(previousImageRef.Name())
   998  		if err != nil {
   999  			return layoutPathConfig{}, err
  1000  		}
  1001  		targetPreviousImagePath = filepath.Join(paths.RootDir, "layout-repo", targetPreviousImagePath)
  1002  		c.logger.Debugf("local previous image path %s will be mounted into the container at path %s", hostPreviousImagePath, targetPreviousImagePath)
  1003  	}
  1004  	return layoutPathConfig{
  1005  		hostImagePath:           hostImagePath,
  1006  		targetImagePath:         targetImagePath,
  1007  		hostPreviousImagePath:   hostPreviousImagePath,
  1008  		targetPreviousImagePath: targetPreviousImagePath,
  1009  	}, nil
  1010  }
  1011  
  1012  func (c *Client) parseReference(opts BuildOptions) (name.Reference, error) {
  1013  	if !opts.Layout() {
  1014  		return c.parseTagReference(opts.Image)
  1015  	}
  1016  	base := filepath.Base(opts.Image)
  1017  	return c.parseTagReference(base)
  1018  }
  1019  
  1020  func (c *Client) processProxyConfig(config *ProxyConfig) ProxyConfig {
  1021  	var (
  1022  		httpProxy, httpsProxy, noProxy string
  1023  		ok                             bool
  1024  	)
  1025  	if config != nil {
  1026  		return *config
  1027  	}
  1028  	if httpProxy, ok = os.LookupEnv("HTTP_PROXY"); !ok {
  1029  		httpProxy = os.Getenv("http_proxy")
  1030  	}
  1031  	if httpsProxy, ok = os.LookupEnv("HTTPS_PROXY"); !ok {
  1032  		httpsProxy = os.Getenv("https_proxy")
  1033  	}
  1034  	if noProxy, ok = os.LookupEnv("NO_PROXY"); !ok {
  1035  		noProxy = os.Getenv("no_proxy")
  1036  	}
  1037  	return ProxyConfig{
  1038  		HTTPProxy:  httpProxy,
  1039  		HTTPSProxy: httpsProxy,
  1040  		NoProxy:    noProxy,
  1041  	}
  1042  }
  1043  
  1044  // processBuildpacks computes an order group based on the existing builder order and declared buildpacks. Additionally,
  1045  // it returns buildpacks that should be added to the builder.
  1046  //
  1047  // Visual examples:
  1048  //
  1049  //		BUILDER ORDER
  1050  //		----------
  1051  //	 - group:
  1052  //			- A
  1053  //			- B
  1054  //	 - group:
  1055  //			- A
  1056  //
  1057  //		WITH DECLARED: "from=builder", X
  1058  //		----------
  1059  //		- group:
  1060  //			- A
  1061  //			- B
  1062  //			- X
  1063  //		 - group:
  1064  //			- A
  1065  //			- X
  1066  //
  1067  //		WITH DECLARED: X, "from=builder", Y
  1068  //		----------
  1069  //		- group:
  1070  //			- X
  1071  //			- A
  1072  //			- B
  1073  //	     - Y
  1074  //		- group:
  1075  //			- X
  1076  //			- A
  1077  //	     - Y
  1078  //
  1079  //		WITH DECLARED: X
  1080  //		----------
  1081  //		- group:
  1082  //			- X
  1083  //
  1084  //		WITH DECLARED: A
  1085  //		----------
  1086  //		- group:
  1087  //			- A
  1088  func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Image, builderBPs []dist.ModuleInfo, builderOrder dist.Order, stackID string, opts BuildOptions) (fetchedBPs []buildpack.BuildModule, order dist.Order, err error) {
  1089  	relativeBaseDir := opts.RelativeBaseDir
  1090  	declaredBPs := opts.Buildpacks
  1091  
  1092  	// declare buildpacks provided by project descriptor when no buildpacks are declared
  1093  	if len(declaredBPs) == 0 && len(opts.ProjectDescriptor.Build.Buildpacks) != 0 {
  1094  		relativeBaseDir = opts.ProjectDescriptorBaseDir
  1095  
  1096  		for _, bp := range opts.ProjectDescriptor.Build.Buildpacks {
  1097  			buildpackLocator, err := getBuildpackLocator(bp, stackID)
  1098  			if err != nil {
  1099  				return nil, nil, err
  1100  			}
  1101  			declaredBPs = append(declaredBPs, buildpackLocator)
  1102  		}
  1103  	}
  1104  
  1105  	order = dist.Order{{Group: []dist.ModuleRef{}}}
  1106  	for _, bp := range declaredBPs {
  1107  		locatorType, err := buildpack.GetLocatorType(bp, relativeBaseDir, builderBPs)
  1108  		if err != nil {
  1109  			return nil, nil, err
  1110  		}
  1111  
  1112  		switch locatorType {
  1113  		case buildpack.FromBuilderLocator:
  1114  			switch {
  1115  			case len(order) == 0 || len(order[0].Group) == 0:
  1116  				order = builderOrder
  1117  			case len(order) > 1:
  1118  				// This should only ever be possible if they are using from=builder twice which we don't allow
  1119  				return nil, nil, errors.New("buildpacks from builder can only be defined once")
  1120  			default:
  1121  				newOrder := dist.Order{}
  1122  				groupToAdd := order[0].Group
  1123  				for _, bOrderEntry := range builderOrder {
  1124  					newEntry := dist.OrderEntry{Group: append(groupToAdd, bOrderEntry.Group...)}
  1125  					newOrder = append(newOrder, newEntry)
  1126  				}
  1127  
  1128  				order = newOrder
  1129  			}
  1130  		default:
  1131  			newFetchedBPs, moduleInfo, err := c.fetchBuildpack(ctx, bp, relativeBaseDir, builderImage, builderBPs, opts, buildpack.KindBuildpack)
  1132  			if err != nil {
  1133  				return fetchedBPs, order, err
  1134  			}
  1135  			fetchedBPs = append(fetchedBPs, newFetchedBPs...)
  1136  			order = appendBuildpackToOrder(order, *moduleInfo)
  1137  		}
  1138  	}
  1139  
  1140  	if (len(order) == 0 || len(order[0].Group) == 0) && len(builderOrder) > 0 {
  1141  		preBuildpacks := opts.PreBuildpacks
  1142  		postBuildpacks := opts.PostBuildpacks
  1143  		if len(preBuildpacks) == 0 && len(opts.ProjectDescriptor.Build.Pre.Buildpacks) > 0 {
  1144  			for _, bp := range opts.ProjectDescriptor.Build.Pre.Buildpacks {
  1145  				buildpackLocator, err := getBuildpackLocator(bp, stackID)
  1146  				if err != nil {
  1147  					return nil, nil, errors.Wrap(err, "get pre-buildpack locator")
  1148  				}
  1149  				preBuildpacks = append(preBuildpacks, buildpackLocator)
  1150  			}
  1151  		}
  1152  		if len(postBuildpacks) == 0 && len(opts.ProjectDescriptor.Build.Post.Buildpacks) > 0 {
  1153  			for _, bp := range opts.ProjectDescriptor.Build.Post.Buildpacks {
  1154  				buildpackLocator, err := getBuildpackLocator(bp, stackID)
  1155  				if err != nil {
  1156  					return nil, nil, errors.Wrap(err, "get post-buildpack locator")
  1157  				}
  1158  				postBuildpacks = append(postBuildpacks, buildpackLocator)
  1159  			}
  1160  		}
  1161  
  1162  		if len(preBuildpacks) > 0 || len(postBuildpacks) > 0 {
  1163  			order = builderOrder
  1164  			for _, bp := range preBuildpacks {
  1165  				newFetchedBPs, moduleInfo, err := c.fetchBuildpack(ctx, bp, relativeBaseDir, builderImage, builderBPs, opts, buildpack.KindBuildpack)
  1166  				if err != nil {
  1167  					return fetchedBPs, order, err
  1168  				}
  1169  				fetchedBPs = append(fetchedBPs, newFetchedBPs...)
  1170  				order = prependBuildpackToOrder(order, *moduleInfo)
  1171  			}
  1172  
  1173  			for _, bp := range postBuildpacks {
  1174  				newFetchedBPs, moduleInfo, err := c.fetchBuildpack(ctx, bp, relativeBaseDir, builderImage, builderBPs, opts, buildpack.KindBuildpack)
  1175  				if err != nil {
  1176  					return fetchedBPs, order, err
  1177  				}
  1178  				fetchedBPs = append(fetchedBPs, newFetchedBPs...)
  1179  				order = appendBuildpackToOrder(order, *moduleInfo)
  1180  			}
  1181  		}
  1182  	}
  1183  
  1184  	return fetchedBPs, order, nil
  1185  }
  1186  
  1187  func (c *Client) fetchBuildpack(ctx context.Context, bp string, relativeBaseDir string, builderImage imgutil.Image, builderBPs []dist.ModuleInfo, opts BuildOptions, kind string) ([]buildpack.BuildModule, *dist.ModuleInfo, error) {
  1188  	pullPolicy := opts.PullPolicy
  1189  	publish := opts.Publish
  1190  	registry := opts.Registry
  1191  
  1192  	locatorType, err := buildpack.GetLocatorType(bp, relativeBaseDir, builderBPs)
  1193  	if err != nil {
  1194  		return nil, nil, err
  1195  	}
  1196  
  1197  	fetchedBPs := []buildpack.BuildModule{}
  1198  	var moduleInfo *dist.ModuleInfo
  1199  	switch locatorType {
  1200  	case buildpack.IDLocator:
  1201  		id, version := buildpack.ParseIDLocator(bp)
  1202  		moduleInfo = &dist.ModuleInfo{
  1203  			ID:      id,
  1204  			Version: version,
  1205  		}
  1206  	default:
  1207  		builderOS, err := builderImage.OS()
  1208  		if err != nil {
  1209  			return nil, nil, errors.Wrapf(err, "getting builder OS")
  1210  		}
  1211  
  1212  		builderArch, err := builderImage.Architecture()
  1213  		if err != nil {
  1214  			return nil, nil, errors.Wrapf(err, "getting builder architecture")
  1215  		}
  1216  		downloadOptions := buildpack.DownloadOptions{
  1217  			RegistryName:    registry,
  1218  			ImageOS:         builderOS,
  1219  			Platform:        fmt.Sprintf("%s/%s", builderOS, builderArch),
  1220  			RelativeBaseDir: relativeBaseDir,
  1221  			Daemon:          !publish,
  1222  			PullPolicy:      pullPolicy,
  1223  		}
  1224  		if kind == buildpack.KindExtension {
  1225  			downloadOptions.ModuleKind = kind
  1226  		}
  1227  		mainBP, depBPs, err := c.buildpackDownloader.Download(ctx, bp, downloadOptions)
  1228  		if err != nil {
  1229  			return nil, nil, errors.Wrap(err, "downloading buildpack")
  1230  		}
  1231  		fetchedBPs = append(append(fetchedBPs, mainBP), depBPs...)
  1232  		mainBPInfo := mainBP.Descriptor().Info()
  1233  		moduleInfo = &mainBPInfo
  1234  
  1235  		packageCfgPath := filepath.Join(bp, "package.toml")
  1236  		_, err = os.Stat(packageCfgPath)
  1237  		if err == nil {
  1238  			fetchedDeps, err := c.fetchBuildpackDependencies(ctx, bp, packageCfgPath, downloadOptions)
  1239  			if err != nil {
  1240  				return nil, nil, errors.Wrapf(err, "fetching package.toml dependencies (path=%s)", style.Symbol(packageCfgPath))
  1241  			}
  1242  			fetchedBPs = append(fetchedBPs, fetchedDeps...)
  1243  		}
  1244  	}
  1245  	return fetchedBPs, moduleInfo, nil
  1246  }
  1247  
  1248  func (c *Client) fetchBuildpackDependencies(ctx context.Context, bp string, packageCfgPath string, downloadOptions buildpack.DownloadOptions) ([]buildpack.BuildModule, error) {
  1249  	packageReader := buildpackage.NewConfigReader()
  1250  	packageCfg, err := packageReader.Read(packageCfgPath)
  1251  	if err == nil {
  1252  		fetchedBPs := []buildpack.BuildModule{}
  1253  		for _, dep := range packageCfg.Dependencies {
  1254  			mainBP, deps, err := c.buildpackDownloader.Download(ctx, dep.URI, buildpack.DownloadOptions{
  1255  				RegistryName:    downloadOptions.RegistryName,
  1256  				ImageOS:         downloadOptions.ImageOS,
  1257  				Platform:        downloadOptions.Platform,
  1258  				Daemon:          downloadOptions.Daemon,
  1259  				PullPolicy:      downloadOptions.PullPolicy,
  1260  				RelativeBaseDir: filepath.Join(bp, packageCfg.Buildpack.URI),
  1261  			})
  1262  
  1263  			if err != nil {
  1264  				return nil, errors.Wrapf(err, "fetching dependencies (uri=%s,image=%s)", style.Symbol(dep.URI), style.Symbol(dep.ImageName))
  1265  			}
  1266  
  1267  			fetchedBPs = append(append(fetchedBPs, mainBP), deps...)
  1268  		}
  1269  		return fetchedBPs, nil
  1270  	}
  1271  	return nil, err
  1272  }
  1273  
  1274  func getBuildpackLocator(bp projectTypes.Buildpack, stackID string) (string, error) {
  1275  	switch {
  1276  	case bp.ID != "" && bp.Script.Inline != "" && bp.URI == "":
  1277  		if bp.Script.API == "" {
  1278  			return "", errors.New("Missing API version for inline buildpack")
  1279  		}
  1280  
  1281  		pathToInlineBuildpack, err := createInlineBuildpack(bp, stackID)
  1282  		if err != nil {
  1283  			return "", errors.Wrap(err, "Could not create temporary inline buildpack")
  1284  		}
  1285  		return pathToInlineBuildpack, nil
  1286  	case bp.URI != "":
  1287  		return bp.URI, nil
  1288  	case bp.ID != "" && bp.Version != "":
  1289  		return fmt.Sprintf("%s@%s", bp.ID, bp.Version), nil
  1290  	case bp.ID != "" && bp.Version == "":
  1291  		return bp.ID, nil
  1292  	default:
  1293  		return "", errors.New("Invalid buildpack definition")
  1294  	}
  1295  }
  1296  
  1297  func appendBuildpackToOrder(order dist.Order, bpInfo dist.ModuleInfo) (newOrder dist.Order) {
  1298  	for _, orderEntry := range order {
  1299  		newEntry := orderEntry
  1300  		newEntry.Group = append(newEntry.Group, dist.ModuleRef{
  1301  			ModuleInfo: bpInfo,
  1302  			Optional:   false,
  1303  		})
  1304  		newOrder = append(newOrder, newEntry)
  1305  	}
  1306  
  1307  	return newOrder
  1308  }
  1309  
  1310  func prependBuildpackToOrder(order dist.Order, bpInfo dist.ModuleInfo) (newOrder dist.Order) {
  1311  	for _, orderEntry := range order {
  1312  		newEntry := orderEntry
  1313  		newGroup := []dist.ModuleRef{{
  1314  			ModuleInfo: bpInfo,
  1315  			Optional:   false,
  1316  		}}
  1317  		newEntry.Group = append(newGroup, newEntry.Group...)
  1318  		newOrder = append(newOrder, newEntry)
  1319  	}
  1320  
  1321  	return newOrder
  1322  }
  1323  
  1324  func (c *Client) processExtensions(ctx context.Context, builderImage imgutil.Image, builderExs []dist.ModuleInfo, builderOrder dist.Order, stackID string, opts BuildOptions) (fetchedExs []buildpack.BuildModule, orderExtensions dist.Order, err error) {
  1325  	relativeBaseDir := opts.RelativeBaseDir
  1326  	declaredExs := opts.Extensions
  1327  
  1328  	orderExtensions = dist.Order{{Group: []dist.ModuleRef{}}}
  1329  	for _, ex := range declaredExs {
  1330  		locatorType, err := buildpack.GetLocatorType(ex, relativeBaseDir, builderExs)
  1331  		if err != nil {
  1332  			return nil, nil, err
  1333  		}
  1334  
  1335  		switch locatorType {
  1336  		case buildpack.RegistryLocator:
  1337  			return nil, nil, errors.New("RegistryLocator type is not valid for extensions")
  1338  		case buildpack.FromBuilderLocator:
  1339  			return nil, nil, errors.New("from builder is not supported for extensions")
  1340  		default:
  1341  			newFetchedExs, moduleInfo, err := c.fetchBuildpack(ctx, ex, relativeBaseDir, builderImage, builderExs, opts, buildpack.KindExtension)
  1342  			if err != nil {
  1343  				return fetchedExs, orderExtensions, err
  1344  			}
  1345  			fetchedExs = append(fetchedExs, newFetchedExs...)
  1346  			orderExtensions = prependBuildpackToOrder(orderExtensions, *moduleInfo)
  1347  		}
  1348  	}
  1349  
  1350  	return fetchedExs, orderExtensions, nil
  1351  }
  1352  
  1353  func userAndGroupIDs(img imgutil.Image) (int, int, error) {
  1354  	sUID, err := img.Env(builder.EnvUID)
  1355  	if err != nil {
  1356  		return 0, 0, errors.Wrap(err, "reading builder env variables")
  1357  	} else if sUID == "" {
  1358  		return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(builder.EnvUID))
  1359  	}
  1360  
  1361  	sGID, err := img.Env(builder.EnvGID)
  1362  	if err != nil {
  1363  		return 0, 0, errors.Wrap(err, "reading builder env variables")
  1364  	} else if sGID == "" {
  1365  		return 0, 0, fmt.Errorf("image %s missing required env var %s", style.Symbol(img.Name()), style.Symbol(builder.EnvGID))
  1366  	}
  1367  
  1368  	var uid, gid int
  1369  	uid, err = strconv.Atoi(sUID)
  1370  	if err != nil {
  1371  		return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(builder.EnvUID), style.Symbol(sUID))
  1372  	}
  1373  
  1374  	gid, err = strconv.Atoi(sGID)
  1375  	if err != nil {
  1376  		return 0, 0, fmt.Errorf("failed to parse %s, value %s should be an integer", style.Symbol(builder.EnvGID), style.Symbol(sGID))
  1377  	}
  1378  
  1379  	return uid, gid, nil
  1380  }
  1381  
  1382  func workspacePathForOS(os, workspace string) string {
  1383  	if workspace == "" {
  1384  		workspace = "workspace"
  1385  	}
  1386  	if os == "windows" {
  1387  		// note we don't use ephemeral lifecycle when os is windows..
  1388  		return "c:\\" + workspace
  1389  	}
  1390  	return "/" + workspace
  1391  }
  1392  
  1393  func (c *Client) addUserMountpoints(lifecycleImage imgutil.Image, dest string, workspace string, uid int, gid int) (string, error) {
  1394  	// today only workspace needs to be added, easy to add future dirs if required.
  1395  
  1396  	imageOS, err := lifecycleImage.OS()
  1397  	if err != nil {
  1398  		return "", errors.Wrap(err, "getting image OS")
  1399  	}
  1400  	layerWriterFactory, err := layer.NewWriterFactory(imageOS)
  1401  	if err != nil {
  1402  		return "", err
  1403  	}
  1404  
  1405  	workspace = workspacePathForOS(imageOS, workspace)
  1406  
  1407  	fh, err := os.Create(filepath.Join(dest, "dirs.tar"))
  1408  	if err != nil {
  1409  		return "", err
  1410  	}
  1411  	defer fh.Close()
  1412  
  1413  	lw := layerWriterFactory.NewWriter(fh)
  1414  	defer lw.Close()
  1415  
  1416  	for _, path := range []string{workspace} {
  1417  		if err := lw.WriteHeader(&tar.Header{
  1418  			Typeflag: tar.TypeDir,
  1419  			Name:     path,
  1420  			Mode:     0755,
  1421  			ModTime:  archive.NormalizedDateTime,
  1422  			Uid:      uid,
  1423  			Gid:      gid,
  1424  		}); err != nil {
  1425  			return "", errors.Wrapf(err, "creating %s mountpoint dir in layer", style.Symbol(path))
  1426  		}
  1427  	}
  1428  
  1429  	return fh.Name(), nil
  1430  }
  1431  
  1432  func (c *Client) createEphemeralLifecycle(lifecycleImage imgutil.Image, workspace string, uid int, gid int) (imgutil.Image, error) {
  1433  	lifecycleImage.Rename(fmt.Sprintf("pack.local/lifecycle/%x:latest", randString(10)))
  1434  
  1435  	tmpDir, err := os.MkdirTemp("", "create-lifecycle-scratch")
  1436  	if err != nil {
  1437  		return nil, err
  1438  	}
  1439  	defer os.RemoveAll(tmpDir)
  1440  	dirsTar, err := c.addUserMountpoints(lifecycleImage, tmpDir, workspace, uid, gid)
  1441  	if err != nil {
  1442  		return nil, err
  1443  	}
  1444  	if err := lifecycleImage.AddLayer(dirsTar); err != nil {
  1445  		return nil, errors.Wrap(err, "adding mountpoint dirs layer")
  1446  	}
  1447  
  1448  	err = lifecycleImage.Save()
  1449  	if err != nil {
  1450  		return nil, err
  1451  	}
  1452  
  1453  	return lifecycleImage, nil
  1454  }
  1455  
  1456  func (c *Client) createEphemeralBuilder(
  1457  	rawBuilderImage imgutil.Image,
  1458  	env map[string]string,
  1459  	order dist.Order,
  1460  	buildpacks []buildpack.BuildModule,
  1461  	orderExtensions dist.Order,
  1462  	extensions []buildpack.BuildModule,
  1463  	validateMixins bool,
  1464  	runImage string,
  1465  ) (*builder.Builder, error) {
  1466  	origBuilderName := rawBuilderImage.Name()
  1467  	bldr, err := builder.New(rawBuilderImage, fmt.Sprintf("pack.local/builder/%x:latest", randString(10)), builder.WithRunImage(runImage))
  1468  	if err != nil {
  1469  		return nil, errors.Wrapf(err, "invalid builder %s", style.Symbol(origBuilderName))
  1470  	}
  1471  
  1472  	bldr.SetEnv(env)
  1473  	for _, bp := range buildpacks {
  1474  		bpInfo := bp.Descriptor().Info()
  1475  		c.logger.Debugf("Adding buildpack %s version %s to builder", style.Symbol(bpInfo.ID), style.Symbol(bpInfo.Version))
  1476  		bldr.AddBuildpack(bp)
  1477  	}
  1478  	if len(order) > 0 && len(order[0].Group) > 0 {
  1479  		c.logger.Debug("Setting custom order")
  1480  		bldr.SetOrder(order)
  1481  	}
  1482  
  1483  	for _, ex := range extensions {
  1484  		exInfo := ex.Descriptor().Info()
  1485  		c.logger.Debugf("Adding extension %s version %s to builder", style.Symbol(exInfo.ID), style.Symbol(exInfo.Version))
  1486  		bldr.AddExtension(ex)
  1487  	}
  1488  	if len(orderExtensions) > 0 && len(orderExtensions[0].Group) > 0 {
  1489  		c.logger.Debug("Setting custom order for extensions")
  1490  		bldr.SetOrderExtensions(orderExtensions)
  1491  	}
  1492  
  1493  	bldr.SetValidateMixins(validateMixins)
  1494  
  1495  	if err := bldr.Save(c.logger, builder.CreatorMetadata{Version: c.version}); err != nil {
  1496  		return nil, err
  1497  	}
  1498  	return bldr, nil
  1499  }
  1500  
  1501  // Returns a string iwith lowercase a-z, of length n
  1502  func randString(n int) string {
  1503  	b := make([]byte, n)
  1504  	_, err := rand.Read(b)
  1505  	if err != nil {
  1506  		panic(err)
  1507  	}
  1508  	for i := range b {
  1509  		b[i] = 'a' + (b[i] % 26)
  1510  	}
  1511  	return string(b)
  1512  }
  1513  
  1514  func (c *Client) logImageNameAndSha(ctx context.Context, publish bool, imageRef name.Reference) error {
  1515  	// The image name and sha are printed in the lifecycle logs, and there is no need to print it again, unless output is suppressed.
  1516  	if !logging.IsQuiet(c.logger) {
  1517  		return nil
  1518  	}
  1519  
  1520  	img, err := c.imageFetcher.Fetch(ctx, imageRef.Name(), image.FetchOptions{Daemon: !publish, PullPolicy: image.PullNever})
  1521  	if err != nil {
  1522  		return fmt.Errorf("fetching built image: %w", err)
  1523  	}
  1524  
  1525  	id, err := img.Identifier()
  1526  	if err != nil {
  1527  		return fmt.Errorf("reading image sha: %w", err)
  1528  	}
  1529  
  1530  	// Remove tag, if it exists, from the image name
  1531  	imgName := strings.TrimSuffix(imageRef.String(), imageRef.Identifier())
  1532  	imgNameAndSha := fmt.Sprintf("%s@%s\n", imgName, parseDigestFromImageID(id))
  1533  
  1534  	// Access the logger's Writer directly to bypass ReportSuccessfulQuietBuild mode
  1535  	_, err = c.logger.Writer().Write([]byte(imgNameAndSha))
  1536  	return err
  1537  }
  1538  
  1539  func parseDigestFromImageID(id imgutil.Identifier) string {
  1540  	var digest string
  1541  	switch v := id.(type) {
  1542  	case local.IDIdentifier:
  1543  		digest = v.String()
  1544  	case remote.DigestIdentifier:
  1545  		digest = v.Digest.DigestStr()
  1546  	}
  1547  
  1548  	digest = strings.TrimPrefix(digest, "sha256:")
  1549  	return fmt.Sprintf("sha256:%s", digest)
  1550  }
  1551  
  1552  func createInlineBuildpack(bp projectTypes.Buildpack, stackID string) (string, error) {
  1553  	pathToInlineBuilpack, err := os.MkdirTemp("", "inline-cnb")
  1554  	if err != nil {
  1555  		return pathToInlineBuilpack, err
  1556  	}
  1557  
  1558  	if bp.Version == "" {
  1559  		bp.Version = "0.0.0"
  1560  	}
  1561  
  1562  	if err = createBuildpackTOML(pathToInlineBuilpack, bp.ID, bp.Version, bp.Script.API, []dist.Stack{{ID: stackID}}, []dist.Target{}, nil); err != nil {
  1563  		return pathToInlineBuilpack, err
  1564  	}
  1565  
  1566  	shell := bp.Script.Shell
  1567  	if shell == "" {
  1568  		shell = "/bin/sh"
  1569  	}
  1570  
  1571  	binBuild := fmt.Sprintf(`#!%s
  1572  
  1573  %s
  1574  `, shell, bp.Script.Inline)
  1575  
  1576  	binDetect := fmt.Sprintf(`#!%s
  1577  
  1578  exit 0
  1579  `, shell)
  1580  
  1581  	if err = createBinScript(pathToInlineBuilpack, "build", binBuild, nil); err != nil {
  1582  		return pathToInlineBuilpack, err
  1583  	}
  1584  
  1585  	if err = createBinScript(pathToInlineBuilpack, "build.bat", bp.Script.Inline, nil); err != nil {
  1586  		return pathToInlineBuilpack, err
  1587  	}
  1588  
  1589  	if err = createBinScript(pathToInlineBuilpack, "detect", binDetect, nil); err != nil {
  1590  		return pathToInlineBuilpack, err
  1591  	}
  1592  
  1593  	if err = createBinScript(pathToInlineBuilpack, "detect.bat", bp.Script.Inline, nil); err != nil {
  1594  		return pathToInlineBuilpack, err
  1595  	}
  1596  
  1597  	return pathToInlineBuilpack, nil
  1598  }
  1599  
  1600  // fullImagePath parses the inputImageReference provided by the user and creates the directory
  1601  // structure if create value is true
  1602  func fullImagePath(inputImageRef InputImageReference, create bool) (string, error) {
  1603  	imagePath, err := inputImageRef.FullName()
  1604  	if err != nil {
  1605  		return "", errors.Wrapf(err, "evaluating image %s destination path", inputImageRef.Name())
  1606  	}
  1607  
  1608  	if create {
  1609  		if err := os.MkdirAll(imagePath, os.ModePerm); err != nil {
  1610  			return "", errors.Wrapf(err, "creating %s layout application destination", imagePath)
  1611  		}
  1612  	}
  1613  
  1614  	return imagePath, nil
  1615  }
  1616  
  1617  // appendLayoutVolumes mount host volume into the build container, in the form '<host path>:<target path>[:<options>]'
  1618  // the volumes mounted are:
  1619  // - The path where the user wants the image to be exported in OCI layout format
  1620  // - The previous image path if it exits
  1621  // - The run-image path
  1622  func appendLayoutVolumes(volumes []string, config layoutPathConfig) []string {
  1623  	if config.hostPreviousImagePath != "" {
  1624  		volumes = append(volumes, readOnlyVolume(config.hostPreviousImagePath, config.targetPreviousImagePath),
  1625  			readOnlyVolume(config.hostRunImagePath, config.targetRunImagePath),
  1626  			writableVolume(config.hostImagePath, config.targetImagePath))
  1627  	} else {
  1628  		volumes = append(volumes, readOnlyVolume(config.hostRunImagePath, config.targetRunImagePath),
  1629  			writableVolume(config.hostImagePath, config.targetImagePath))
  1630  	}
  1631  	return volumes
  1632  }
  1633  
  1634  func writableVolume(hostPath, targetPath string) string {
  1635  	tp := targetPath
  1636  	if !filepath.IsAbs(targetPath) {
  1637  		tp = filepath.Join(string(filepath.Separator), targetPath)
  1638  	}
  1639  	return fmt.Sprintf("%s:%s:rw", hostPath, tp)
  1640  }
  1641  
  1642  func readOnlyVolume(hostPath, targetPath string) string {
  1643  	tp := targetPath
  1644  	if !filepath.IsAbs(targetPath) {
  1645  		tp = filepath.Join(string(filepath.Separator), targetPath)
  1646  	}
  1647  	return fmt.Sprintf("%s:%s", hostPath, tp)
  1648  }