From 8fd77d8154e6859ca94dd764ee921912f6353df8 Mon Sep 17 00:00:00 2001 From: zongzhe Date: Thu, 26 Sep 2024 15:06:29 +0800 Subject: [PATCH 1/4] feat: make vendor supports mvs Signed-off-by: zongzhe --- pkg/client/client.go | 113 +--------- pkg/client/client_test.go | 54 +++-- .../resolve_metadata/with_package/kcl.mod | 2 +- .../with_package/kcl.mod.lock | 14 +- pkg/client/test_data/test_vendor/dep1/kcl.mod | 7 + .../test_data/test_vendor/dep1/kcl.mod.lock | 5 + pkg/client/test_data/test_vendor/dep1/main.k | 1 + pkg/client/test_data/test_vendor/dep2/kcl.mod | 7 + .../test_data/test_vendor/dep2/kcl.mod.lock | 5 + pkg/client/test_data/test_vendor/dep2/main.k | 1 + pkg/client/test_data/test_vendor/pkg/kcl.mod | 9 + .../test_data/test_vendor/pkg/kcl.mod.lock | 13 ++ .../test_vendor/pkg/kcl.mod.lock.expect | 13 ++ pkg/client/test_data/test_vendor/pkg/main.k | 1 + .../test_data/test_vendor_mvs/dep1/kcl.mod | 7 + .../test_vendor_mvs/dep1/kcl.mod.lock | 5 + .../test_data/test_vendor_mvs/dep1/main.k | 1 + .../test_data/test_vendor_mvs/dep2/kcl.mod | 7 + .../test_vendor_mvs/dep2/kcl.mod.lock | 5 + .../test_data/test_vendor_mvs/dep2/main.k | 1 + .../test_data/test_vendor_mvs/pkg/kcl.mod | 8 + .../test_vendor_mvs/pkg/kcl.mod.lock | 9 + .../test_data/test_vendor_mvs/pkg/main.k | 1 + pkg/client/vendor.go | 205 ++++++++++++++++++ pkg/client/vendor_test.go | 109 ++++++++++ test/e2e/test_suites/test_data/kcl1.tar | Bin 691712 -> 827904 bytes 26 files changed, 479 insertions(+), 124 deletions(-) create mode 100644 pkg/client/test_data/test_vendor/dep1/kcl.mod create mode 100644 pkg/client/test_data/test_vendor/dep1/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor/dep1/main.k create mode 100644 pkg/client/test_data/test_vendor/dep2/kcl.mod create mode 100644 pkg/client/test_data/test_vendor/dep2/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor/dep2/main.k create mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod create mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect create mode 100644 pkg/client/test_data/test_vendor/pkg/main.k create mode 100644 pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod create mode 100644 pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor_mvs/dep1/main.k create mode 100644 pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod create mode 100644 pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor_mvs/dep2/main.k create mode 100644 pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod create mode 100644 pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod.lock create mode 100644 pkg/client/test_data/test_vendor_mvs/pkg/main.k create mode 100644 pkg/client/vendor.go create mode 100644 pkg/client/vendor_test.go diff --git a/pkg/client/client.go b/pkg/client/client.go index 514eee97..c002e071 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -13,7 +13,6 @@ import ( "github.com/dominikbraun/graph" "github.com/elliotchance/orderedmap/v2" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/otiai10/copy" "golang.org/x/mod/module" "kcl-lang.io/kcl-go/pkg/kcl" "oras.land/oras-go/pkg/auth" @@ -668,105 +667,6 @@ func (c *KpmClient) Package(kclPkg *pkg.KclPkg, tarPath string, vendorMode bool) return nil } -func (c *KpmClient) vendorDeps(kclPkg *pkg.KclPkg, vendorPath string) error { - lockDeps := make([]pkg.Dependency, 0, kclPkg.Dependencies.Deps.Len()) - for _, k := range kclPkg.Dependencies.Deps.Keys() { - d, _ := kclPkg.Dependencies.Deps.Get(k) - lockDeps = append(lockDeps, d) - } - - // Traverse all dependencies in kcl.mod.lock. - for i := 0; i < len(lockDeps); i++ { - d := lockDeps[i] - if len(d.Name) == 0 { - return errors.InvalidDependency - } - // If the dependency is from the local path, do not vendor it, vendor its dependencies. - if d.IsFromLocal() { - dpkg, err := c.LoadPkgFromPath(d.GetLocalFullPath(kclPkg.HomePath)) - if err != nil { - return err - } - err = c.vendorDeps(dpkg, vendorPath) - if err != nil { - return err - } - continue - } else { - vendorFullPath := filepath.Join(vendorPath, d.GenPathSuffix()) - - // If the package already exists in the 'vendor', do nothing. - if utils.DirExists(vendorFullPath) { - d.LocalFullPath = vendorFullPath - lockDeps[i] = d - continue - } else { - // If not in the 'vendor', check the global cache. - cacheFullPath := c.getDepStorePath(c.homePath, &d, false) - if utils.DirExists(cacheFullPath) { - // If there is, copy it into the 'vendor' directory. - err := copy.Copy(cacheFullPath, vendorFullPath) - if err != nil { - return err - } - } else { - // re-download if not. - err := c.AddDepToPkg(kclPkg, &d) - if err != nil { - return err - } - // re-vendor again with new kcl.mod and kcl.mod.lock - err = c.vendorDeps(kclPkg, vendorPath) - if err != nil { - return err - } - return nil - } - } - - if d.GetPackage() != "" { - tempVendorFullPath, err := utils.FindPackage(vendorFullPath, d.GetPackage()) - if err != nil { - return err - } - vendorFullPath = tempVendorFullPath - } - - dpkg, err := c.LoadPkgFromPath(vendorFullPath) - if err != nil { - return err - } - - // Vendor the dependencies of the current dependency. - err = c.vendorDeps(dpkg, vendorPath) - if err != nil { - return err - } - d.LocalFullPath = vendorFullPath - lockDeps[i] = d - } - } - - // Update the dependencies in kcl.mod.lock. - for _, d := range lockDeps { - kclPkg.Dependencies.Deps.Set(d.Name, d) - } - - return nil -} - -// VendorDeps will vendor all the dependencies of the current kcl package. -func (c *KpmClient) VendorDeps(kclPkg *pkg.KclPkg) error { - // Mkdir the dir "vendor". - vendorPath := kclPkg.LocalVendorPath() - err := os.MkdirAll(vendorPath, 0755) - if err != nil { - return err - } - - return c.vendorDeps(kclPkg, vendorPath) -} - // FillDepInfo will fill registry information for a dependency. func (c *KpmClient) FillDepInfo(dep *pkg.Dependency, homepath string) error { // Homepath for a dependency is the homepath of the kcl package. @@ -1428,9 +1328,9 @@ func (c *KpmClient) InitGraphAndDownloadDeps(kclPkg *pkg.KclPkg) (*pkg.Dependenc } // dependencyExists will check whether the dependency exists in the local filesystem. -func (c *KpmClient) dependencyExistsLocal(searchPath string, dep *pkg.Dependency) (*pkg.Dependency, error) { +func (c *KpmClient) dependencyExistsLocal(searchPath string, dep *pkg.Dependency, isVendor bool) (*pkg.Dependency, error) { // If the flag '--no_sum_check' is set, skip the checksum check. - deppath := c.getDepStorePath(searchPath, dep, false) + deppath := c.getDepStorePath(searchPath, dep, isVendor) if utils.DirExists(deppath) { depPkg, err := c.LoadPkgFromPath(deppath) if err != nil { @@ -1440,6 +1340,13 @@ func (c *KpmClient) dependencyExistsLocal(searchPath string, dep *pkg.Dependency // TODO: new local dependency structure will replace this // issue: https://github.com/kcl-lang/kpm/issues/384 dep.FullName = dep.GenDepFullName() + + if dep.GetPackage() != "" { + dep.LocalFullPath, err = utils.FindPackage(dep.LocalFullPath, dep.GetPackage()) + if err != nil { + return nil, err + } + } return dep, nil } return nil, nil @@ -1459,7 +1366,7 @@ func (c *KpmClient) DownloadDeps(deps *pkg.Dependencies, lockDeps *pkg.Dependenc return nil, errors.InvalidDependency } - existDep, err := c.dependencyExistsLocal(pkghome, &d) + existDep, err := c.dependencyExistsLocal(pkghome, &d, false) if existDep != nil && err == nil { newDeps.Deps.Set(d.Name, *existDep) continue diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 84052b98..e223e2a3 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -622,6 +622,13 @@ func testResolveDepsVendorMode(t *testing.T) { FullName: "kcl1_0.0.1", Version: "0.0.1", Sum: kcl1Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl1", + Tag: "0.0.1", + }, + }, } depKcl2 := pkg.Dependency{ @@ -629,6 +636,13 @@ func testResolveDepsVendorMode(t *testing.T) { FullName: "kcl2_0.0.1", Version: "0.0.1", Sum: kcl2Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl2", + Tag: "0.0.1", + }, + }, } mppTest := orderedmap.NewOrderedMap[string, pkg.Dependency]() @@ -687,6 +701,13 @@ func testCompileWithEntryFile(t *testing.T) { FullName: "kcl1_0.0.1", Version: "0.0.1", Sum: kcl1Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl2", + Tag: "0.0.1", + }, + }, } kcl2Sum, _ := utils.HashDir(filepath.Join(kpm_home, "kcl2")) depKcl2 := pkg.Dependency{ @@ -694,6 +715,13 @@ func testCompileWithEntryFile(t *testing.T) { FullName: "kcl2_0.0.1", Version: "0.0.1", Sum: kcl2Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl2", + Tag: "0.0.1", + }, + }, } mppTest := orderedmap.NewOrderedMap[string, pkg.Dependency]() @@ -796,7 +824,7 @@ func testResolveMetadataInJsonStr(t *testing.T) { expectedDep.Deps["flask_demo_kcl_manifests"] = pkg.Dependency{ Name: "flask_demo_kcl_manifests", FullName: "flask-demo-kcl-manifests_ade147b", - Version: "ade147b", + Version: "0.1.0", LocalFullPath: filepath.Join(globalPkgPath, "flask-demo-kcl-manifests_ade147b"), } @@ -819,7 +847,7 @@ func testResolveMetadataInJsonStr(t *testing.T) { expectedDep.Deps["flask_demo_kcl_manifests"] = pkg.Dependency{ Name: "flask_demo_kcl_manifests", FullName: "flask-demo-kcl-manifests_ade147b", - Version: "ade147b", + Version: "0.1.0", LocalFullPath: filepath.Join(vendorDir, "flask-demo-kcl-manifests_ade147b"), } @@ -869,13 +897,13 @@ func testResolveMetadataInJsonStrWithPackage(t *testing.T) { Deps: make(map[string]pkg.Dependency), } - localFullPath, err := utils.FindPackage(filepath.Join(globalPkgPath, "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8"), "helloworld") + localFullPath, err := utils.FindPackage(filepath.Join(globalPkgPath, "flask-demo-kcl-manifests_8308200"), "cc") assert.Equal(t, err, nil) - expectedDep.Deps["helloworld"] = pkg.Dependency{ - Name: "helloworld", - FullName: "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8", - Version: "ee03122b5f45b09eb48694422fc99a0772f6bba8", + expectedDep.Deps["cc"] = pkg.Dependency{ + Name: "cc", + FullName: "flask-demo-kcl-manifests_8308200", + Version: "8308200", LocalFullPath: localFullPath, } @@ -903,19 +931,19 @@ func testResolveMetadataInJsonStrWithPackage(t *testing.T) { assert.Equal(t, err, nil) assert.Equal(t, utils.DirExists(vendorDir), true) - assert.Equal(t, utils.DirExists(filepath.Join(vendorDir, "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8")), true) + assert.Equal(t, utils.DirExists(filepath.Join(vendorDir, "flask-demo-kcl-manifests_8308200")), true) - localFullPath, err = utils.FindPackage(filepath.Join(vendorDir, "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8"), "helloworld") + localFullPath, err = utils.FindPackage(filepath.Join(vendorDir, "flask-demo-kcl-manifests_8308200"), "cc") assert.Equal(t, err, nil) expectedDep = pkg.DependenciesUI{ Deps: make(map[string]pkg.Dependency), } - expectedDep.Deps["helloworld"] = pkg.Dependency{ - Name: "helloworld", - FullName: "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8", - Version: "ee03122b5f45b09eb48694422fc99a0772f6bba8", + expectedDep.Deps["cc"] = pkg.Dependency{ + Name: "cc", + FullName: "flask-demo-kcl-manifests_8308200", + Version: "8308200", LocalFullPath: localFullPath, } diff --git a/pkg/client/test_data/resolve_metadata/with_package/kcl.mod b/pkg/client/test_data/resolve_metadata/with_package/kcl.mod index f6bc0ae4..c74af8a1 100644 --- a/pkg/client/test_data/resolve_metadata/with_package/kcl.mod +++ b/pkg/client/test_data/resolve_metadata/with_package/kcl.mod @@ -4,4 +4,4 @@ edition = "v0.8.0" version = "0.0.1" [dependencies] -helloworld = { git = "https://github.com/kcl-lang/modules.git", commit = "ee03122b5f45b09eb48694422fc99a0772f6bba8", package = "helloworld" } +cc = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests.git", commit = "8308200", package = "cc" } diff --git a/pkg/client/test_data/resolve_metadata/with_package/kcl.mod.lock b/pkg/client/test_data/resolve_metadata/with_package/kcl.mod.lock index eb0223ed..32355765 100644 --- a/pkg/client/test_data/resolve_metadata/with_package/kcl.mod.lock +++ b/pkg/client/test_data/resolve_metadata/with_package/kcl.mod.lock @@ -1,8 +1,8 @@ [dependencies] - [dependencies.helloworld] - name = "helloworld" - full_name = "modules_ee03122b5f45b09eb48694422fc99a0772f6bba8" - version = "0.1.2" - url = "https://github.com/kcl-lang/modules.git" - commit = "ee03122b5f45b09eb48694422fc99a0772f6bba8" - package = "helloworld" + [dependencies.cc] + name = "cc" + full_name = "flask-demo-kcl-manifests_8308200" + version = "0.0.1" + url = "https://github.com/kcl-lang/flask-demo-kcl-manifests.git" + commit = "8308200" + package = "cc" diff --git a/pkg/client/test_data/test_vendor/dep1/kcl.mod b/pkg/client/test_data/test_vendor/dep1/kcl.mod new file mode 100644 index 00000000..81a79664 --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep1/kcl.mod @@ -0,0 +1,7 @@ +[package] +name = "dep1" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +helloworld = "0.1.1" diff --git a/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock b/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock new file mode 100644 index 00000000..8de6a50f --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock @@ -0,0 +1,5 @@ +[dependencies] + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.1" + version = "0.1.1" diff --git a/pkg/client/test_data/test_vendor/dep1/main.k b/pkg/client/test_data/test_vendor/dep1/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep1/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor/dep2/kcl.mod b/pkg/client/test_data/test_vendor/dep2/kcl.mod new file mode 100644 index 00000000..95ee77f5 --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep2/kcl.mod @@ -0,0 +1,7 @@ +[package] +name = "dep2" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +helloworld = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock b/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock new file mode 100644 index 00000000..e64da0b4 --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock @@ -0,0 +1,5 @@ +[dependencies] + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.2" + version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/dep2/main.k b/pkg/client/test_data/test_vendor/dep2/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor/dep2/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod b/pkg/client/test_data/test_vendor/pkg/kcl.mod new file mode 100644 index 00000000..e130c21f --- /dev/null +++ b/pkg/client/test_data/test_vendor/pkg/kcl.mod @@ -0,0 +1,9 @@ +[package] +name = "pkg" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +dep1 = { path = "../dep1" } +dep2 = { path = "../dep2" } +helloworld = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock new file mode 100644 index 00000000..018034cd --- /dev/null +++ b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock @@ -0,0 +1,13 @@ +[dependencies] + [dependencies.dep1] + name = "dep1" + full_name = "dep1_0.0.1" + version = "0.0.1" + [dependencies.dep2] + name = "dep2" + full_name = "dep2_0.0.1" + version = "0.0.1" + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.2" + version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect new file mode 100644 index 00000000..018034cd --- /dev/null +++ b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect @@ -0,0 +1,13 @@ +[dependencies] + [dependencies.dep1] + name = "dep1" + full_name = "dep1_0.0.1" + version = "0.0.1" + [dependencies.dep2] + name = "dep2" + full_name = "dep2_0.0.1" + version = "0.0.1" + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.2" + version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/main.k b/pkg/client/test_data/test_vendor/pkg/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor/pkg/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod b/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod new file mode 100644 index 00000000..81a79664 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod @@ -0,0 +1,7 @@ +[package] +name = "dep1" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +helloworld = "0.1.1" diff --git a/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod.lock b/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod.lock new file mode 100644 index 00000000..8de6a50f --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep1/kcl.mod.lock @@ -0,0 +1,5 @@ +[dependencies] + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.1" + version = "0.1.1" diff --git a/pkg/client/test_data/test_vendor_mvs/dep1/main.k b/pkg/client/test_data/test_vendor_mvs/dep1/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep1/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod b/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod new file mode 100644 index 00000000..95ee77f5 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod @@ -0,0 +1,7 @@ +[package] +name = "dep2" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +helloworld = "0.1.2" diff --git a/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod.lock b/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod.lock new file mode 100644 index 00000000..e64da0b4 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep2/kcl.mod.lock @@ -0,0 +1,5 @@ +[dependencies] + [dependencies.helloworld] + name = "helloworld" + full_name = "helloworld_0.1.2" + version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor_mvs/dep2/main.k b/pkg/client/test_data/test_vendor_mvs/dep2/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/dep2/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod b/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod new file mode 100644 index 00000000..b1567789 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod @@ -0,0 +1,8 @@ +[package] +name = "pkg" +edition = "v0.10.0" +version = "0.0.1" + +[dependencies] +dep1 = { path = "../dep1" } +dep2 = { path = "../dep2" } diff --git a/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod.lock b/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod.lock new file mode 100644 index 00000000..88838e6e --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/pkg/kcl.mod.lock @@ -0,0 +1,9 @@ +[dependencies] + [dependencies.dep1] + name = "dep1" + full_name = "dep1_0.0.1" + version = "0.0.1" + [dependencies.dep2] + name = "dep2" + full_name = "dep2_0.0.1" + version = "0.0.1" diff --git a/pkg/client/test_data/test_vendor_mvs/pkg/main.k b/pkg/client/test_data/test_vendor_mvs/pkg/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/client/test_data/test_vendor_mvs/pkg/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/vendor.go b/pkg/client/vendor.go new file mode 100644 index 00000000..0c128253 --- /dev/null +++ b/pkg/client/vendor.go @@ -0,0 +1,205 @@ +package client + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/elliotchance/orderedmap/v2" + "github.com/hashicorp/go-version" + "github.com/otiai10/copy" + "kcl-lang.io/kpm/pkg/constants" + "kcl-lang.io/kpm/pkg/downloader" + pkg "kcl-lang.io/kpm/pkg/package" + "kcl-lang.io/kpm/pkg/utils" + "kcl-lang.io/kpm/pkg/visitor" +) + +// VendorDeps will vendor all the dependencies of the current kcl package. +func (c *KpmClient) VendorDeps(kclPkg *pkg.KclPkg) error { + // Mkdir the dir "vendor". + vendorPath := kclPkg.LocalVendorPath() + err := os.MkdirAll(vendorPath, 0755) + if err != nil { + return err + } + + return c.vendorDeps(kclPkg, vendorPath) +} + +func (c *KpmClient) vendorDeps(kclPkg *pkg.KclPkg, vendorPath string) error { + // Select all the vendored dependencies + // and fill the vendored dependencies into kclPkg.Dependencies.Deps + err := c.selectVendoredDeps(kclPkg, vendorPath, kclPkg.Dependencies.Deps) + if err != nil { + return err + } + + // Move all the selected vendored dependencies to the vendor directory. + for _, depName := range kclPkg.Dependencies.Deps.Keys() { + dep, ok := kclPkg.Dependencies.Deps.Get(depName) + if !ok { + return fmt.Errorf("failed to get dependency %s", depName) + } + + // Check if the dependency is already vendored in the vendor directory. + existLocalDep, err := c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) + if err != nil { + return err + } + + if existLocalDep == nil { + vendorFullPath := filepath.Join(vendorPath, dep.GenDepFullName()) + cacheFullPath := filepath.Join(c.homePath, dep.GenDepFullName()) + if !utils.DirExists(vendorFullPath) { + err := copy.Copy(cacheFullPath, vendorFullPath) + if err != nil { + return err + } + } + // Load the vendored dependency + existLocalDep, err = c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) + if err != nil { + return err + } + + if existLocalDep == nil { + return fmt.Errorf("failed to find the vendored dependency %s", depName) + } + } + kclPkg.Dependencies.Deps.Set(depName, *existLocalDep) + } + return nil +} + +func (c *KpmClient) selectVendoredDeps(kpkg *pkg.KclPkg, vendorPath string, vendoredDeps *orderedmap.OrderedMap[string, pkg.Dependency]) error { + // visitorSelectorFunc selects the visitor for the source. + // For remote source, it will use the RemoteVisitor and enable the cache. + // For local source, it will use the PkgVisitor. + visitorSelectorFunc := func(source *downloader.Source) (visitor.Visitor, error) { + pkgVisitor := &visitor.PkgVisitor{ + Settings: &c.settings, + LogWriter: c.logWriter, + } + + if source.IsRemote() { + return &visitor.RemoteVisitor{ + PkgVisitor: pkgVisitor, + Downloader: c.DepDownloader, + InsecureSkipTLSverify: c.insecureSkipTLSverify, + EnableCache: true, + CachePath: c.homePath, + }, nil + } else if source.IsLocalTarPath() || source.IsLocalTgzPath() { + return visitor.NewArchiveVisitor(pkgVisitor), nil + } else if source.IsLocalPath() { + rootPath, err := source.FindRootPath() + if err != nil { + return nil, err + } + kclmodpath := filepath.Join(rootPath, constants.KCL_MOD) + if utils.DirExists(kclmodpath) { + return pkgVisitor, nil + } else { + return visitor.NewVirtualPkgVisitor(pkgVisitor), nil + } + } else { + return nil, fmt.Errorf("unsupported source") + } + } + + // Iterate all the dependencies of the package in kcl.mod. + for _, depName := range kpkg.ModFile.Dependencies.Deps.Keys() { + dep, ok := kpkg.ModFile.Dependencies.Deps.Get(depName) + if !ok { + return fmt.Errorf("failed to get dependency %s", depName) + } + + // Select the dependency with the MVS + // Keep the greater version in dependencies graph + selectedDep := &dep + if existsDep, exists := vendoredDeps.Get(depName); exists && len(existsDep.Version) > 0 && + len(dep.Version) > 0 && + // TODO: Skip the git dependencies for now and get the version from the cache when the new local storage structure is complete + // the new local storage structure: https://github.com/kcl-lang/kpm/issues/384 + dep.Source.Git == nil && + existsDep.Source.Git == nil { + existsVersion, err := version.NewVersion(existsDep.Version) + if err != nil { + return err + } + depVersion, err := version.NewVersion(dep.Version) + if err != nil { + return err + } + // Select the greater version + if existsVersion.GreaterThan(depVersion) { + selectedDep = &existsDep + } + } + + // Check if the dependency is already vendored in the vendor directory. + existLocalDep, err := c.dependencyExistsLocal(filepath.Dir(vendorPath), selectedDep, true) + if err != nil { + return err + } + + // If the dependency is already vendored, just update the dependency path. + if existLocalDep != nil { + // Collect the vendored dependency + vendoredDeps.Set(depName, *existLocalDep) + // Load the vendored dependency + dpkg, err := pkg.LoadKclPkgWithOpts( + pkg.WithPath(existLocalDep.LocalFullPath), + pkg.WithSettings(&c.settings), + ) + if err != nil { + return err + } + // Vendor the indirected dependencies of the vendored dependency + err = c.selectVendoredDeps(dpkg, vendorPath, vendoredDeps) + if err != nil { + return err + } + } else { + // If the dependency is not vendored in the vendor directory + selectDepSource := &selectedDep.Source + // Check if the dependency is a local path and it is not an absolute path. + // If it is not an absolute path, transform the path to an absolute path. + if selectDepSource.IsLocalPath() && !filepath.IsAbs(selectDepSource.Local.Path) { + selectDepSource = &downloader.Source{ + Local: &downloader.Local{ + Path: filepath.Join(kpkg.HomePath, selectDepSource.Local.Path), + }, + } + } + + // By visitor, if the dependency is a remote source, it will download and load the dependency + // if the dependency is a local source, it will load the dependency. + // if the dependency is cached, it will load the dependency from the cache. + pkgVisitor, err := visitorSelectorFunc(selectDepSource) + if err != nil { + return err + } + err = pkgVisitor.Visit(selectDepSource, + func(kclPkg *pkg.KclPkg) error { + existLocalDep, err := c.dependencyExistsLocal(c.homePath, selectedDep, false) + if err != nil { + return err + } + + if existLocalDep == nil { + return fmt.Errorf("failed to find the vendored dependency %s", depName) + } + // Collect the vendored dependency + vendoredDeps.Set(depName, *existLocalDep) + return nil + }, + ) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/client/vendor_test.go b/pkg/client/vendor_test.go new file mode 100644 index 00000000..2d04cc20 --- /dev/null +++ b/pkg/client/vendor_test.go @@ -0,0 +1,109 @@ +package client + +import ( + "os" + "path/filepath" + "testing" + + "github.com/elliotchance/orderedmap/v2" + "github.com/stretchr/testify/assert" + "kcl-lang.io/kpm/pkg/downloader" + pkg "kcl-lang.io/kpm/pkg/package" + "kcl-lang.io/kpm/pkg/settings" + "kcl-lang.io/kpm/pkg/utils" +) + +func TestVendorDeps(t *testing.T) { + testDir := getTestDir("resolve_deps") + kpm_home := filepath.Join(testDir, "kpm_home") + os.RemoveAll(filepath.Join(testDir, "my_kcl")) + kcl1Sum, _ := utils.HashDir(filepath.Join(kpm_home, "kcl1")) + kcl2Sum, _ := utils.HashDir(filepath.Join(kpm_home, "kcl2")) + + depKcl1 := pkg.Dependency{ + Name: "kcl1", + FullName: "kcl1", + Version: "0.0.1", + Sum: kcl1Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl1", + Tag: "0.0.1", + }, + }, + } + + depKcl2 := pkg.Dependency{ + Name: "kcl2", + FullName: "kcl2", + Version: "0.0.1", + Sum: kcl2Sum, + Source: downloader.Source{ + Oci: &downloader.Oci{ + Reg: "ghcr.io", + Repo: "kcl-lang/kcl2", + Tag: "0.0.1", + }, + }, + } + + mppTest := orderedmap.NewOrderedMap[string, pkg.Dependency]() + mppTest.Set("kcl1", depKcl1) + mppTest.Set("kcl2", depKcl2) + + kclPkg := pkg.KclPkg{ + ModFile: pkg.ModFile{ + HomePath: filepath.Join(testDir, "my_kcl"), + // Whether the current package uses the vendor mode + // In the vendor mode, kpm will look for the package in the vendor subdirectory + // in the current package directory. + VendorMode: false, + Dependencies: pkg.Dependencies{ + Deps: mppTest, + }, + }, + HomePath: filepath.Join(testDir, "my_kcl"), + // The dependencies in the current kcl package are the dependencies of kcl.mod.lock, + // not the dependencies in kcl.mod. + Dependencies: pkg.Dependencies{ + Deps: mppTest, + }, + } + + mykclVendorPath := filepath.Join(filepath.Join(testDir, "my_kcl"), "vendor") + assert.Equal(t, utils.DirExists(mykclVendorPath), false) + kpmcli, err := NewKpmClient() + kpmcli.homePath = kpm_home + assert.Equal(t, err, nil) + err = kpmcli.VendorDeps(&kclPkg) + assert.Equal(t, err, nil) + assert.Equal(t, utils.DirExists(mykclVendorPath), true) + assert.Equal(t, utils.DirExists(filepath.Join(mykclVendorPath, "kcl1_0.0.1")), true) + assert.Equal(t, utils.DirExists(filepath.Join(mykclVendorPath, "kcl2_0.0.1")), true) + + maps, err := kpmcli.ResolveDepsIntoMap(&kclPkg) + assert.Equal(t, err, nil) + assert.Equal(t, len(maps), 2) + + os.RemoveAll(filepath.Join(testDir, "my_kcl")) +} + +func TestVendorWithMVS(t *testing.T) { + testDir := getTestDir("test_vendor") + pkgPath := filepath.Join(testDir, "pkg") + kPkg, err := pkg.LoadKclPkgWithOpts( + pkg.WithPath(pkgPath), + pkg.WithSettings(settings.GetSettings()), + ) + assert.Equal(t, err, nil) + + kpmcli, err := NewKpmClient() + assert.Equal(t, err, nil) + err = kpmcli.VendorDeps(kPkg) + assert.Equal(t, err, nil) + + assert.Equal(t, utils.DirExists(filepath.Join(pkgPath, "vendor")), true) + assert.Equal(t, utils.DirExists(filepath.Join(pkgPath, "vendor", "helloworld_0.1.2")), true) + assert.Equal(t, utils.DirExists(filepath.Join(pkgPath, "vendor", "helloworld_0.1.1")), false) +} diff --git a/test/e2e/test_suites/test_data/kcl1.tar b/test/e2e/test_suites/test_data/kcl1.tar index 37427392357739ff85de4788c4d83b93cd69809f..75991074eb412ac89b51f6a7a0b5a676d73d9387 100644 GIT binary patch delta 19813 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 F{twAjD-Hkv From 42c86e3da895850e6ab3c9056c25e00c8bace1a4 Mon Sep 17 00:00:00 2001 From: zongzhe Date: Mon, 14 Oct 2024 14:07:03 +0800 Subject: [PATCH 2/4] fix: rebase main Signed-off-by: zongzhe --- pkg/client/client_test.go | 63 --------------------------------------- pkg/client/vendor_test.go | 10 +++++-- 2 files changed, 8 insertions(+), 65 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index e223e2a3..346e859d 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -75,7 +75,6 @@ func TestWithGlobalLock(t *testing.T) { test.RunTestWithGlobalLock(t, "TestResolveMetadataInJsonStr", testResolveMetadataInJsonStr) test.RunTestWithGlobalLock(t, "testPackageCurrentPkgPath", testPackageCurrentPkgPath) test.RunTestWithGlobalLock(t, "TestUpdateKclModAndLock", testUpdateKclModAndLock) - test.RunTestWithGlobalLock(t, "TestVendorDeps", testVendorDeps) test.RunTestWithGlobalLock(t, "TestResolveDepsWithOnlyKclMod", testResolveDepsWithOnlyKclMod) test.RunTestWithGlobalLock(t, "TestResolveDepsVendorMode", testResolveDepsVendorMode) test.RunTestWithGlobalLock(t, "TestCompileWithEntryFile", testCompileWithEntryFile) @@ -528,68 +527,6 @@ func testUpdateKclModAndLock(t *testing.T) { } } -func testVendorDeps(t *testing.T) { - testDir := getTestDir("resolve_deps") - kpm_home := filepath.Join(testDir, "kpm_home") - os.RemoveAll(filepath.Join(testDir, "my_kcl")) - kcl1Sum, _ := utils.HashDir(filepath.Join(kpm_home, "kcl1")) - kcl2Sum, _ := utils.HashDir(filepath.Join(kpm_home, "kcl2")) - - depKcl1 := pkg.Dependency{ - Name: "kcl1", - FullName: "kcl1", - Version: "0.0.1", - Sum: kcl1Sum, - } - - depKcl2 := pkg.Dependency{ - Name: "kcl2", - FullName: "kcl2", - Version: "0.0.1", - Sum: kcl2Sum, - } - - mppTest := orderedmap.NewOrderedMap[string, pkg.Dependency]() - mppTest.Set("kcl1", depKcl1) - mppTest.Set("kcl2", depKcl2) - - kclPkg := pkg.KclPkg{ - ModFile: pkg.ModFile{ - HomePath: filepath.Join(testDir, "my_kcl"), - // Whether the current package uses the vendor mode - // In the vendor mode, kpm will look for the package in the vendor subdirectory - // in the current package directory. - VendorMode: false, - Dependencies: pkg.Dependencies{ - Deps: mppTest, - }, - }, - HomePath: filepath.Join(testDir, "my_kcl"), - // The dependencies in the current kcl package are the dependencies of kcl.mod.lock, - // not the dependencies in kcl.mod. - Dependencies: pkg.Dependencies{ - Deps: mppTest, - }, - } - - mykclVendorPath := filepath.Join(filepath.Join(testDir, "my_kcl"), "vendor") - assert.Equal(t, utils.DirExists(mykclVendorPath), false) - kpmcli, err := NewKpmClient() - kpmcli.homePath = kpm_home - assert.Equal(t, err, nil) - err = kpmcli.VendorDeps(&kclPkg) - assert.Equal(t, err, nil) - assert.Equal(t, utils.DirExists(mykclVendorPath), true) - assert.Equal(t, utils.DirExists(filepath.Join(mykclVendorPath, "kcl1_0.0.1")), true) - assert.Equal(t, utils.DirExists(filepath.Join(mykclVendorPath, "kcl2_0.0.1")), true) - - maps, err := kpmcli.ResolveDepsIntoMap(&kclPkg) - assert.Equal(t, err, nil) - assert.Equal(t, len(maps), 2) - - os.RemoveAll(filepath.Join(testDir, "my_kcl")) -} - func testResolveDepsWithOnlyKclMod(t *testing.T) { testDir := getTestDir("resolve_dep_with_kclmod") assert.Equal(t, utils.DirExists(filepath.Join(testDir, "kcl.mod.lock")), false) diff --git a/pkg/client/vendor_test.go b/pkg/client/vendor_test.go index 2d04cc20..a12abe01 100644 --- a/pkg/client/vendor_test.go +++ b/pkg/client/vendor_test.go @@ -10,10 +10,11 @@ import ( "kcl-lang.io/kpm/pkg/downloader" pkg "kcl-lang.io/kpm/pkg/package" "kcl-lang.io/kpm/pkg/settings" + "kcl-lang.io/kpm/pkg/test" "kcl-lang.io/kpm/pkg/utils" ) -func TestVendorDeps(t *testing.T) { +func testVendorDeps(t *testing.T) { testDir := getTestDir("resolve_deps") kpm_home := filepath.Join(testDir, "kpm_home") os.RemoveAll(filepath.Join(testDir, "my_kcl")) @@ -89,7 +90,7 @@ func TestVendorDeps(t *testing.T) { os.RemoveAll(filepath.Join(testDir, "my_kcl")) } -func TestVendorWithMVS(t *testing.T) { +func testVendorWithMVS(t *testing.T) { testDir := getTestDir("test_vendor") pkgPath := filepath.Join(testDir, "pkg") kPkg, err := pkg.LoadKclPkgWithOpts( @@ -107,3 +108,8 @@ func TestVendorWithMVS(t *testing.T) { assert.Equal(t, utils.DirExists(filepath.Join(pkgPath, "vendor", "helloworld_0.1.2")), true) assert.Equal(t, utils.DirExists(filepath.Join(pkgPath, "vendor", "helloworld_0.1.1")), false) } + +func TestVendorWithGlobalLock(t *testing.T) { + test.RunTestWithGlobalLock(t, "TestVendorDeps", testVendorDeps) + test.RunTestWithGlobalLock(t, "TestVendorWithMVS", testVendorWithMVS) +} From 10558bff0ab8bfa9f0902bfd86477d83f028608d Mon Sep 17 00:00:00 2001 From: zongzhe Date: Mon, 14 Oct 2024 14:19:29 +0800 Subject: [PATCH 3/4] fix: add feature flag Signed-off-by: zongzhe --- pkg/client/vendor.go | 145 ++++++++++++++++++++++++++++++-------- pkg/client/vendor_test.go | 3 + 2 files changed, 120 insertions(+), 28 deletions(-) diff --git a/pkg/client/vendor.go b/pkg/client/vendor.go index 0c128253..cda83b8c 100644 --- a/pkg/client/vendor.go +++ b/pkg/client/vendor.go @@ -10,6 +10,8 @@ import ( "github.com/otiai10/copy" "kcl-lang.io/kpm/pkg/constants" "kcl-lang.io/kpm/pkg/downloader" + "kcl-lang.io/kpm/pkg/errors" + "kcl-lang.io/kpm/pkg/features" pkg "kcl-lang.io/kpm/pkg/package" "kcl-lang.io/kpm/pkg/utils" "kcl-lang.io/kpm/pkg/visitor" @@ -28,46 +30,133 @@ func (c *KpmClient) VendorDeps(kclPkg *pkg.KclPkg) error { } func (c *KpmClient) vendorDeps(kclPkg *pkg.KclPkg, vendorPath string) error { - // Select all the vendored dependencies - // and fill the vendored dependencies into kclPkg.Dependencies.Deps - err := c.selectVendoredDeps(kclPkg, vendorPath, kclPkg.Dependencies.Deps) - if err != nil { - return err - } - - // Move all the selected vendored dependencies to the vendor directory. - for _, depName := range kclPkg.Dependencies.Deps.Keys() { - dep, ok := kclPkg.Dependencies.Deps.Get(depName) - if !ok { - return fmt.Errorf("failed to get dependency %s", depName) - } - - // Check if the dependency is already vendored in the vendor directory. - existLocalDep, err := c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) + if ok, err := features.Enabled(features.SupportMVS); err != nil && ok { + // Select all the vendored dependencies + // and fill the vendored dependencies into kclPkg.Dependencies.Deps + err := c.selectVendoredDeps(kclPkg, vendorPath, kclPkg.Dependencies.Deps) if err != nil { return err } - if existLocalDep == nil { - vendorFullPath := filepath.Join(vendorPath, dep.GenDepFullName()) - cacheFullPath := filepath.Join(c.homePath, dep.GenDepFullName()) - if !utils.DirExists(vendorFullPath) { - err := copy.Copy(cacheFullPath, vendorFullPath) - if err != nil { - return err - } + // Move all the selected vendored dependencies to the vendor directory. + for _, depName := range kclPkg.Dependencies.Deps.Keys() { + dep, ok := kclPkg.Dependencies.Deps.Get(depName) + if !ok { + return fmt.Errorf("failed to get dependency %s", depName) } - // Load the vendored dependency - existLocalDep, err = c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) + + // Check if the dependency is already vendored in the vendor directory. + existLocalDep, err := c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) if err != nil { return err } if existLocalDep == nil { - return fmt.Errorf("failed to find the vendored dependency %s", depName) + vendorFullPath := filepath.Join(vendorPath, dep.GenDepFullName()) + cacheFullPath := filepath.Join(c.homePath, dep.GenDepFullName()) + if !utils.DirExists(vendorFullPath) { + err := copy.Copy(cacheFullPath, vendorFullPath) + if err != nil { + return err + } + } + // Load the vendored dependency + existLocalDep, err = c.dependencyExistsLocal(filepath.Dir(vendorPath), &dep, true) + if err != nil { + return err + } + + if existLocalDep == nil { + return fmt.Errorf("failed to find the vendored dependency %s", depName) + } + } + kclPkg.Dependencies.Deps.Set(depName, *existLocalDep) + } + } else { + lockDeps := make([]pkg.Dependency, 0, kclPkg.Dependencies.Deps.Len()) + for _, k := range kclPkg.Dependencies.Deps.Keys() { + d, _ := kclPkg.Dependencies.Deps.Get(k) + lockDeps = append(lockDeps, d) + } + + // Traverse all dependencies in kcl.mod.lock. + for i := 0; i < len(lockDeps); i++ { + d := lockDeps[i] + if len(d.Name) == 0 { + return errors.InvalidDependency + } + // If the dependency is from the local path, do not vendor it, vendor its dependencies. + if d.IsFromLocal() { + dpkg, err := c.LoadPkgFromPath(d.GetLocalFullPath(kclPkg.HomePath)) + if err != nil { + return err + } + err = c.vendorDeps(dpkg, vendorPath) + if err != nil { + return err + } + continue + } else { + vendorFullPath := filepath.Join(vendorPath, d.GenPathSuffix()) + + // If the package already exists in the 'vendor', do nothing. + if utils.DirExists(vendorFullPath) { + d.LocalFullPath = vendorFullPath + lockDeps[i] = d + continue + } else { + // If not in the 'vendor', check the global cache. + cacheFullPath := c.getDepStorePath(c.homePath, &d, false) + if utils.DirExists(cacheFullPath) { + // If there is, copy it into the 'vendor' directory. + err := copy.Copy(cacheFullPath, vendorFullPath) + if err != nil { + return err + } + } else { + // re-download if not. + err := c.AddDepToPkg(kclPkg, &d) + if err != nil { + return err + } + // re-vendor again with new kcl.mod and kcl.mod.lock + err = c.vendorDeps(kclPkg, vendorPath) + if err != nil { + return err + } + return nil + } + } + + if d.GetPackage() != "" { + tempVendorFullPath, err := utils.FindPackage(vendorFullPath, d.GetPackage()) + if err != nil { + return err + } + vendorFullPath = tempVendorFullPath + } + + dpkg, err := c.LoadPkgFromPath(vendorFullPath) + if err != nil { + return err + } + + // Vendor the dependencies of the current dependency. + err = c.vendorDeps(dpkg, vendorPath) + if err != nil { + return err + } + d.LocalFullPath = vendorFullPath + lockDeps[i] = d } } - kclPkg.Dependencies.Deps.Set(depName, *existLocalDep) + + // Update the dependencies in kcl.mod.lock. + for _, d := range lockDeps { + kclPkg.Dependencies.Deps.Set(d.Name, d) + } + + return nil } return nil } diff --git a/pkg/client/vendor_test.go b/pkg/client/vendor_test.go index a12abe01..3f9aed7e 100644 --- a/pkg/client/vendor_test.go +++ b/pkg/client/vendor_test.go @@ -8,6 +8,7 @@ import ( "github.com/elliotchance/orderedmap/v2" "github.com/stretchr/testify/assert" "kcl-lang.io/kpm/pkg/downloader" + "kcl-lang.io/kpm/pkg/features" pkg "kcl-lang.io/kpm/pkg/package" "kcl-lang.io/kpm/pkg/settings" "kcl-lang.io/kpm/pkg/test" @@ -91,6 +92,8 @@ func testVendorDeps(t *testing.T) { } func testVendorWithMVS(t *testing.T) { + features.Enable(features.SupportMVS) + defer features.Disable(features.SupportMVS) testDir := getTestDir("test_vendor") pkgPath := filepath.Join(testDir, "pkg") kPkg, err := pkg.LoadKclPkgWithOpts( From eb0e1552f3ecd3bf2f0f92bf2a9fc2d47d7ac453 Mon Sep 17 00:00:00 2001 From: zongzhe Date: Mon, 14 Oct 2024 14:26:45 +0800 Subject: [PATCH 4/4] fix: fix test Signed-off-by: zongzhe --- pkg/client/test_data/test_vendor/dep1/kcl.mod | 7 ------- pkg/client/test_data/test_vendor/dep1/kcl.mod.lock | 5 ----- pkg/client/test_data/test_vendor/dep1/main.k | 1 - pkg/client/test_data/test_vendor/dep2/kcl.mod | 7 ------- pkg/client/test_data/test_vendor/dep2/kcl.mod.lock | 5 ----- pkg/client/test_data/test_vendor/dep2/main.k | 1 - pkg/client/test_data/test_vendor/pkg/kcl.mod | 9 --------- pkg/client/test_data/test_vendor/pkg/kcl.mod.lock | 13 ------------- .../test_data/test_vendor/pkg/kcl.mod.lock.expect | 13 ------------- pkg/client/test_data/test_vendor/pkg/main.k | 1 - pkg/client/vendor.go | 2 +- pkg/client/vendor_test.go | 2 +- 12 files changed, 2 insertions(+), 64 deletions(-) delete mode 100644 pkg/client/test_data/test_vendor/dep1/kcl.mod delete mode 100644 pkg/client/test_data/test_vendor/dep1/kcl.mod.lock delete mode 100644 pkg/client/test_data/test_vendor/dep1/main.k delete mode 100644 pkg/client/test_data/test_vendor/dep2/kcl.mod delete mode 100644 pkg/client/test_data/test_vendor/dep2/kcl.mod.lock delete mode 100644 pkg/client/test_data/test_vendor/dep2/main.k delete mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod delete mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod.lock delete mode 100644 pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect delete mode 100644 pkg/client/test_data/test_vendor/pkg/main.k diff --git a/pkg/client/test_data/test_vendor/dep1/kcl.mod b/pkg/client/test_data/test_vendor/dep1/kcl.mod deleted file mode 100644 index 81a79664..00000000 --- a/pkg/client/test_data/test_vendor/dep1/kcl.mod +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "dep1" -edition = "v0.10.0" -version = "0.0.1" - -[dependencies] -helloworld = "0.1.1" diff --git a/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock b/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock deleted file mode 100644 index 8de6a50f..00000000 --- a/pkg/client/test_data/test_vendor/dep1/kcl.mod.lock +++ /dev/null @@ -1,5 +0,0 @@ -[dependencies] - [dependencies.helloworld] - name = "helloworld" - full_name = "helloworld_0.1.1" - version = "0.1.1" diff --git a/pkg/client/test_data/test_vendor/dep1/main.k b/pkg/client/test_data/test_vendor/dep1/main.k deleted file mode 100644 index fa7048e6..00000000 --- a/pkg/client/test_data/test_vendor/dep1/main.k +++ /dev/null @@ -1 +0,0 @@ -The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor/dep2/kcl.mod b/pkg/client/test_data/test_vendor/dep2/kcl.mod deleted file mode 100644 index 95ee77f5..00000000 --- a/pkg/client/test_data/test_vendor/dep2/kcl.mod +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "dep2" -edition = "v0.10.0" -version = "0.0.1" - -[dependencies] -helloworld = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock b/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock deleted file mode 100644 index e64da0b4..00000000 --- a/pkg/client/test_data/test_vendor/dep2/kcl.mod.lock +++ /dev/null @@ -1,5 +0,0 @@ -[dependencies] - [dependencies.helloworld] - name = "helloworld" - full_name = "helloworld_0.1.2" - version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/dep2/main.k b/pkg/client/test_data/test_vendor/dep2/main.k deleted file mode 100644 index fa7048e6..00000000 --- a/pkg/client/test_data/test_vendor/dep2/main.k +++ /dev/null @@ -1 +0,0 @@ -The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod b/pkg/client/test_data/test_vendor/pkg/kcl.mod deleted file mode 100644 index e130c21f..00000000 --- a/pkg/client/test_data/test_vendor/pkg/kcl.mod +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "pkg" -edition = "v0.10.0" -version = "0.0.1" - -[dependencies] -dep1 = { path = "../dep1" } -dep2 = { path = "../dep2" } -helloworld = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock deleted file mode 100644 index 018034cd..00000000 --- a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock +++ /dev/null @@ -1,13 +0,0 @@ -[dependencies] - [dependencies.dep1] - name = "dep1" - full_name = "dep1_0.0.1" - version = "0.0.1" - [dependencies.dep2] - name = "dep2" - full_name = "dep2_0.0.1" - version = "0.0.1" - [dependencies.helloworld] - name = "helloworld" - full_name = "helloworld_0.1.2" - version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect b/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect deleted file mode 100644 index 018034cd..00000000 --- a/pkg/client/test_data/test_vendor/pkg/kcl.mod.lock.expect +++ /dev/null @@ -1,13 +0,0 @@ -[dependencies] - [dependencies.dep1] - name = "dep1" - full_name = "dep1_0.0.1" - version = "0.0.1" - [dependencies.dep2] - name = "dep2" - full_name = "dep2_0.0.1" - version = "0.0.1" - [dependencies.helloworld] - name = "helloworld" - full_name = "helloworld_0.1.2" - version = "0.1.2" diff --git a/pkg/client/test_data/test_vendor/pkg/main.k b/pkg/client/test_data/test_vendor/pkg/main.k deleted file mode 100644 index fa7048e6..00000000 --- a/pkg/client/test_data/test_vendor/pkg/main.k +++ /dev/null @@ -1 +0,0 @@ -The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/client/vendor.go b/pkg/client/vendor.go index cda83b8c..7c026bea 100644 --- a/pkg/client/vendor.go +++ b/pkg/client/vendor.go @@ -30,7 +30,7 @@ func (c *KpmClient) VendorDeps(kclPkg *pkg.KclPkg) error { } func (c *KpmClient) vendorDeps(kclPkg *pkg.KclPkg, vendorPath string) error { - if ok, err := features.Enabled(features.SupportMVS); err != nil && ok { + if ok, err := features.Enabled(features.SupportMVS); err == nil && ok { // Select all the vendored dependencies // and fill the vendored dependencies into kclPkg.Dependencies.Deps err := c.selectVendoredDeps(kclPkg, vendorPath, kclPkg.Dependencies.Deps) diff --git a/pkg/client/vendor_test.go b/pkg/client/vendor_test.go index 3f9aed7e..db388c0b 100644 --- a/pkg/client/vendor_test.go +++ b/pkg/client/vendor_test.go @@ -94,7 +94,7 @@ func testVendorDeps(t *testing.T) { func testVendorWithMVS(t *testing.T) { features.Enable(features.SupportMVS) defer features.Disable(features.SupportMVS) - testDir := getTestDir("test_vendor") + testDir := getTestDir("test_vendor_mvs") pkgPath := filepath.Join(testDir, "pkg") kPkg, err := pkg.LoadKclPkgWithOpts( pkg.WithPath(pkgPath),