github.com/KusionStack/kpm@v0.8.4-0.20240326033734-dc72298a30e5/pkg/cmd/cmd_push.go (about)

     1  // Copyright 2023 The KCL Authors. All rights reserved.
     2  // Deprecated: The entire contents of this file will be deprecated. 
     3  // Please use the kcl cli - https://github.com/kcl-lang/cli.
     4  
     5  package cmd
     6  
     7  import (
     8  	"fmt"
     9  	"net/url"
    10  	"os"
    11  
    12  	"github.com/urfave/cli/v2"
    13  	"kcl-lang.io/kpm/pkg/client"
    14  	"kcl-lang.io/kpm/pkg/errors"
    15  	"kcl-lang.io/kpm/pkg/oci"
    16  	"kcl-lang.io/kpm/pkg/opt"
    17  	pkg "kcl-lang.io/kpm/pkg/package"
    18  	"kcl-lang.io/kpm/pkg/reporter"
    19  	"kcl-lang.io/kpm/pkg/utils"
    20  )
    21  
    22  // NewPushCmd new a Command for `kpm push`.
    23  func NewPushCmd(kpmcli *client.KpmClient) *cli.Command {
    24  	return &cli.Command{
    25  		Hidden: false,
    26  		Name:   "push",
    27  		Usage:  "push kcl package to OCI registry.",
    28  		Flags: []cli.Flag{
    29  			&cli.StringFlag{
    30  				Name:  FLAG_TAR_PATH,
    31  				Usage: "a kcl file as the compile entry file",
    32  			},
    33  			// '--vendor' will trigger the vendor mode
    34  			// In the vendor mode, the package search path is the subdirectory 'vendor' in current package.
    35  			// In the non-vendor mode, the package search path is the $KCL_PKG_PATH.
    36  			&cli.BoolFlag{
    37  				Name:  FLAG_VENDOR,
    38  				Usage: "push in vendor mode",
    39  			},
    40  		},
    41  		Action: func(c *cli.Context) error {
    42  			return KpmPush(c, kpmcli)
    43  		},
    44  	}
    45  }
    46  
    47  func KpmPush(c *cli.Context, kpmcli *client.KpmClient) error {
    48  	localTarPath := c.String(FLAG_TAR_PATH)
    49  	ociUrl := c.Args().First()
    50  
    51  	var err error
    52  
    53  	if len(localTarPath) == 0 {
    54  		// If the tar package to be pushed is not specified,
    55  		// the current kcl package is packaged into tar and pushed.
    56  		err = pushCurrentPackage(ociUrl, c.Bool(FLAG_VENDOR), kpmcli)
    57  	} else {
    58  		// Else push the tar package specified.
    59  		err = pushTarPackage(ociUrl, localTarPath, c.Bool(FLAG_VENDOR), kpmcli)
    60  	}
    61  
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  // genDefaultOciUrlForKclPkg will generate the default oci url from the current package.
    70  func genDefaultOciUrlForKclPkg(pkg *pkg.KclPkg, kpmcli *client.KpmClient) (string, error) {
    71  
    72  	urlPath := utils.JoinPath(kpmcli.GetSettings().DefaultOciRepo(), pkg.GetPkgName())
    73  
    74  	u := &url.URL{
    75  		Scheme: oci.OCI_SCHEME,
    76  		Host:   kpmcli.GetSettings().DefaultOciRegistry(),
    77  		Path:   urlPath,
    78  	}
    79  
    80  	return u.String(), nil
    81  }
    82  
    83  // pushCurrentPackage will push the current package to the oci registry.
    84  func pushCurrentPackage(ociUrl string, vendorMode bool, kpmcli *client.KpmClient) error {
    85  	pwd, err := os.Getwd()
    86  
    87  	if err != nil {
    88  		reporter.ReportEventToStderr(reporter.NewEvent(reporter.Bug, "internal bug: failed to load working directory"))
    89  		return err
    90  	}
    91  	// 1. Load the current kcl packege.
    92  	kclPkg, err := pkg.LoadKclPkg(pwd)
    93  
    94  	if err != nil {
    95  		reporter.ReportEventToStderr(reporter.NewEvent(reporter.FailedLoadKclMod, fmt.Sprintf("failed to load package in '%s'", pwd)))
    96  		return err
    97  	}
    98  
    99  	// 2. push the package
   100  	return pushPackage(ociUrl, kclPkg, vendorMode, kpmcli)
   101  }
   102  
   103  // pushTarPackage will push the kcl package in tarPath to the oci registry.
   104  // If the tar in 'tarPath' is not a kcl package tar, pushTarPackage will return an error.
   105  func pushTarPackage(ociUrl, localTarPath string, vendorMode bool, kpmcli *client.KpmClient) error {
   106  	var kclPkg *pkg.KclPkg
   107  	var err error
   108  
   109  	// clean the temp dir used to untar kcl package tar file.
   110  	defer func() {
   111  		if kclPkg != nil && utils.DirExists(kclPkg.HomePath) {
   112  			err = os.RemoveAll(kclPkg.HomePath)
   113  			if err != nil {
   114  				err = reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, failed to clean the temp dir.")
   115  			}
   116  		}
   117  	}()
   118  
   119  	// 1. load the kcl package from the tar path.
   120  	kclPkg, err = pkg.LoadKclPkgFromTar(localTarPath)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	// 2. push the package
   126  	return pushPackage(ociUrl, kclPkg, vendorMode, kpmcli)
   127  }
   128  
   129  // pushPackage will push the kcl package to the oci registry.
   130  // 1. pushPackage will package the current kcl package into default tar path.
   131  // 2. If the oci url is not specified, generate the default oci url from the current package.
   132  // 3. Generate the OCI options from oci url and the version of current kcl package.
   133  // 4. Push the package to the oci registry.
   134  func pushPackage(ociUrl string, kclPkg *pkg.KclPkg, vendorMode bool, kpmcli *client.KpmClient) error {
   135  
   136  	tarPath, err := kpmcli.PackagePkg(kclPkg, vendorMode)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	// clean the tar path.
   142  	defer func() {
   143  		if kclPkg != nil && utils.DirExists(tarPath) {
   144  			err = os.RemoveAll(tarPath)
   145  			if err != nil {
   146  				err = reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, failed to clean the temp dir.")
   147  			}
   148  		}
   149  	}()
   150  
   151  	// 2. If the oci url is not specified, generate the default oci url from the current package.
   152  	if len(ociUrl) == 0 {
   153  		ociUrl, err = genDefaultOciUrlForKclPkg(kclPkg, kpmcli)
   154  		if err != nil || len(ociUrl) == 0 {
   155  			return reporter.NewErrorEvent(
   156  				reporter.InvalidCmd,
   157  				fmt.Errorf("failed to generate default oci url for current package"),
   158  				"run 'kpm push help' for more information",
   159  			)
   160  		}
   161  	}
   162  
   163  	// 3. Generate the OCI options from oci url and the version of current kcl package.
   164  	ociOpts, err := opt.ParseOciOptionFromOciUrl(ociUrl, kclPkg.GetPkgTag())
   165  	if err != (*reporter.KpmEvent)(nil) {
   166  		return reporter.NewErrorEvent(
   167  			reporter.UnsupportOciUrlScheme,
   168  			errors.InvalidOciUrl,
   169  			"only support url scheme 'oci://'",
   170  		)
   171  	}
   172  
   173  	ociOpts.Annotations, err = oci.GenOciManifestFromPkg(kclPkg)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	reporter.ReportMsgTo(fmt.Sprintf("package '%s' will be pushed", kclPkg.GetPkgName()), kpmcli.GetLogWriter())
   179  	// 4. Push it.
   180  	err = kpmcli.PushToOci(tarPath, ociOpts)
   181  	if err != (*reporter.KpmEvent)(nil) {
   182  		return err
   183  	}
   184  	return nil
   185  }