diff --git a/pkg/client/client.go b/pkg/client/client.go index d575c9e0..5a1efdc0 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -37,6 +37,7 @@ import ( "kcl-lang.io/kpm/pkg/runner" "kcl-lang.io/kpm/pkg/settings" "kcl-lang.io/kpm/pkg/utils" + "kcl-lang.io/kpm/pkg/visitor" ) // KpmClient is the client of kpm. @@ -1720,6 +1721,37 @@ func (c *KpmClient) FetchOciManifestIntoJsonStr(opts opt.OciFetchOptions) (strin return manifestJson, nil } +// NewVisitor is a factory function to create a new Visitor. +func NewVisitor(source downloader.Source, kpmcli *KpmClient) visitor.Visitor { + PkgVisitor := &visitor.PkgVisitor{ + Settings: kpmcli.GetSettings(), + LogWriter: kpmcli.logWriter, + } + + if source.IsRemote() { + return &visitor.RemoteVisitor{ + PkgVisitor: PkgVisitor, + Downloader: kpmcli.DepDownloader, + InsecureSkipTLSverify: kpmcli.insecureSkipTLSverify, + } + } else if source.IsLocalTarPath() || source.IsLocalTgzPath() { + return visitor.NewArchiveVisitor(PkgVisitor) + } else if source.IsLocalPath() { + rootPath, err := source.FindRootPath() + if err != nil { + return nil + } + kclmodpath := filepath.Join(rootPath, constants.KCL_MOD) + if utils.DirExists(kclmodpath) { + return PkgVisitor + } else { + return visitor.NewVirtualPkgVisitor(PkgVisitor) + } + } else { + return nil + } +} + // createDepRef will create a dependency reference for the dependency saved on the local filesystem. // On the unix-like system, it will create a symbolic link. // On the windows system, it will create a junction. diff --git a/pkg/client/resolver.go b/pkg/client/resolver.go index 87d1fb19..16304f07 100644 --- a/pkg/client/resolver.go +++ b/pkg/client/resolver.go @@ -5,6 +5,7 @@ import ( "kcl-lang.io/kpm/pkg/downloader" pkg "kcl-lang.io/kpm/pkg/package" + "kcl-lang.io/kpm/pkg/visitor" ) // ResolveOption is the option for resolving dependencies. @@ -95,16 +96,24 @@ func (dr *DepsResolver) Resolve(options ...ResolveOption) 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, error) { + visitorSelectorFunc := func(source *downloader.Source) (visitor.Visitor, error) { if source.IsRemote() { - PkgVisitor := NewRemoteVisitor(NewPkgVisitor(dr.kpmClient)) - PkgVisitor.EnableCache = opts.EnableCache - if opts.CachePath == "" { - PkgVisitor.CachePath = dr.kpmClient.homePath + var cachePath string + if opts.CachePath != "" { + cachePath = opts.CachePath } else { - PkgVisitor.CachePath = opts.CachePath + cachePath = dr.kpmClient.homePath } - return PkgVisitor, nil + return &visitor.RemoteVisitor{ + PkgVisitor: &visitor.PkgVisitor{ + Settings: &dr.kpmClient.settings, + LogWriter: dr.kpmClient.logWriter, + }, + Downloader: dr.kpmClient.DepDownloader, + InsecureSkipTLSverify: dr.kpmClient.insecureSkipTLSverify, + EnableCache: opts.EnableCache, + CachePath: cachePath, + }, nil } else { return NewVisitor(*source, dr.kpmClient), nil } diff --git a/pkg/visitor/test_data/test_visit_dir/kcl.mod b/pkg/visitor/test_data/test_visit_dir/kcl.mod new file mode 100644 index 00000000..9b2415e7 --- /dev/null +++ b/pkg/visitor/test_data/test_visit_dir/kcl.mod @@ -0,0 +1,4 @@ +[package] +name = "test_visit_dir" +edition = "v0.10.0" +version = "0.0.1" diff --git a/pkg/visitor/test_data/test_visit_dir/kcl.mod.lock b/pkg/visitor/test_data/test_visit_dir/kcl.mod.lock new file mode 100644 index 00000000..e69de29b diff --git a/pkg/visitor/test_data/test_visit_dir/main.k b/pkg/visitor/test_data/test_visit_dir/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/visitor/test_data/test_visit_dir/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/visitor/test_data/test_visit_tar/kcl.mod b/pkg/visitor/test_data/test_visit_tar/kcl.mod new file mode 100644 index 00000000..7bdbfc0d --- /dev/null +++ b/pkg/visitor/test_data/test_visit_tar/kcl.mod @@ -0,0 +1,4 @@ +[package] +name = "test_visit_tar" +edition = "v0.10.0" +version = "0.0.1" diff --git a/pkg/visitor/test_data/test_visit_tar/kcl.mod.lock b/pkg/visitor/test_data/test_visit_tar/kcl.mod.lock new file mode 100644 index 00000000..e69de29b diff --git a/pkg/visitor/test_data/test_visit_tar/main.k b/pkg/visitor/test_data/test_visit_tar/main.k new file mode 100644 index 00000000..fa7048e6 --- /dev/null +++ b/pkg/visitor/test_data/test_visit_tar/main.k @@ -0,0 +1 @@ +The_first_kcl_program = 'Hello World!' \ No newline at end of file diff --git a/pkg/visitor/test_data/test_visit_tar/test_visit_tar_0.0.1.tar b/pkg/visitor/test_data/test_visit_tar/test_visit_tar_0.0.1.tar new file mode 100644 index 00000000..6facf8a0 Binary files /dev/null and b/pkg/visitor/test_data/test_visit_tar/test_visit_tar_0.0.1.tar differ diff --git a/pkg/client/visitor.go b/pkg/visitor/visitor.go similarity index 77% rename from pkg/client/visitor.go rename to pkg/visitor/visitor.go index 0d151ded..c05729e5 100644 --- a/pkg/client/visitor.go +++ b/pkg/visitor/visitor.go @@ -1,7 +1,8 @@ -package client +package visitor import ( "fmt" + "io" "os" "path/filepath" @@ -10,6 +11,7 @@ import ( "kcl-lang.io/kpm/pkg/downloader" "kcl-lang.io/kpm/pkg/opt" pkg "kcl-lang.io/kpm/pkg/package" + "kcl-lang.io/kpm/pkg/settings" "kcl-lang.io/kpm/pkg/utils" ) @@ -22,14 +24,8 @@ type Visitor interface { // PkgVisitor is the visitor for visiting a local package. type PkgVisitor struct { - kpmcli *KpmClient -} - -// NewPkgVisitor creates a new PkgVisitor. -func NewPkgVisitor(kpmcli *KpmClient) *PkgVisitor { - return &PkgVisitor{ - kpmcli: kpmcli, - } + Settings *settings.Settings + LogWriter io.Writer } // Visit visits a local package. @@ -39,12 +35,15 @@ func (pv *PkgVisitor) Visit(s *downloader.Source, v visitFunc) error { } // Find the root path of the source. // There must be a kcl.mod file in the root path. - rootPath, err := s.FindRootPath() + modPath, err := s.FindRootPath() if err != nil { return err } - kclPkg, err := pv.kpmcli.LoadPkgFromPath(rootPath) + kclPkg, err := pkg.LoadKclPkgWithOpts( + pkg.WithPath(modPath), + pkg.WithSettings(pv.Settings), + ) if err != nil { return err } @@ -90,8 +89,10 @@ func (vpv *VirtualPkgVisitor) Visit(s *downloader.Source, v visitFunc) error { // RemoteVisitor is the visitor for visiting a remote package. type RemoteVisitor struct { *PkgVisitor - EnableCache bool - CachePath string + EnableCache bool + CachePath string + Downloader downloader.Downloader + InsecureSkipTLSverify bool } // NewRemoteVisitor creates a new RemoteVisitor. @@ -118,21 +119,21 @@ func (rv *RemoteVisitor) Visit(s *downloader.Source, v visitFunc) error { tmpDir = filepath.Join(tmpDir, constants.GitScheme) } - credCli, err := rv.kpmcli.GetCredsClient() + credCli, err := downloader.LoadCredentialFile(rv.Settings.CredentialsFile) if err != nil { return err } defer os.RemoveAll(tmpDir) - err = rv.kpmcli.DepDownloader.Download(*downloader.NewDownloadOptions( + err = rv.Downloader.Download(*downloader.NewDownloadOptions( downloader.WithLocalPath(tmpDir), downloader.WithSource(*s), - downloader.WithLogWriter(rv.kpmcli.GetLogWriter()), - downloader.WithSettings(*rv.kpmcli.GetSettings()), + downloader.WithLogWriter(rv.LogWriter), + downloader.WithSettings(*rv.Settings), downloader.WithCredsClient(credCli), downloader.WithCachePath(rv.CachePath), downloader.WithEnableCache(rv.EnableCache), - downloader.WithInsecureSkipTLSverify(rv.kpmcli.insecureSkipTLSverify), + downloader.WithInsecureSkipTLSverify(rv.InsecureSkipTLSverify), )) if err != nil { @@ -146,7 +147,10 @@ func (rv *RemoteVisitor) Visit(s *downloader.Source, v visitFunc) error { } } - kclPkg, err := rv.kpmcli.LoadPkgFromPath(pkgPath) + kclPkg, err := pkg.LoadKclPkgWithOpts( + pkg.WithPath(pkgPath), + pkg.WithSettings(rv.Settings), + ) if err != nil { return err } @@ -212,7 +216,10 @@ func (av *ArchiveVisitor) Visit(s *downloader.Source, v visitFunc) error { return err } - kclPkg, err := av.kpmcli.LoadPkgFromPath(tmpDir) + kclPkg, err := pkg.LoadKclPkgWithOpts( + pkg.WithPath(tmpDir), + pkg.WithSettings(av.Settings), + ) if err != nil { return err @@ -220,25 +227,3 @@ func (av *ArchiveVisitor) Visit(s *downloader.Source, v visitFunc) error { return v(kclPkg) } - -// NewVisitor is a factory function to create a new Visitor. -func NewVisitor(source downloader.Source, kpmcli *KpmClient) Visitor { - if source.IsRemote() { - return NewRemoteVisitor(NewPkgVisitor(kpmcli)) - } else if source.IsLocalTarPath() || source.IsLocalTgzPath() { - return NewArchiveVisitor(NewPkgVisitor(kpmcli)) - } else if source.IsLocalPath() { - rootPath, err := source.FindRootPath() - if err != nil { - return nil - } - kclmodpath := filepath.Join(rootPath, constants.KCL_MOD) - if utils.DirExists(kclmodpath) { - return NewPkgVisitor(kpmcli) - } else { - return NewVirtualPkgVisitor(NewPkgVisitor(kpmcli)) - } - } else { - return nil - } -} diff --git a/pkg/visitor/visitor_test.go b/pkg/visitor/visitor_test.go new file mode 100644 index 00000000..e163c3ce --- /dev/null +++ b/pkg/visitor/visitor_test.go @@ -0,0 +1,102 @@ +package visitor + +import ( + "bytes" + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" + "kcl-lang.io/kpm/pkg/downloader" + pkg "kcl-lang.io/kpm/pkg/package" + "kcl-lang.io/kpm/pkg/settings" +) + +const testDataDir = "test_data" + +func getTestDir(subDir string) string { + pwd, _ := os.Getwd() + testDir := filepath.Join(pwd, testDataDir) + testDir = filepath.Join(testDir, subDir) + + return testDir +} + +func TestVisitPkgDir(t *testing.T) { + pkgDir := getTestDir("test_visit_dir") + pVisitor := PkgVisitor{} + source, err := downloader.NewSourceFromStr(pkgDir) + if err != nil { + t.Fatal(err) + } + + err = pVisitor.Visit(source, func(pkg *pkg.KclPkg) error { + assert.Equal(t, pkg.GetPkgName(), "test_visit_dir") + assert.Equal(t, pkg.GetPkgVersion(), "0.0.1") + return nil + }) + assert.NilError(t, err) +} + +func TestVisitPkgTar(t *testing.T) { + pkgTar := filepath.Join(getTestDir("test_visit_tar"), "test_visit_tar-0.0.1.tar") + pVisitor := PkgVisitor{} + source, err := downloader.NewSourceFromStr(pkgTar) + if err != nil { + t.Fatal(err) + } + + err = pVisitor.Visit(source, func(pkg *pkg.KclPkg) error { + assert.Equal(t, pkg.GetPkgName(), "test_visit_tar") + assert.Equal(t, pkg.GetPkgVersion(), "0.0.1") + return nil + }) + assert.NilError(t, err) +} + +func TestVisitPkgRemote(t *testing.T) { + var buf bytes.Buffer + remotePkgVisitor := RemoteVisitor{ + PkgVisitor: &PkgVisitor{ + LogWriter: &buf, + Settings: settings.GetSettings(), + }, + Downloader: &downloader.DepDownloader{}, + } + + tests := []struct { + sourceStr string + expectedPkgName string + expectedPkgVer string + expectedLog string + }{ + { + sourceStr: "oci://ghcr.io/kcl-lang/helloworld?tag=0.1.2", + expectedPkgName: "helloworld", + expectedPkgVer: "0.1.2", + expectedLog: "downloading 'kcl-lang/helloworld:0.1.2' from 'ghcr.io/kcl-lang/helloworld:0.1.2'\n", + }, + { + sourceStr: "git://github.com/kcl-lang/flask-demo-kcl-manifests.git?branch=main", + expectedPkgName: "flask_manifests", + expectedPkgVer: "0.0.1", + expectedLog: "cloning 'https://github.com/kcl-lang/flask-demo-kcl-manifests.git' with branch 'main'\n", + }, + } + + for _, tt := range tests { + buf.Reset() + source, err := downloader.NewSourceFromStr(tt.sourceStr) + if err != nil { + t.Fatal(err) + } + + err = remotePkgVisitor.Visit(source, func(pkg *pkg.KclPkg) error { + assert.Equal(t, pkg.GetPkgName(), tt.expectedPkgName) + assert.Equal(t, pkg.GetPkgVersion(), tt.expectedPkgVer) + return nil + }) + assert.Equal(t, buf.String(), tt.expectedLog) + assert.NilError(t, err) + } +}