kcl-lang.io/kpm@v0.8.7-0.20240520061008-9fc4c5efc8c7/pkg/cmd/cmd_update.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 "os" 10 "slices" 11 "strings" 12 13 "github.com/dominikbraun/graph" 14 "github.com/urfave/cli/v2" 15 "golang.org/x/mod/module" 16 "kcl-lang.io/kpm/pkg/client" 17 "kcl-lang.io/kpm/pkg/env" 18 "kcl-lang.io/kpm/pkg/mvs" 19 pkg "kcl-lang.io/kpm/pkg/package" 20 "kcl-lang.io/kpm/pkg/reporter" 21 "kcl-lang.io/kpm/pkg/semver" 22 ) 23 24 // NewUpdateCmd new a Command for `kpm update`. 25 func NewUpdateCmd(kpmcli *client.KpmClient) *cli.Command { 26 return &cli.Command{ 27 Hidden: false, 28 Name: "update", 29 Usage: "Update dependencies listed in kcl.mod.lock based on kcl.mod", 30 Flags: []cli.Flag{ 31 &cli.BoolFlag{ 32 Name: FLAG_NO_SUM_CHECK, 33 Usage: "do not check the checksum of the package and update kcl.mod.lock", 34 }, 35 }, 36 Action: func(c *cli.Context) error { 37 return KpmUpdate(c, kpmcli) 38 }, 39 } 40 } 41 42 func KpmUpdate(c *cli.Context, kpmcli *client.KpmClient) error { 43 kpmcli.SetNoSumCheck(c.Bool(FLAG_NO_SUM_CHECK)) 44 45 // acquire the lock of the package cache. 46 err := kpmcli.AcquirePackageCacheLock() 47 if err != nil { 48 return err 49 } 50 51 defer func() { 52 // release the lock of the package cache after the function returns. 53 releaseErr := kpmcli.ReleasePackageCacheLock() 54 if releaseErr != nil && err == nil { 55 err = releaseErr 56 } 57 }() 58 59 pkgInfos := c.Args().Slice() 60 61 pwd, err := os.Getwd() 62 if err != nil { 63 return reporter.NewErrorEvent(reporter.Bug, err, "internal bugs, please contact us to fix it.") 64 } 65 66 kclPkg, err := kpmcli.LoadPkgFromPath(pwd) 67 if err != nil { 68 return err 69 } 70 71 globalPkgPath, err := env.GetAbsPkgPath() 72 if err != nil { 73 return err 74 } 75 76 err = kclPkg.ValidateKpmHome(globalPkgPath) 77 if err != (*reporter.KpmEvent)(nil) { 78 return err 79 } 80 81 var ( 82 modulesToUpgrade []module.Version 83 modulesToDowngrade []module.Version 84 ) 85 86 for _, pkgInfo := range pkgInfos { 87 err = GetModulesToUpdate(kclPkg, modulesToUpgrade, modulesToDowngrade, pkgInfo) 88 if err != nil { 89 reporter.Report(err) 90 } 91 } 92 93 _, depGraph, err := kpmcli.InitGraphAndDownloadDeps(kclPkg) 94 if err != nil { 95 return err 96 } 97 98 reqs := mvs.ReqsGraph{ 99 Graph: depGraph, 100 KpmClient: kpmcli, 101 KpmPkg: kclPkg, 102 } 103 104 target := module.Version{Path: kclPkg.GetPkgName(), Version: kclPkg.GetPkgVersion()} 105 buildList, err := mvs.UpdateBuildList(target, modulesToUpgrade, modulesToDowngrade, &reqs) 106 if err != nil { 107 return reporter.NewErrorEvent(reporter.FailedUpdatingBuildList, err, "failed to update build list") 108 } 109 110 // get all the vertices in the graph 111 modules, err := graph.TopologicalSort(depGraph) 112 if err != nil { 113 return reporter.NewErrorEvent(reporter.FailedTopologicalSort, err, "failed to sort the dependencies") 114 } 115 116 kclPkg.ModFile.Dependencies.Deps = make(map[string]pkg.Dependency) 117 118 for _, module := range modules { 119 err = InsertModuleToDeps(kclPkg, module, target, buildList, reqs) 120 if err != nil { 121 return err 122 } 123 } 124 125 err = kpmcli.UpdateDeps(kclPkg) 126 if err != nil { 127 return err 128 } 129 return nil 130 } 131 132 // GetModulesToUpdate validates if the packages is present in kcl.mod file and 133 // find the latest version if version is not specified. Depending on the value of pkgVersion, 134 // modulesToUpgrade or modulesToDowngrade will be updated. 135 func GetModulesToUpdate(kclPkg *pkg.KclPkg, modulesToUpgrade []module.Version, modulesToDowngrade []module.Version, pkgInfo string) error { 136 pkgInfo = strings.TrimSpace(pkgInfo) 137 pkgName, pkgVersion, err := ParseOciPkgNameAndVersion(pkgInfo) 138 if err != nil { 139 return err 140 } 141 142 var dep pkg.Dependency 143 var ok bool 144 if dep, ok = kclPkg.Deps[pkgName]; !ok { 145 return err 146 } 147 148 if pkgVersion == "" { 149 var releases []string 150 releases, err = client.GetReleasesFromSource(dep.GetSourceType(), dep.GetDownloadPath()) 151 if err != nil { 152 return reporter.NewErrorEvent( 153 reporter.FailedGetReleases, 154 err, 155 fmt.Sprintf("failed to get releases for %s", pkgName), 156 ) 157 } 158 pkgVersion, err = semver.LatestCompatibleVersion(releases, dep.Version) 159 if err != nil { 160 return reporter.NewErrorEvent( 161 reporter.FailedSelectLatestCompatibleVersion, 162 err, 163 fmt.Sprintf("failed to find the latest version for %s", pkgName), 164 ) 165 } 166 } 167 if pkgVersion < dep.Version { 168 modulesToDowngrade = append(modulesToDowngrade, module.Version{Path: pkgName, Version: pkgVersion}) 169 } else if pkgVersion > dep.Version { 170 modulesToUpgrade = append(modulesToUpgrade, module.Version{Path: pkgName, Version: pkgVersion}) 171 } 172 return nil 173 } 174 175 // InsertModuleToDeps checks whether module is present in the buildList and it is not the same as the target module, 176 // and inserts it to the dependencies of kclPkg 177 func InsertModuleToDeps(kclPkg *pkg.KclPkg, module module.Version, target module.Version, buildList []module.Version, reqs mvs.ReqsGraph) (error) { 178 if module.Path == target.Path || !slices.Contains(buildList, module) { 179 return nil 180 } 181 d := pkg.Dependency{ 182 Name: module.Path, 183 Version: module.Version, 184 } 185 d.FullName = d.GenDepFullName() 186 _, properties, err := reqs.VertexWithProperties(module) 187 if err != nil { 188 return reporter.NewErrorEvent(reporter.FailedGetVertexProperties, err, "failed to get vertex with properties") 189 } 190 // there must be one property depending on the download source type 191 for sourceType, uri := range properties.Attributes { 192 d.Source, err = pkg.GenSource(sourceType, uri, module.Version) 193 if err != nil { 194 return reporter.NewErrorEvent(reporter.FailedGenerateSource, err, "failed to generate source") 195 } 196 } 197 kclPkg.ModFile.Dependencies.Deps[module.Path] = d 198 return nil 199 }