Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparse checkout feature - Download subdirectory #387

Closed
6 changes: 6 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ func (c *KpmClient) CompileGitPkg(gitOpts *git.CloneOptions, compileOpts *opt.Co
git.WithCommit(gitOpts.Commit),
git.WithBranch(gitOpts.Branch),
git.WithTag(gitOpts.Tag),
git.WithSubPackage(gitOpts.SubPackage),
git.WithRepoURL(gitOpts.RepoURL),
git.WithLocalPath(tmpDir),
)
Expand Down Expand Up @@ -1277,6 +1278,10 @@ func (c *KpmClient) DownloadFromGit(dep *downloader.Git, localPath string) (stri
msg = fmt.Sprintf("with commit '%s'", dep.Commit)
}

if len(dep.SubPackage) != 0 {
msg = fmt.Sprintf("with subpackage '%s'", dep.SubPackage)
}

if len(dep.Branch) != 0 {
msg = fmt.Sprintf("with branch '%s'", dep.Branch)
}
Expand All @@ -1290,6 +1295,7 @@ func (c *KpmClient) DownloadFromGit(dep *downloader.Git, localPath string) (stri
git.WithCommit(dep.Commit),
git.WithTag(dep.Tag),
git.WithRepoURL(dep.Url),
git.WithSubPackage(dep.SubPackage),
git.WithLocalPath(localPath),
git.WithWriter(c.logWriter),
)
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ func TestRunGit(t *testing.T) {
testPath := getTestDir("test_run_git")

opts := opt.DefaultCompileOptions()
gitOpts := git.NewCloneOptions("https://github.com/KusionStack/catalog", "", "0.1.2", "", filepath.Join(testPath, "catalog"), nil)
gitOpts := git.NewCloneOptions("https://github.com/KusionStack/catalog", "", "0.1.2", "", "", filepath.Join(testPath, "catalog"), nil)
defer func() {
_ = os.RemoveAll(filepath.Join(testPath, "catalog"))
}()
Expand Down Expand Up @@ -1402,7 +1402,7 @@ func TestRunGitWithLocalDep(t *testing.T) {

expectPath := filepath.Join(testPath, tc.expectFile)
opts := opt.DefaultCompileOptions()
gitOpts := git.NewCloneOptions("https://github.com/kcl-lang/flask-demo-kcl-manifests.git", tc.ref, "", "", "", nil)
gitOpts := git.NewCloneOptions("https://github.com/kcl-lang/flask-demo-kcl-manifests.git", tc.ref, "", "", "", "", nil)

result, err := kpmcli.CompileGitPkg(gitOpts, opts)
assert.Equal(t, err, nil)
Expand Down
13 changes: 10 additions & 3 deletions pkg/cmd/cmd_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ func NewAddCmd(kpmcli *client.KpmClient) *cli.Command {
Name: "rename",
Usage: "rename the package name in kcl.mod.lock",
},
&cli.StringFlag{
Name: "sub-package",
Usage: "package name inside a monorepo when using git flag",
},
},

Action: func(c *cli.Context) error {
Expand Down Expand Up @@ -198,6 +202,8 @@ func parseGitRegistryOptions(c *cli.Context) (*opt.RegistryOptions, *reporter.Kp
return nil, err
}

gitSubPackage := c.String("sub-package")

if gitUrl == "" {
return nil, reporter.NewErrorEvent(reporter.InvalidGitUrl, fmt.Errorf("the argument 'git' is required"))
}
Expand All @@ -208,9 +214,10 @@ func parseGitRegistryOptions(c *cli.Context) (*opt.RegistryOptions, *reporter.Kp

return &opt.RegistryOptions{
Git: &opt.GitOptions{
Url: gitUrl,
Tag: gitTag,
Commit: gitCommit,
Url: gitUrl,
Tag: gitTag,
Commit: gitCommit,
SubPackage: gitSubPackage,
},
}, nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func KpmRun(c *cli.Context, kpmcli *client.KpmClient) error {
// 'kpm run' compile the package from the kcl package tar.
compileResult, err = kpmcli.CompileTarPkg(runEntry.PackageSource(), kclOpts)
} else if runEntry.IsGit() {
gitOpts := git.NewCloneOptions(runEntry.PackageSource(), "", c.String(FLAG_TAG), "", "", nil)
gitOpts := git.NewCloneOptions(runEntry.PackageSource(), "", c.String(FLAG_TAG), "", "", "", nil)
// 'kpm run' compile the package from the git url
compileResult, err = kpmcli.CompileGitPkg(gitOpts, kclOpts)
} else {
Expand Down
5 changes: 5 additions & 0 deletions pkg/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ func (d *GitDownloader) Download(opts DownloadOptions) error {
msg = fmt.Sprintf("with branch '%s'", opts.Source.Git.Branch)
}

if len(opts.Source.Git.SubPackage) != 0 {
msg = fmt.Sprintf("with sub-package '%s'", opts.Source.Git.SubPackage)
}

reporter.ReportMsgTo(
fmt.Sprintf("cloning '%s' %s", opts.Source.Git.Url, msg),
opts.LogWriter,
Expand All @@ -244,6 +248,7 @@ func (d *GitDownloader) Download(opts DownloadOptions) error {
git.WithCommit(gitSource.Commit),
git.WithBranch(gitSource.Branch),
git.WithTag(gitSource.Tag),
git.WithSubPackage(gitSource.SubPackage),
git.WithRepoURL(gitSource.Url),
git.WithLocalPath(opts.LocalPath),
)
Expand Down
11 changes: 6 additions & 5 deletions pkg/downloader/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ type Oci struct {

// Git is the package source from git registry.
type Git struct {
Url string `toml:"url,omitempty"`
Branch string `toml:"branch,omitempty"`
Commit string `toml:"commit,omitempty"`
Tag string `toml:"git_tag,omitempty"`
Version string `toml:"version,omitempty"`
Url string `toml:"url,omitempty"`
Branch string `toml:"branch,omitempty"`
Commit string `toml:"commit,omitempty"`
SubPackage string `toml:"sub-package,omitempty"`
Tag string `toml:"git_tag,omitempty"`
Version string `toml:"version,omitempty"`
}

type Registry struct {
Expand Down
11 changes: 11 additions & 0 deletions pkg/downloader/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func (registry *Registry) MarshalTOML() string {
const GIT_URL_PATTERN = "git = \"%s\""
const TAG_PATTERN = "tag = \"%s\""
const GIT_COMMIT_PATTERN = "commit = \"%s\""
const GIT_SUB_PACKAGE_PATTERN = "sub-package = \"%s\""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think use package is better.

a = { git = "https://github.com/test/test.git" package = "test" } # means select the package called "test" from git repo.

const GIT_BRANCH_PATTERN = "branch = \"%s\""
const VERSION_PATTERN = "version = \"%s\""
const SEPARATOR = ", "
Expand All @@ -85,6 +86,11 @@ func (git *Git) MarshalTOML() string {
sb.WriteString(fmt.Sprintf(GIT_COMMIT_PATTERN, git.Commit))
}

if len(git.SubPackage) != 0 {
sb.WriteString(SEPARATOR)
sb.WriteString(fmt.Sprintf(GIT_SUB_PACKAGE_PATTERN, git.SubPackage))
}

if len(git.Branch) != 0 {
sb.WriteString(SEPARATOR)
sb.WriteString(fmt.Sprintf(GIT_BRANCH_PATTERN, git.Branch))
Expand Down Expand Up @@ -174,6 +180,7 @@ func (source *Source) UnmarshalModTOML(data interface{}) error {
const GIT_URL_FLAG = "git"
const TAG_FLAG = "tag"
const GIT_COMMIT_FLAG = "commit"
const GIT_SUB_PACKAGE_flag = "sub-package"
const GIT_BRANCH_FLAG = "branch"

func (git *Git) UnmarshalModTOML(data interface{}) error {
Expand All @@ -194,6 +201,10 @@ func (git *Git) UnmarshalModTOML(data interface{}) error {
git.Commit = v
}

if v, ok := meta[GIT_SUB_PACKAGE_PATTERN].(string); ok {
git.SubPackage = v
}

if v, ok := meta[GIT_BRANCH_FLAG].(string); ok {
git.Branch = v
}
Expand Down
21 changes: 9 additions & 12 deletions pkg/git/getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,36 @@ package git

import (
"fmt"

"github.com/hashicorp/go-getter"
"kcl-lang.io/kpm/pkg/constants"
)

var goGetterGetters = map[string]getter.Getter{
"git": new(getter.GitGetter),
}

var goGetterNoDetectors = []getter.Detector{}

const GIT_PROTOCOL = "git::"

func ForceProtocol(url, protocol string) string {
return protocol + url
}

// ForceGitUrl will add the branch, tag or commit to the git URL and force it to the git protocol
// `<URL>` will return `Git::<URL>?ref=<branch|tag|commit>`
// ForceGitUrl will add the subpackage and branch, tag or commit to the git URL and force it to the git protocol
// `<URL>` will return `Git::<URL>//<SubPackage>?ref=<branch|tag|commit>`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to make sure that go-getter can do subdirectory download directly in this form

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func (cloneOpts *CloneOptions) ForceGitUrl() (string, error) {
if err := cloneOpts.Validate(); err != nil {
return "", nil
}

newRepoUrl := cloneOpts.RepoURL
if cloneOpts.SubPackage != "" {
newRepoUrl += "//" + cloneOpts.SubPackage
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation idea here is still wrong, the idea we mentioned before is clone the entire repo. Recursively finds the dependency with the specified package name and loads it

Copy link
Contributor Author

@officialasishkumar officialasishkumar Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay will open a PR with that changes.

I am still not sure why we are discarding downloading a subdirectory (this method )? In case of large repo (let's say 100 MB +) with only one directory of kcl dependecies, it will defeat the purpose of sparse checkout since it will download the entire repo when the user only needs that particular subdirectory any day in the future.

cc: @zong-zhe @Peefy


var attributes = []string{cloneOpts.Branch, cloneOpts.Commit, cloneOpts.Tag}
for _, attr := range attributes {
if attr != "" {
return ForceProtocol(
cloneOpts.RepoURL+fmt.Sprintf(constants.GIT_PROTOCOL_URL_PATTERN, attr),
newRepoUrl+fmt.Sprintf(constants.GIT_PROTOCOL_URL_PATTERN, attr),
GIT_PROTOCOL,
), nil
}
}

return ForceProtocol(cloneOpts.RepoURL, GIT_PROTOCOL), nil
return ForceProtocol(newRepoUrl, GIT_PROTOCOL), nil
}
65 changes: 38 additions & 27 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,28 @@ import (

// CloneOptions is a struct for specifying options for cloning a git repository
type CloneOptions struct {
RepoURL string
Commit string
Tag string
Branch string
LocalPath string
Writer io.Writer
Bare bool // New field to indicate if the clone should be bare
RepoURL string
Commit string
Tag string
SubPackage string
Branch string
LocalPath string
Writer io.Writer
Bare bool // New field to indicate if the clone should be bare
}

// CloneOption is a function that modifies CloneOptions
type CloneOption func(*CloneOptions)

func NewCloneOptions(repoUrl, commit, tag, branch, localpath string, Writer io.Writer) *CloneOptions {
func NewCloneOptions(repoUrl, commit, tag, subpackage, branch, localpath string, Writer io.Writer) *CloneOptions {
return &CloneOptions{
RepoURL: repoUrl,
Commit: commit,
Tag: tag,
Branch: branch,
LocalPath: localpath,
Writer: Writer,
RepoURL: repoUrl,
Commit: commit,
Tag: tag,
SubPackage: subpackage,
Branch: branch,
LocalPath: localpath,
Writer: Writer,
}
}

Expand Down Expand Up @@ -69,6 +71,13 @@ func WithCommit(commit string) CloneOption {
}
}

// WithSubPackage sets the subpackage for CloneOptions
func WithSubPackage(subpackage string) CloneOption {
return func(o *CloneOptions) {
o.SubPackage = subpackage
}
}

// WithTag sets the tag for CloneOptions
func WithTag(tag string) CloneOption {
return func(o *CloneOptions) {
Expand All @@ -93,6 +102,7 @@ func WithWriter(writer io.Writer) CloneOption {
// Validate checks if the CloneOptions are valid
func (cloneOpts *CloneOptions) Validate() error {
onlyOneAllowed := 0
onlyOnePackageAllowed := 0
if cloneOpts.Branch != "" {
onlyOneAllowed++
}
Expand All @@ -102,10 +112,16 @@ func (cloneOpts *CloneOptions) Validate() error {
if cloneOpts.Commit != "" {
onlyOneAllowed++
}
if cloneOpts.SubPackage != "" {
onlyOnePackageAllowed++
}

if onlyOneAllowed > 1 {
return errors.New("only one of branch, tag or commit is allowed")
}
if onlyOnePackageAllowed > 1 {
return errors.New("only one subpackage is allowed")
}

return nil
}
Expand Down Expand Up @@ -172,22 +188,17 @@ func (cloneOpts *CloneOptions) Clone() (*git.Repository, error) {
return nil, err
}

client := &getter.Client{
Src: url,
Dst: cloneOpts.LocalPath,
Pwd: cloneOpts.LocalPath,
Mode: getter.ClientModeDir,
Detectors: goGetterNoDetectors,
Getters: goGetterGetters,
}

if err := client.Get(); err != nil {
if err := getter.GetAny(cloneOpts.LocalPath, url); err != nil {
return nil, err
}

repo, err := git.PlainOpen(cloneOpts.LocalPath)
if err != nil {
return nil, err
repo := &git.Repository{}

if cloneOpts.SubPackage == "" {
repo, err = git.PlainOpen(cloneOpts.LocalPath)
if err != nil {
return nil, err
}
}

return repo, nil
Expand Down
3 changes: 2 additions & 1 deletion pkg/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ func TestWithGitOptions(t *testing.T) {
}

func TestNewCloneOptions(t *testing.T) {
cloneOpts := NewCloneOptions("https://github.com/kcl-lang/kcl", "", "v1.0.0", "", "", nil)
cloneOpts := NewCloneOptions("https://github.com/kcl-lang/kcl", "", "v1.0.0", "", "", "", nil)
assert.Equal(t, cloneOpts.RepoURL, "https://github.com/kcl-lang/kcl")
assert.Equal(t, cloneOpts.Tag, "v1.0.0")
assert.Equal(t, cloneOpts.Commit, "")
assert.Equal(t, cloneOpts.SubPackage, "")
assert.Equal(t, cloneOpts.Branch, "")
assert.Equal(t, cloneOpts.LocalPath, "")
assert.Equal(t, cloneOpts.Writer, nil)
Expand Down
1 change: 1 addition & 0 deletions pkg/opt/opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ type GitOptions struct {
Branch string
Commit string
Tag string
SubPackage string
}

func (opts *GitOptions) Validate() error {
Expand Down
9 changes: 5 additions & 4 deletions pkg/package/modfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,10 +482,11 @@ func (deps *Dependencies) loadLockFile(filepath string) error {
func ParseOpt(opt *opt.RegistryOptions) (*Dependency, error) {
if opt.Git != nil {
gitSource := downloader.Git{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more test cases for GitDownloader to test the field you added.

Url: opt.Git.Url,
Branch: opt.Git.Branch,
Commit: opt.Git.Commit,
Tag: opt.Git.Tag,
Url: opt.Git.Url,
Branch: opt.Git.Branch,
Commit: opt.Git.Commit,
Tag: opt.Git.Tag,
SubPackage: opt.Git.SubPackage,
}

gitRef, err := gitSource.GetValidGitReference()
Expand Down
Loading