Skip to content

Commit 0e3c652

Browse files
Merge pull request #420 from spring-financial-group/partial-clone-funcs
feat: add function for sparse & partial clone of cluster repo
2 parents b2aa853 + 7b66a76 commit 0e3c652

File tree

2 files changed

+101
-39
lines changed

2 files changed

+101
-39
lines changed

pkg/gitclient/helpers.go

+49-22
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,9 @@ func NthTag(g Interface, dir string, n int) (string, string, error) {
331331

332332
// CloneToDir clones the git repository to either the given directory or create a temporary
333333
func CloneToDir(g Interface, gitURL, dir string) (string, error) {
334-
var err error
335-
if dir != "" {
336-
err = os.MkdirAll(dir, util.DefaultWritePermissions)
337-
if err != nil {
338-
return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
339-
}
340-
} else {
341-
dir, err = os.MkdirTemp("", "jx-git-")
342-
if err != nil {
343-
return "", fmt.Errorf("failed to create temporary directory: %w", err)
344-
}
334+
dir, err := createDir(dir)
335+
if err != nil {
336+
return "", err
345337
}
346338

347339
log.Logger().Debugf("cloning %s to directory %s", termcolor.ColorInfo(gitURL), termcolor.ColorInfo(dir))
@@ -354,23 +346,40 @@ func CloneToDir(g Interface, gitURL, dir string) (string, error) {
354346
return dir, nil
355347
}
356348

349+
// PartialCloneToDir Partially clones the git repository to either the given directory or create a temporary one
350+
// Alternative to SparseCloneToDir when git provider does not support sparse-checkout
351+
// sparseCheckoutPatterns not supported
352+
// If shallow is true the clone is made with --depth=1
353+
func PartialCloneToDir(g Interface, gitURL, dir string, shallow bool) (string, error) {
354+
dir, err := createDir(dir)
355+
if err != nil {
356+
return "", err
357+
}
358+
359+
log.Logger().Debugf("initiating partial clone %s to directory %s", termcolor.ColorInfo(gitURL), termcolor.ColorInfo(dir))
360+
361+
parentDir := filepath.Dir(dir)
362+
partialCloneArgs := []string{"clone", "--filter=blob:none"}
363+
if shallow {
364+
log.Logger().Debugf("setting clone depth to 1")
365+
partialCloneArgs = append(partialCloneArgs, "--depth=1")
366+
}
367+
_, err = g.Command(parentDir, append(partialCloneArgs, gitURL, dir)...)
368+
if err != nil {
369+
return "", fmt.Errorf("failed to partially clone repository %s to directory: %s: %w", gitURL, dir, err)
370+
}
371+
return dir, nil
372+
}
373+
357374
// SparseCloneToDir clones the git repository sparsely to either the given directory or create a temporary on.
358375
// SparseCheckoutPatterns are checked out interpreted as in .gitignore. If no sparseCheckoutPatterns are given the files
359376
// directly under the root of the repository are checked out.
360377
// NOTE: This functionality is experimental and also the behaviour may vary between different git servers.
361378
// If shallow is true the clone is made with --depth=1
362379
func SparseCloneToDir(g Interface, gitURL, dir string, shallow bool, sparseCheckoutPatterns ...string) (string, error) {
363-
var err error
364-
if dir != "" {
365-
err = os.MkdirAll(dir, util.DefaultWritePermissions)
366-
if err != nil {
367-
return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
368-
}
369-
} else {
370-
dir, err = os.MkdirTemp("", "jx-git-")
371-
if err != nil {
372-
return "", fmt.Errorf("failed to create temporary directory: %w", err)
373-
}
380+
dir, err := createDir(dir)
381+
if err != nil {
382+
return "", err
374383
}
375384

376385
log.Logger().Debugf("cloning %s to directory %s sparsely", termcolor.ColorInfo(gitURL), termcolor.ColorInfo(dir))
@@ -566,6 +575,24 @@ func CheckoutRemoteBranch(g Interface, dir string, branch string) error {
566575
return Checkout(g, dir, branch)
567576
}
568577

578+
// createDir creates input directory if it does not exist, or creates a temporary directory
579+
// createDir creates the input directory if it does not exist, or creates a temporary directory
580+
func createDir(dir string) (string, error) {
581+
var err error
582+
if dir != "" {
583+
err = os.MkdirAll(dir, util.DefaultWritePermissions)
584+
if err != nil {
585+
return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
586+
}
587+
} else {
588+
dir, err = os.MkdirTemp("", "jx-git-")
589+
if err != nil {
590+
return "", fmt.Errorf("failed to create temporary directory: %w", err)
591+
}
592+
}
593+
return dir, nil
594+
}
595+
569596
// GetLatestCommitMessage returns the latest git commit message
570597
func GetLatestCommitMessage(g Interface, dir string) (string, error) {
571598
return g.Command(dir, "log", "-1", "--pretty=%B")

pkg/requirements/requirements.go

+52-17
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,9 @@ func GetRequirementsAndGit(g gitclient.Interface, gitURL string) (*jxcore.Requir
6464
func CloneClusterRepo(g gitclient.Interface, gitURL string) (string, error) {
6565
// if we have a kubernetes secret with git auth mounted to the filesystem when running in cluster
6666
// we need to turn it into a git credentials file see https://git-scm.com/docs/git-credential-store
67-
secretMountPath := os.Getenv(credentialhelper.GIT_SECRET_MOUNT_PATH)
68-
if secretMountPath != "" {
69-
err := credentialhelper.WriteGitCredentialFromSecretMount()
70-
if err != nil {
71-
return "", fmt.Errorf("failed to write git credentials file for secret %s : %w", secretMountPath, err)
72-
}
73-
74-
gitURL, err = AddUserPasswordToURLFromDir(gitURL, secretMountPath)
75-
if err != nil {
76-
return "", fmt.Errorf("failed to add username and password to git URL: %w", err)
77-
}
78-
} else {
79-
if kube.IsInCluster() {
80-
log.Logger().Warnf("no $GIT_SECRET_MOUNT_PATH environment variable set")
81-
} else {
82-
log.Logger().Debugf("no $GIT_SECRET_MOUNT_PATH environment variable set")
83-
}
67+
gitURL, err := gitCredsFromCluster(gitURL)
68+
if err != nil {
69+
return "", err
8470
}
8571

8672
// clone cluster repo to a temp dir and load the requirements
@@ -91,6 +77,33 @@ func CloneClusterRepo(g gitclient.Interface, gitURL string) (string, error) {
9177
return dir, nil
9278
}
9379

80+
// PartialCloneClusterRepo clones the cluster repo to a temporary directory and returns the directory path
81+
// Attempts a sparse clone first, falling back to a partial clone without checkout patterns, then a default clone
82+
func PartialCloneClusterRepo(g gitclient.Interface, gitURL string, shallow bool, sparseCheckoutPatterns ...string) (string, error) {
83+
gitURL, err := gitCredsFromCluster(gitURL)
84+
if err != nil {
85+
return "", err
86+
}
87+
// Attempt sparse clone first
88+
dir, err := gitclient.SparseCloneToDir(g, gitURL, "", shallow, sparseCheckoutPatterns...)
89+
if err != nil {
90+
log.Logger().Warnf("failed sparse clone of cluster git repo %s: %v", gitURL, err)
91+
log.Logger().Warnf("falling back to partial clone without checkout patterns")
92+
// If sparse clone fails, fall back to partial clone
93+
dir, err = gitclient.PartialCloneToDir(g, gitURL, "", shallow)
94+
if err != nil {
95+
log.Logger().Warnf("failed partial clone of cluster git repo %s: %v", gitURL, err)
96+
log.Logger().Warnf("falling back to default clone, without checkout patterns")
97+
dir, err = gitclient.CloneToDir(g, gitURL, "")
98+
if err != nil {
99+
return "", fmt.Errorf("failed to clone cluster git repo %s: %w", gitURL, err)
100+
}
101+
}
102+
return dir, nil
103+
}
104+
return dir, nil
105+
}
106+
94107
// AddUserPasswordToURLFromDir loads the username and password files from the given directory and adds them to the URL if they are found
95108
func AddUserPasswordToURLFromDir(gitURL, path string) (string, error) {
96109
username, err := loadFile(filepath.Join(path, "username"))
@@ -107,6 +120,28 @@ func AddUserPasswordToURLFromDir(gitURL, path string) (string, error) {
107120
return gitURL, nil
108121
}
109122

123+
func gitCredsFromCluster(gitURL string) (string, error) {
124+
secretMountPath := os.Getenv(credentialhelper.GIT_SECRET_MOUNT_PATH)
125+
if secretMountPath != "" {
126+
err := credentialhelper.WriteGitCredentialFromSecretMount()
127+
if err != nil {
128+
return "", fmt.Errorf("failed to write git credentials file for secret %s : %w", secretMountPath, err)
129+
}
130+
131+
gitURL, err = AddUserPasswordToURLFromDir(gitURL, secretMountPath)
132+
if err != nil {
133+
return "", fmt.Errorf("failed to add username and password to git URL: %w", err)
134+
}
135+
} else {
136+
if kube.IsInCluster() {
137+
log.Logger().Warnf("no $GIT_SECRET_MOUNT_PATH environment variable set")
138+
} else {
139+
log.Logger().Debugf("no $GIT_SECRET_MOUNT_PATH environment variable set")
140+
}
141+
}
142+
return gitURL, nil
143+
}
144+
110145
func loadFile(path string) (string, error) {
111146
exists, err := files.FileExists(path)
112147
if err != nil {

0 commit comments

Comments
 (0)