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 }