github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/client/package_buildpack.go (about) 1 package client 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 8 pubbldpkg "github.com/buildpacks/pack/buildpackage" 9 "github.com/buildpacks/pack/internal/layer" 10 "github.com/buildpacks/pack/internal/paths" 11 "github.com/buildpacks/pack/internal/style" 12 "github.com/buildpacks/pack/pkg/blob" 13 "github.com/buildpacks/pack/pkg/buildpack" 14 "github.com/buildpacks/pack/pkg/image" 15 ) 16 17 const ( 18 // Packaging indicator that format of inputs/outputs will be an OCI image on the registry. 19 FormatImage = "image" 20 21 // Packaging indicator that format of output will be a file on the host filesystem. 22 FormatFile = "file" 23 24 // CNBExtension is the file extension for a cloud native buildpack tar archive 25 CNBExtension = ".cnb" 26 ) 27 28 // PackageBuildpackOptions is a configuration object used to define 29 // the behavior of PackageBuildpack. 30 type PackageBuildpackOptions struct { 31 // The base director to resolve relative assest from 32 RelativeBaseDir string 33 34 // The name of the output buildpack artifact. 35 Name string 36 37 // Type of output format, The options are the either the const FormatImage, or FormatFile. 38 Format string 39 40 // Defines the Buildpacks configuration. 41 Config pubbldpkg.Config 42 43 // Push resulting builder image up to a registry 44 // specified in the Name variable. 45 Publish bool 46 47 // Strategy for updating images before packaging. 48 PullPolicy image.PullPolicy 49 50 // Name of the buildpack registry. Used to 51 // add buildpacks to a package. 52 Registry string 53 54 // Flatten layers 55 Flatten bool 56 57 // List of buildpack images to exclude from the package been flatten. 58 FlattenExclude []string 59 60 // Map of labels to add to the Buildpack 61 Labels map[string]string 62 } 63 64 // PackageBuildpack packages buildpack(s) into either an image or file. 65 func (c *Client) PackageBuildpack(ctx context.Context, opts PackageBuildpackOptions) error { 66 if opts.Format == "" { 67 opts.Format = FormatImage 68 } 69 70 if opts.Config.Platform.OS == "windows" && !c.experimental { 71 return NewExperimentError("Windows buildpackage support is currently experimental.") 72 } 73 74 err := c.validateOSPlatform(ctx, opts.Config.Platform.OS, opts.Publish, opts.Format) 75 if err != nil { 76 return err 77 } 78 79 writerFactory, err := layer.NewWriterFactory(opts.Config.Platform.OS) 80 if err != nil { 81 return errors.Wrap(err, "creating layer writer factory") 82 } 83 84 var packageBuilderOpts []buildpack.PackageBuilderOption 85 if opts.Flatten { 86 packageBuilderOpts = append(packageBuilderOpts, buildpack.DoNotFlatten(opts.FlattenExclude), 87 buildpack.WithLayerWriterFactory(writerFactory), buildpack.WithLogger(c.logger)) 88 } 89 packageBuilder := buildpack.NewBuilder(c.imageFactory, packageBuilderOpts...) 90 91 bpURI := opts.Config.Buildpack.URI 92 if bpURI == "" { 93 return errors.New("buildpack URI must be provided") 94 } 95 96 mainBlob, err := c.downloadBuildpackFromURI(ctx, bpURI, opts.RelativeBaseDir) 97 if err != nil { 98 return err 99 } 100 101 bp, err := buildpack.FromBuildpackRootBlob(mainBlob, writerFactory, c.logger) 102 if err != nil { 103 return errors.Wrapf(err, "creating buildpack from %s", style.Symbol(bpURI)) 104 } 105 106 packageBuilder.SetBuildpack(bp) 107 108 for _, dep := range opts.Config.Dependencies { 109 mainBP, deps, err := c.buildpackDownloader.Download(ctx, dep.URI, buildpack.DownloadOptions{ 110 RegistryName: opts.Registry, 111 RelativeBaseDir: opts.RelativeBaseDir, 112 ImageOS: opts.Config.Platform.OS, 113 ImageName: dep.ImageName, 114 Daemon: !opts.Publish, 115 PullPolicy: opts.PullPolicy, 116 }) 117 118 if err != nil { 119 return errors.Wrapf(err, "packaging dependencies (uri=%s,image=%s)", style.Symbol(dep.URI), style.Symbol(dep.ImageName)) 120 } 121 122 packageBuilder.AddDependencies(mainBP, deps) 123 } 124 125 switch opts.Format { 126 case FormatFile: 127 return packageBuilder.SaveAsFile(opts.Name, opts.Config.Platform.OS, opts.Labels) 128 case FormatImage: 129 _, err = packageBuilder.SaveAsImage(opts.Name, opts.Publish, opts.Config.Platform.OS, opts.Labels) 130 return errors.Wrapf(err, "saving image") 131 default: 132 return errors.Errorf("unknown format: %s", style.Symbol(opts.Format)) 133 } 134 } 135 136 func (c *Client) downloadBuildpackFromURI(ctx context.Context, uri, relativeBaseDir string) (blob.Blob, error) { 137 absPath, err := paths.FilePathToURI(uri, relativeBaseDir) 138 if err != nil { 139 return nil, errors.Wrapf(err, "making absolute: %s", style.Symbol(uri)) 140 } 141 uri = absPath 142 143 c.logger.Debugf("Downloading buildpack from URI: %s", style.Symbol(uri)) 144 blob, err := c.downloader.Download(ctx, uri) 145 if err != nil { 146 return nil, errors.Wrapf(err, "downloading buildpack from %s", style.Symbol(uri)) 147 } 148 149 return blob, nil 150 } 151 152 func (c *Client) validateOSPlatform(ctx context.Context, os string, publish bool, format string) error { 153 if publish || format == FormatFile { 154 return nil 155 } 156 157 info, err := c.docker.Info(ctx) 158 if err != nil { 159 return err 160 } 161 162 if info.OSType != os { 163 return errors.Errorf("invalid %s specified: DOCKER_OS is %s", style.Symbol("platform.os"), style.Symbol(info.OSType)) 164 } 165 166 return nil 167 }