github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/builder_create.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "path/filepath" 6 7 "github.com/pkg/errors" 8 "github.com/spf13/cobra" 9 10 "github.com/buildpacks/pack/builder" 11 "github.com/buildpacks/pack/internal/config" 12 "github.com/buildpacks/pack/internal/style" 13 "github.com/buildpacks/pack/pkg/buildpack" 14 "github.com/buildpacks/pack/pkg/client" 15 "github.com/buildpacks/pack/pkg/image" 16 "github.com/buildpacks/pack/pkg/logging" 17 ) 18 19 // BuilderCreateFlags define flags provided to the CreateBuilder command 20 type BuilderCreateFlags struct { 21 Publish bool 22 BuilderTomlPath string 23 Registry string 24 Policy string 25 Flatten []string 26 Label map[string]string 27 } 28 29 // CreateBuilder creates a builder image, based on a builder config 30 func BuilderCreate(logger logging.Logger, cfg config.Config, pack PackClient) *cobra.Command { 31 var flags BuilderCreateFlags 32 33 cmd := &cobra.Command{ 34 Use: "create <image-name> --config <builder-config-path>", 35 Args: cobra.ExactArgs(1), 36 Short: "Create builder image", 37 Example: "pack builder create my-builder:bionic --config ./builder.toml", 38 Long: `A builder is an image that bundles all the bits and information on how to build your apps, such as buildpacks, an implementation of the lifecycle, and a build-time environment that pack uses when executing the lifecycle. When building an app, you can use community builders; you can see our suggestions by running 39 40 pack builders suggest 41 42 Creating a custom builder allows you to control what buildpacks are used and what image apps are based on. For more on how to create a builder, see: https://buildpacks.io/docs/operator-guide/create-a-builder/. 43 `, 44 RunE: logError(logger, func(cmd *cobra.Command, args []string) error { 45 if err := validateCreateFlags(&flags, cfg); err != nil { 46 return err 47 } 48 49 stringPolicy := flags.Policy 50 if stringPolicy == "" { 51 stringPolicy = cfg.PullPolicy 52 } 53 pullPolicy, err := image.ParsePullPolicy(stringPolicy) 54 if err != nil { 55 return errors.Wrapf(err, "parsing pull policy %s", flags.Policy) 56 } 57 58 builderConfig, warns, err := builder.ReadConfig(flags.BuilderTomlPath) 59 if err != nil { 60 return errors.Wrap(err, "invalid builder toml") 61 } 62 for _, w := range warns { 63 logger.Warnf("builder configuration: %s", w) 64 } 65 66 if hasExtensions(builderConfig) { 67 if !cfg.Experimental { 68 return errors.New("builder config contains image extensions; support for image extensions is currently experimental") 69 } 70 } 71 72 relativeBaseDir, err := filepath.Abs(filepath.Dir(flags.BuilderTomlPath)) 73 if err != nil { 74 return errors.Wrap(err, "getting absolute path for config") 75 } 76 77 envMap, warnings, err := builder.ParseBuildConfigEnv(builderConfig.Build.Env, flags.BuilderTomlPath) 78 for _, v := range warnings { 79 logger.Warn(v) 80 } 81 if err != nil { 82 return err 83 } 84 85 toFlatten, err := buildpack.ParseFlattenBuildModules(flags.Flatten) 86 if err != nil { 87 return err 88 } 89 90 imageName := args[0] 91 if err := pack.CreateBuilder(cmd.Context(), client.CreateBuilderOptions{ 92 RelativeBaseDir: relativeBaseDir, 93 BuildConfigEnv: envMap, 94 BuilderName: imageName, 95 Config: builderConfig, 96 Publish: flags.Publish, 97 Registry: flags.Registry, 98 PullPolicy: pullPolicy, 99 Flatten: toFlatten, 100 Labels: flags.Label, 101 }); err != nil { 102 return err 103 } 104 logger.Infof("Successfully created builder image %s", style.Symbol(imageName)) 105 logging.Tip(logger, "Run %s to use this builder", style.Symbol(fmt.Sprintf("pack build <image-name> --builder %s", imageName))) 106 return nil 107 }), 108 } 109 110 cmd.Flags().StringVarP(&flags.Registry, "buildpack-registry", "R", cfg.DefaultRegistryName, "Buildpack Registry by name") 111 if !cfg.Experimental { 112 cmd.Flags().MarkHidden("buildpack-registry") 113 } 114 cmd.Flags().StringVarP(&flags.BuilderTomlPath, "config", "c", "", "Path to builder TOML file (required)") 115 cmd.Flags().BoolVar(&flags.Publish, "publish", false, "Publish the builder directly to the container registry specified in <image-name>, instead of the daemon.") 116 cmd.Flags().StringVar(&flags.Policy, "pull-policy", "", "Pull policy to use. Accepted values are always, never, and if-not-present. The default is always") 117 cmd.Flags().StringArrayVar(&flags.Flatten, "flatten", nil, "List of buildpacks to flatten together into a single layer (format: '<buildpack-id>@<buildpack-version>,<buildpack-id>@<buildpack-version>'") 118 cmd.Flags().StringToStringVarP(&flags.Label, "label", "l", nil, "Labels to add to the builder image, in the form of '<name>=<value>'") 119 120 AddHelpFlag(cmd, "create") 121 return cmd 122 } 123 124 func hasExtensions(builderConfig builder.Config) bool { 125 return len(builderConfig.Extensions) > 0 || len(builderConfig.OrderExtensions) > 0 126 } 127 128 func validateCreateFlags(flags *BuilderCreateFlags, cfg config.Config) error { 129 if flags.Publish && flags.Policy == image.PullNever.String() { 130 return errors.Errorf("--publish and --pull-policy never cannot be used together. The --publish flag requires the use of remote images.") 131 } 132 133 if flags.Registry != "" && !cfg.Experimental { 134 return client.NewExperimentError("Support for buildpack registries is currently experimental.") 135 } 136 137 if flags.BuilderTomlPath == "" { 138 return errors.Errorf("Please provide a builder config path, using --config.") 139 } 140 141 return nil 142 }