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

     1  package api
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"kcl-lang.io/kcl-go/pkg/kcl"
     9  	"kcl-lang.io/kpm/pkg/client"
    10  	"kcl-lang.io/kpm/pkg/constants"
    11  	"kcl-lang.io/kpm/pkg/env"
    12  	"kcl-lang.io/kpm/pkg/errors"
    13  	"kcl-lang.io/kpm/pkg/oci"
    14  	"kcl-lang.io/kpm/pkg/opt"
    15  	pkg "kcl-lang.io/kpm/pkg/package"
    16  	"kcl-lang.io/kpm/pkg/reporter"
    17  	"kcl-lang.io/kpm/pkg/runner"
    18  	"kcl-lang.io/kpm/pkg/utils"
    19  )
    20  
    21  // RunTar will compile the kcl package from a kcl package tar.
    22  func RunTar(tarPath string, opts *opt.CompileOptions) (string, error) {
    23  	// The directory after extracting the tar package is taken as the root directory of the package,
    24  	// and kclvm is called to compile the kcl program under the 'destDir'.
    25  	// e.g.
    26  	// if the tar path is 'xxx/xxx/xxx/test.tar',
    27  	// the 'xxx/xxx/xxx/test' will be taken as the root path of the kcl package to compile.
    28  	compileResult, compileErr := RunTarPkg(tarPath, opts)
    29  
    30  	if compileErr != nil {
    31  		return "", compileErr
    32  	}
    33  	return compileResult.GetRawYamlResult(), nil
    34  }
    35  
    36  // RunOci will compile the kcl package from an OCI reference.
    37  func RunOci(ociRef, version string, opts *opt.CompileOptions) (string, error) {
    38  	compileResult, compileErr := RunOciPkg(ociRef, version, opts)
    39  
    40  	if compileErr != nil {
    41  		return "", compileErr
    42  	}
    43  	return compileResult.GetRawYamlResult(), nil
    44  }
    45  
    46  // RunPkg will compile current kcl package.
    47  func RunPkg(opts *opt.CompileOptions) (string, error) {
    48  
    49  	compileResult, err := RunCurrentPkg(opts)
    50  	if err != nil {
    51  		return "", err
    52  	}
    53  
    54  	return compileResult.GetRawYamlResult(), nil
    55  }
    56  
    57  // RunPkgInPath will load the 'KclPkg' from path 'pkgPath'.
    58  // And run the kcl package with entry file in 'entryFilePath' in 'vendorMode'.
    59  func RunPkgInPath(opts *opt.CompileOptions) (string, error) {
    60  	// Call the kcl compiler.
    61  	compileResult, err := RunPkgWithOpt(opts)
    62  	if err != nil {
    63  		return "", err
    64  	}
    65  
    66  	return compileResult.GetRawYamlResult(), nil
    67  }
    68  
    69  // CompileWithOpt will compile the kcl program without kcl package.
    70  // Deprecated: This method will not be maintained in the future. Use RunWithOpts instead.
    71  func RunWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
    72  	if len(opts.Entries()) > 0 {
    73  		for _, entry := range opts.Entries() {
    74  			if filepath.IsAbs(entry) {
    75  				opts.Merge(kcl.WithKFilenames(entry))
    76  			} else {
    77  				opts.Merge(kcl.WithKFilenames(filepath.Join(opts.PkgPath(), entry)))
    78  			}
    79  		}
    80  	} else if !opts.HasSettingsYaml() && len(opts.KFilenameList) == 0 {
    81  		// If no entry, no kcl files and no settings files.
    82  		opts.Merge(kcl.WithKFilenames(opts.PkgPath()))
    83  	}
    84  	opts.Merge(kcl.WithWorkDir(opts.PkgPath()))
    85  	return kcl.RunWithOpts(*opts.Option)
    86  }
    87  
    88  // RunWithOpts will compile the kcl package with the compile options.
    89  func RunWithOpts(opts ...opt.Option) (*kcl.KCLResultList, error) {
    90  	mergedOpts := opt.DefaultCompileOptions()
    91  	for _, opt := range opts {
    92  		opt(mergedOpts)
    93  	}
    94  	return runPkgWithOpt(mergedOpts)
    95  }
    96  
    97  // getAbsInputPath will return the abs path of the file path described by '--input'.
    98  // If the path exists after 'inputPath' is computed as a full path, it will be returned.
    99  // If not, the kpm checks whether the full path of 'pkgPath/inputPath' exists,
   100  // If the full path of 'pkgPath/inputPath' exists, it will be returned.
   101  // If not, getAbsInputPath returns 'entry file not found' error.
   102  func getAbsInputPath(pkgPath string, inputPath string) (string, error) {
   103  	absPath, err := filepath.Abs(filepath.Join(pkgPath, inputPath))
   104  	if err != nil {
   105  		return "", err
   106  	}
   107  
   108  	if utils.DirExists(absPath) {
   109  		return absPath, nil
   110  	}
   111  
   112  	return "", errors.EntryFileNotFound
   113  }
   114  
   115  // RunPkgWithOpt will compile the kcl package with the compile options.
   116  // Deprecated: This method will not be maintained in the future. Use RunWithOpts instead.
   117  func RunPkgWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   118  	kpmcli, err := client.NewKpmClient()
   119  	kpmcli.SetNoSumCheck(opts.NoSumCheck())
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	return run(kpmcli, opts)
   124  }
   125  
   126  func runPkgWithOpt(opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   127  	kpmcli, err := client.NewKpmClient()
   128  	kpmcli.SetNoSumCheck(opts.NoSumCheck())
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return run(kpmcli, opts)
   133  }
   134  
   135  // RunCurrentPkg will compile the current kcl package.
   136  func RunCurrentPkg(opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   137  	pwd, err := os.Getwd()
   138  	opts.SetPkgPath(pwd)
   139  
   140  	if err != nil {
   141  		return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, failed to load working directory.")
   142  	}
   143  
   144  	return RunPkgWithOpt(opts)
   145  }
   146  
   147  // RunTarPkg will compile the kcl package from a kcl package tar.
   148  func RunTarPkg(tarPath string, opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   149  	absTarPath, err := utils.AbsTarPath(tarPath)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	// Extract the tar package to a directory with the same name.
   154  	// e.g.
   155  	// 'xxx/xxx/xxx/test.tar' will be extracted to the directory 'xxx/xxx/xxx/test'.
   156  	destDir := strings.TrimSuffix(absTarPath, filepath.Ext(absTarPath))
   157  	err = utils.UnTarDir(absTarPath, destDir)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	opts.SetPkgPath(destDir)
   163  	kpmcli, err := client.NewKpmClient()
   164  	kpmcli.SetNoSumCheck(opts.NoSumCheck())
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	// The directory after extracting the tar package is taken as the root directory of the package,
   169  	// and kclvm is called to compile the kcl program under the 'destDir'.
   170  	// e.g.
   171  	// if the tar path is 'xxx/xxx/xxx/test.tar',
   172  	// the 'xxx/xxx/xxx/test' will be taken as the root path of the kcl package to compile.
   173  	return run(kpmcli, opts)
   174  }
   175  
   176  // RunOciPkg will compile the kcl package from an OCI reference.
   177  func RunOciPkg(ociRef, version string, opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   178  	kpmcli, err := client.NewKpmClient()
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	kpmcli.SetNoSumCheck(opts.NoSumCheck())
   183  	ociOpts, err := kpmcli.ParseOciOptionFromString(ociRef, version)
   184  
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	// 1. Create the temporary directory to pull the tar.
   190  	tmpDir, err := os.MkdirTemp("", "")
   191  	if err != nil {
   192  		return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, please contact us to fix it.")
   193  	}
   194  	// clean the temp dir.
   195  	defer os.RemoveAll(tmpDir)
   196  
   197  	localPath := ociOpts.SanitizePathWithSuffix(tmpDir)
   198  
   199  	// 2. Pull the tar.
   200  	err = oci.Pull(localPath, ociOpts.Reg, ociOpts.Repo, ociOpts.Tag, kpmcli.GetSettings())
   201  
   202  	if err != (*reporter.KpmEvent)(nil) {
   203  		return nil, err
   204  	}
   205  
   206  	// 3.Get the (*.tar) file path.
   207  	matches, err := filepath.Glob(filepath.Join(localPath, constants.KCL_PKG_TAR))
   208  	if err != nil || len(matches) != 1 {
   209  		if err != nil {
   210  			return nil, reporter.NewErrorEvent(reporter.FailedGetPkg, err, "failed to pull kcl package")
   211  		} else {
   212  			return nil, errors.FailedPull
   213  		}
   214  	}
   215  
   216  	// 4. Untar the tar file.
   217  	absTarPath, err := utils.AbsTarPath(matches[0])
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	// Extract the tar package to a directory with the same name.
   222  	// e.g.
   223  	// 'xxx/xxx/xxx/test.tar' will be extracted to the directory 'xxx/xxx/xxx/test'.
   224  	destDir := strings.TrimSuffix(absTarPath, filepath.Ext(absTarPath))
   225  	err = utils.UnTarDir(absTarPath, destDir)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	opts.SetPkgPath(destDir)
   231  	return run(kpmcli, opts)
   232  }
   233  
   234  // 'run' will compile the kcl package from the compile options by kpm client.
   235  func run(kpmcli *client.KpmClient, opts *opt.CompileOptions) (*kcl.KCLResultList, error) {
   236  	pkgPath, err := filepath.Abs(opts.PkgPath())
   237  	if err != nil {
   238  		return nil, reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, please contact us to fix it.")
   239  	}
   240  
   241  	kclPkg, err := pkg.LoadKclPkg(pkgPath)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	kclPkg.SetVendorMode(opts.IsVendor())
   247  
   248  	globalPkgPath, err := env.GetAbsPkgPath()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	err = kclPkg.ValidateKpmHome(globalPkgPath)
   254  	if err != (*reporter.KpmEvent)(nil) {
   255  		return nil, err
   256  	}
   257  
   258  	if len(opts.Entries()) > 0 {
   259  		// add entry from '--input'
   260  		for _, entry := range opts.Entries() {
   261  			if filepath.IsAbs(entry) {
   262  				opts.Merge(kcl.WithKFilenames(entry))
   263  			} else {
   264  				opts.Merge(kcl.WithKFilenames(filepath.Join(opts.PkgPath(), entry)))
   265  			}
   266  		}
   267  		// add entry from 'kcl.mod'
   268  	} else if kclPkg.HasProfile() {
   269  		opts.Merge(*kclPkg.GetKclOpts())
   270  	} else if !opts.HasSettingsYaml() {
   271  		// no entry
   272  		opts.Merge(kcl.WithKFilenames(opts.PkgPath()))
   273  	}
   274  	opts.Merge(kcl.WithWorkDir(opts.PkgPath()))
   275  
   276  	// Calculate the absolute path of entry file described by '--input'.
   277  	compiler := runner.NewCompilerWithOpts(opts)
   278  
   279  	kpmcli.SetLogWriter(opts.LogWriter())
   280  
   281  	// Call the kcl compiler.
   282  	compileResult, err := kpmcli.Compile(kclPkg, compiler)
   283  
   284  	if err != nil {
   285  		return nil, reporter.NewErrorEvent(reporter.CompileFailed, err, "failed to compile the kcl package")
   286  	}
   287  
   288  	return compileResult, nil
   289  }