github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/extension_package.go (about)

     1  package commands
     2  
     3  import (
     4  	"context"
     5  	"path/filepath"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/spf13/cobra"
     9  
    10  	pubbldpkg "github.com/buildpacks/pack/buildpackage"
    11  	"github.com/buildpacks/pack/internal/config"
    12  	"github.com/buildpacks/pack/internal/style"
    13  	"github.com/buildpacks/pack/pkg/client"
    14  	"github.com/buildpacks/pack/pkg/image"
    15  	"github.com/buildpacks/pack/pkg/logging"
    16  )
    17  
    18  // ExtensionPackageFlags define flags provided to the ExtensionPackage command
    19  type ExtensionPackageFlags struct {
    20  	PackageTomlPath string
    21  	Format          string
    22  	Publish         bool
    23  	Policy          string
    24  }
    25  
    26  // ExtensionPackager packages extensions
    27  type ExtensionPackager interface {
    28  	PackageExtension(ctx context.Context, options client.PackageBuildpackOptions) error
    29  }
    30  
    31  // ExtensionPackage packages (a) extension(s) into OCI format, based on a package config
    32  func ExtensionPackage(logger logging.Logger, cfg config.Config, packager ExtensionPackager, packageConfigReader PackageConfigReader) *cobra.Command {
    33  	var flags ExtensionPackageFlags
    34  	cmd := &cobra.Command{
    35  		Use:   "package <name> --config <config-path>",
    36  		Short: "Package an extension in OCI format",
    37  		Args:  cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
    38  		RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
    39  			if err := validateExtensionPackageFlags(&flags); err != nil {
    40  				return err
    41  			}
    42  
    43  			stringPolicy := flags.Policy
    44  			if stringPolicy == "" {
    45  				stringPolicy = cfg.PullPolicy
    46  			}
    47  
    48  			pullPolicy, err := image.ParsePullPolicy(stringPolicy)
    49  			if err != nil {
    50  				return errors.Wrap(err, "parsing pull policy")
    51  			}
    52  
    53  			exPackageCfg := pubbldpkg.DefaultExtensionConfig()
    54  			relativeBaseDir := ""
    55  			if flags.PackageTomlPath != "" {
    56  				exPackageCfg, err = packageConfigReader.Read(flags.PackageTomlPath)
    57  				if err != nil {
    58  					return errors.Wrap(err, "reading config")
    59  				}
    60  
    61  				relativeBaseDir, err = filepath.Abs(filepath.Dir(flags.PackageTomlPath))
    62  				if err != nil {
    63  					return errors.Wrap(err, "getting absolute path for config")
    64  				}
    65  			}
    66  			name := args[0]
    67  			if flags.Format == client.FormatFile {
    68  				switch ext := filepath.Ext(name); ext {
    69  				case client.CNBExtension:
    70  				case "":
    71  					name += client.CNBExtension
    72  				default:
    73  					logger.Warnf("%s is not a valid extension for a packaged extension. Packaged extensions must have a %s extension", style.Symbol(ext), style.Symbol(client.CNBExtension))
    74  				}
    75  			}
    76  
    77  			if err := packager.PackageExtension(cmd.Context(), client.PackageBuildpackOptions{
    78  				RelativeBaseDir: relativeBaseDir,
    79  				Name:            name,
    80  				Format:          flags.Format,
    81  				Config:          exPackageCfg,
    82  				Publish:         flags.Publish,
    83  				PullPolicy:      pullPolicy,
    84  			}); err != nil {
    85  				return err
    86  			}
    87  
    88  			action := "created"
    89  			location := "docker daemon"
    90  			if flags.Publish {
    91  				action = "published"
    92  				location = "registry"
    93  			}
    94  			if flags.Format == client.FormatFile {
    95  				location = "file"
    96  			}
    97  			logger.Infof("Successfully %s package %s and saved to %s", action, style.Symbol(name), location)
    98  			return nil
    99  		}),
   100  	}
   101  
   102  	// flags will be added here
   103  	cmd.Flags().StringVarP(&flags.PackageTomlPath, "config", "c", "", "Path to package TOML config")
   104  	cmd.Flags().StringVarP(&flags.Format, "format", "f", "", `Format to save package as ("image" or "file")`)
   105  	cmd.Flags().BoolVar(&flags.Publish, "publish", false, `Publish the extension directly to the container registry specified in <name>, instead of the daemon (applies to "--format=image" only).`)
   106  	cmd.Flags().StringVar(&flags.Policy, "pull-policy", "", "Pull policy to use. Accepted values are always, never, and if-not-present. The default is always")
   107  	AddHelpFlag(cmd, "package")
   108  	return cmd
   109  }
   110  
   111  func validateExtensionPackageFlags(p *ExtensionPackageFlags) error {
   112  	if p.Publish && p.Policy == image.PullNever.String() {
   113  		return errors.Errorf("--publish and --pull-policy=never cannot be used together. The --publish flag requires the use of remote images.")
   114  	}
   115  	return nil
   116  }