Variable expansion in repository templates (#9163)
* Start expansion Signed-off-by: jolheiser <john.olheiser@gmail.com> * _template rather than .template Signed-off-by: jolheiser <john.olheiser@gmail.com> * Use ioutil Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add descriptions to mapping * Start globbing Signed-off-by: jolheiser <john.olheiser@gmail.com> * Tune globbing Signed-off-by: jolheiser <john.olheiser@gmail.com> * Re-arrange imports Signed-off-by: jolheiser <john.olheiser@gmail.com> * Don't expand git hooks Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add glob tests for .giteatemplate Signed-off-by: jolheiser <john.olheiser@gmail.com> * Parse globs separately so they can be tested more easily Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change template location and add docs Signed-off-by: jolheiser <john.olheiser@gmail.com> * nit Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update docs/content/doc/features/gitea-directory.md Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Update docs/content/doc/features/gitea-directory.md Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Add upper-lower case match Signed-off-by: jolheiser <john.olheiser@gmail.com> * Nits Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update models/repo_generate.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									c9d50bcab5
								
							
						
					
					
						commit
						15a5c10d33
					
				
					 5 changed files with 298 additions and 51 deletions
				
			
		
							
								
								
									
										56
									
								
								docs/content/doc/features/gitea-directory.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								docs/content/doc/features/gitea-directory.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | --- | ||||||
|  | date: "2019-11-28:00:00+02:00" | ||||||
|  | title: "The .gitea Directory" | ||||||
|  | slug: "gitea-directory" | ||||||
|  | weight: 40 | ||||||
|  | toc: true | ||||||
|  | draft: false | ||||||
|  | menu: | ||||||
|  |   sidebar: | ||||||
|  |     parent: "features" | ||||||
|  |     name: "The .gitea Directory" | ||||||
|  |     weight: 50 | ||||||
|  |     identifier: "gitea-directory" | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | # The .gitea directory | ||||||
|  | Gitea repositories can include a `.gitea` directory at their base which will store settings/configurations for certain features. | ||||||
|  | 
 | ||||||
|  | ## Templates | ||||||
|  | Gitea includes template repositories, and one feature implemented with them is auto-expansion of specific variables within your template files.   | ||||||
|  | To tell Gitea which files to expand, you must include a `template` file inside the `.gitea` directory of the template repository.   | ||||||
|  | Gitea uses [gobwas/glob](https://github.com/gobwas/glob) for its glob syntax. It closely resembles a traditional `.gitignore`, however there may be slight differences. | ||||||
|  | 
 | ||||||
|  | ### Example `.gitea/template` file   | ||||||
|  | All paths are relative to the base of the repository | ||||||
|  | ```gitignore | ||||||
|  | # All .go files, anywhere in the repository | ||||||
|  | **.go | ||||||
|  | 
 | ||||||
|  | # All text files in the text directory | ||||||
|  | text/*.txt | ||||||
|  | 
 | ||||||
|  | # A specific file | ||||||
|  | a/b/c/d.json | ||||||
|  | 
 | ||||||
|  | # Batch files in both upper or lower case can be matched | ||||||
|  | **.[bB][aA][tT] | ||||||
|  | ``` | ||||||
|  | **NOTE:** The `template` file will be removed from the `.gitea` directory when a repository is generated from the template. | ||||||
|  | 
 | ||||||
|  | ### Variable Expansion | ||||||
|  | In any file matched by the above globs, certain variables will be expanded.   | ||||||
|  | All variables must be of the form `$VAR` or `${VAR}`. To escape an expansion, use a double `$$`, such as `$$VAR` or `$${VAR}` | ||||||
|  | 
 | ||||||
|  | | Variable             | Expands To                                          | | ||||||
|  | |----------------------|-----------------------------------------------------| | ||||||
|  | | REPO_NAME            | The name of the generated repository                | | ||||||
|  | | TEMPLATE_NAME        | The name of the template repository                 | | ||||||
|  | | REPO_DESCRIPTION     | The description of the generated repository         | | ||||||
|  | | TEMPLATE_DESCRIPTION | The description of the template repository          | | ||||||
|  | | REPO_LINK            | The URL to the generated repository                 | | ||||||
|  | | TEMPLATE_LINK        | The URL to the template repository                  | | ||||||
|  | | REPO_HTTPS_URL       | The HTTP(S) clone link for the generated repository | | ||||||
|  | | TEMPLATE_HTTPS_URL   | The HTTP(S) clone link for the template repository  | | ||||||
|  | | REPO_SSH_URL         | The SSH clone link for the generated repository     | | ||||||
|  | | TEMPLATE_SSH_URL     | The SSH clone link for the template repository      | | ||||||
|  | @ -1361,54 +1361,6 @@ func prepareRepoCommit(e Engine, repo *Repository, tmpDir, repoPath string, opts | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func generateRepoCommit(e Engine, repo, templateRepo *Repository, tmpDir string) error { |  | ||||||
| 	commitTimeStr := time.Now().Format(time.RFC3339) |  | ||||||
| 	authorSig := repo.Owner.NewGitSig() |  | ||||||
| 
 |  | ||||||
| 	// Because this may call hooks we should pass in the environment
 |  | ||||||
| 	env := append(os.Environ(), |  | ||||||
| 		"GIT_AUTHOR_NAME="+authorSig.Name, |  | ||||||
| 		"GIT_AUTHOR_EMAIL="+authorSig.Email, |  | ||||||
| 		"GIT_AUTHOR_DATE="+commitTimeStr, |  | ||||||
| 		"GIT_COMMITTER_NAME="+authorSig.Name, |  | ||||||
| 		"GIT_COMMITTER_EMAIL="+authorSig.Email, |  | ||||||
| 		"GIT_COMMITTER_DATE="+commitTimeStr, |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	// Clone to temporary path and do the init commit.
 |  | ||||||
| 	templateRepoPath := templateRepo.repoPath(e) |  | ||||||
| 	_, stderr, err := process.GetManager().ExecDirEnv( |  | ||||||
| 		-1, "", |  | ||||||
| 		fmt.Sprintf("generateRepoCommit(git clone): %s", templateRepoPath), |  | ||||||
| 		env, |  | ||||||
| 		git.GitExecutable, "clone", "--depth", "1", templateRepoPath, tmpDir, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("git clone: %v - %s", err, stderr) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := os.RemoveAll(path.Join(tmpDir, ".git")); err != nil { |  | ||||||
| 		return fmt.Errorf("remove git dir: %v", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := git.InitRepository(tmpDir, false); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	repoPath := repo.repoPath(e) |  | ||||||
| 	_, stderr, err = process.GetManager().ExecDirEnv( |  | ||||||
| 		-1, tmpDir, |  | ||||||
| 		fmt.Sprintf("generateRepoCommit(git remote add): %s", repoPath), |  | ||||||
| 		env, |  | ||||||
| 		git.GitExecutable, "remote", "add", "origin", repoPath, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("git remote add: %v - %s", err, stderr) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return initRepoCommit(tmpDir, repo.Owner) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func checkInitRepository(repoPath string) (err error) { | func checkInitRepository(repoPath string) (err error) { | ||||||
| 	// Somehow the directory could exist.
 | 	// Somehow the directory could exist.
 | ||||||
| 	if com.IsExist(repoPath) { | 	if com.IsExist(repoPath) { | ||||||
|  |  | ||||||
|  | @ -6,7 +6,9 @@ package models | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | @ -14,7 +16,10 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/process" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gobwas/glob" | ||||||
| 	"github.com/unknwon/com" | 	"github.com/unknwon/com" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -36,8 +41,148 @@ func (gro GenerateRepoOptions) IsValid() bool { | ||||||
| 	return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added
 | 	return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GiteaTemplate holds information about a .gitea/template file
 | ||||||
|  | type GiteaTemplate struct { | ||||||
|  | 	Path    string | ||||||
|  | 	Content []byte | ||||||
|  | 
 | ||||||
|  | 	globs []glob.Glob | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Globs parses the .gitea/template globs or returns them if they were already parsed
 | ||||||
|  | func (gt GiteaTemplate) Globs() []glob.Glob { | ||||||
|  | 	if gt.globs != nil { | ||||||
|  | 		return gt.globs | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	gt.globs = make([]glob.Glob, 0) | ||||||
|  | 	lines := strings.Split(string(util.NormalizeEOL(gt.Content)), "\n") | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		line = strings.TrimSpace(line) | ||||||
|  | 		if line == "" || strings.HasPrefix(line, "#") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		g, err := glob.Compile(line, '/') | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Info("Invalid glob expression '%s' (skipped): %v", line, err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		gt.globs = append(gt.globs, g) | ||||||
|  | 	} | ||||||
|  | 	return gt.globs | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) { | ||||||
|  | 	gtPath := filepath.Join(tmpDir, ".gitea", "template") | ||||||
|  | 	if _, err := os.Stat(gtPath); os.IsNotExist(err) { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} else if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	content, err := ioutil.ReadFile(gtPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	gt := &GiteaTemplate{ | ||||||
|  | 		Path:    gtPath, | ||||||
|  | 		Content: content, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return gt, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func generateRepoCommit(e Engine, repo, templateRepo, generateRepo *Repository, tmpDir string) error { | ||||||
|  | 	commitTimeStr := time.Now().Format(time.RFC3339) | ||||||
|  | 	authorSig := repo.Owner.NewGitSig() | ||||||
|  | 
 | ||||||
|  | 	// Because this may call hooks we should pass in the environment
 | ||||||
|  | 	env := append(os.Environ(), | ||||||
|  | 		"GIT_AUTHOR_NAME="+authorSig.Name, | ||||||
|  | 		"GIT_AUTHOR_EMAIL="+authorSig.Email, | ||||||
|  | 		"GIT_AUTHOR_DATE="+commitTimeStr, | ||||||
|  | 		"GIT_COMMITTER_NAME="+authorSig.Name, | ||||||
|  | 		"GIT_COMMITTER_EMAIL="+authorSig.Email, | ||||||
|  | 		"GIT_COMMITTER_DATE="+commitTimeStr, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	// Clone to temporary path and do the init commit.
 | ||||||
|  | 	templateRepoPath := templateRepo.repoPath(e) | ||||||
|  | 	if err := git.Clone(templateRepoPath, tmpDir, git.CloneRepoOptions{ | ||||||
|  | 		Depth: 1, | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return fmt.Errorf("git clone: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := os.RemoveAll(path.Join(tmpDir, ".git")); err != nil { | ||||||
|  | 		return fmt.Errorf("remove git dir: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Variable expansion
 | ||||||
|  | 	gt, err := checkGiteaTemplate(tmpDir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("checkGiteaTemplate: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := os.Remove(gt.Path); err != nil { | ||||||
|  | 		return fmt.Errorf("remove .giteatemplate: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Avoid walking tree if there are no globs
 | ||||||
|  | 	if len(gt.Globs()) > 0 { | ||||||
|  | 		tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/" | ||||||
|  | 		if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error { | ||||||
|  | 			if walkErr != nil { | ||||||
|  | 				return walkErr | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if info.IsDir() { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash) | ||||||
|  | 			for _, g := range gt.Globs() { | ||||||
|  | 				if g.Match(base) { | ||||||
|  | 					content, err := ioutil.ReadFile(path) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					if err := ioutil.WriteFile(path, | ||||||
|  | 						[]byte(generateExpansion(string(content), templateRepo, generateRepo)), | ||||||
|  | 						0644); err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := git.InitRepository(tmpDir, false); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	repoPath := repo.repoPath(e) | ||||||
|  | 	_, stderr, err := process.GetManager().ExecDirEnv( | ||||||
|  | 		-1, tmpDir, | ||||||
|  | 		fmt.Sprintf("generateRepoCommit(git remote add): %s", repoPath), | ||||||
|  | 		env, | ||||||
|  | 		git.GitExecutable, "remote", "add", "origin", repoPath, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("git remote add: %v - %s", err, stderr) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return initRepoCommit(tmpDir, repo.Owner) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // generateRepository initializes repository from template
 | // generateRepository initializes repository from template
 | ||||||
| func generateRepository(e Engine, repo, templateRepo *Repository) (err error) { | func generateRepository(e Engine, repo, templateRepo, generateRepo *Repository) (err error) { | ||||||
| 	tmpDir := filepath.Join(os.TempDir(), "gitea-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond())) | 	tmpDir := filepath.Join(os.TempDir(), "gitea-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond())) | ||||||
| 
 | 
 | ||||||
| 	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil { | 	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil { | ||||||
|  | @ -50,7 +195,7 @@ func generateRepository(e Engine, repo, templateRepo *Repository) (err error) { | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	if err = generateRepoCommit(e, repo, templateRepo, tmpDir); err != nil { | 	if err = generateRepoCommit(e, repo, templateRepo, generateRepo, tmpDir); err != nil { | ||||||
| 		return fmt.Errorf("generateRepoCommit: %v", err) | 		return fmt.Errorf("generateRepoCommit: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -95,7 +240,7 @@ func GenerateRepository(ctx DBContext, doer, owner *User, templateRepo *Reposito | ||||||
| 
 | 
 | ||||||
| // GenerateGitContent generates git content from a template repository
 | // GenerateGitContent generates git content from a template repository
 | ||||||
| func GenerateGitContent(ctx DBContext, templateRepo, generateRepo *Repository) error { | func GenerateGitContent(ctx DBContext, templateRepo, generateRepo *Repository) error { | ||||||
| 	if err := generateRepository(ctx.e, generateRepo, templateRepo); err != nil { | 	if err := generateRepository(ctx.e, generateRepo, templateRepo, generateRepo); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -210,3 +355,36 @@ func GenerateIssueLabels(ctx DBContext, templateRepo, generateRepo *Repository) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func generateExpansion(src string, templateRepo, generateRepo *Repository) string { | ||||||
|  | 	return os.Expand(src, func(key string) string { | ||||||
|  | 		switch key { | ||||||
|  | 		case "REPO_NAME": | ||||||
|  | 			return generateRepo.Name | ||||||
|  | 		case "TEMPLATE_NAME": | ||||||
|  | 			return templateRepo.Name | ||||||
|  | 		case "REPO_DESCRIPTION": | ||||||
|  | 			return generateRepo.Description | ||||||
|  | 		case "TEMPLATE_DESCRIPTION": | ||||||
|  | 			return templateRepo.Description | ||||||
|  | 		case "REPO_OWNER": | ||||||
|  | 			return generateRepo.MustOwnerName() | ||||||
|  | 		case "TEMPLATE_OWNER": | ||||||
|  | 			return templateRepo.MustOwnerName() | ||||||
|  | 		case "REPO_LINK": | ||||||
|  | 			return generateRepo.Link() | ||||||
|  | 		case "TEMPLATE_LINK": | ||||||
|  | 			return templateRepo.Link() | ||||||
|  | 		case "REPO_HTTPS_URL": | ||||||
|  | 			return generateRepo.CloneLink().HTTPS | ||||||
|  | 		case "TEMPLATE_HTTPS_URL": | ||||||
|  | 			return templateRepo.CloneLink().HTTPS | ||||||
|  | 		case "REPO_SSH_URL": | ||||||
|  | 			return generateRepo.CloneLink().SSH | ||||||
|  | 		case "TEMPLATE_SSH_URL": | ||||||
|  | 			return templateRepo.CloneLink().SSH | ||||||
|  | 		default: | ||||||
|  | 			return key | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										57
									
								
								models/repo_generate_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								models/repo_generate_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var giteaTemplate = []byte(` | ||||||
|  | # Header | ||||||
|  | 
 | ||||||
|  | # All .go files | ||||||
|  | **.go | ||||||
|  | 
 | ||||||
|  | # All text files in /text/ | ||||||
|  | text/*.txt | ||||||
|  | 
 | ||||||
|  | # All files in modules folders | ||||||
|  | **/modules/* | ||||||
|  | `) | ||||||
|  | 
 | ||||||
|  | func TestGiteaTemplate(t *testing.T) { | ||||||
|  | 	gt := GiteaTemplate{Content: giteaTemplate} | ||||||
|  | 	assert.Equal(t, len(gt.Globs()), 3) | ||||||
|  | 
 | ||||||
|  | 	tt := []struct { | ||||||
|  | 		Path  string | ||||||
|  | 		Match bool | ||||||
|  | 	}{ | ||||||
|  | 		{Path: "main.go", Match: true}, | ||||||
|  | 		{Path: "a/b/c/d/e.go", Match: true}, | ||||||
|  | 		{Path: "main.txt", Match: false}, | ||||||
|  | 		{Path: "a/b.txt", Match: false}, | ||||||
|  | 		{Path: "text/a.txt", Match: true}, | ||||||
|  | 		{Path: "text/b.txt", Match: true}, | ||||||
|  | 		{Path: "text/c.json", Match: false}, | ||||||
|  | 		{Path: "a/b/c/modules/README.md", Match: true}, | ||||||
|  | 		{Path: "a/b/c/modules/d/README.md", Match: false}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, tc := range tt { | ||||||
|  | 		t.Run(tc.Path, func(t *testing.T) { | ||||||
|  | 			match := false | ||||||
|  | 			for _, g := range gt.Globs() { | ||||||
|  | 				if g.Match(tc.Path) { | ||||||
|  | 					match = true | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			assert.Equal(t, tc.Match, match) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -161,6 +161,7 @@ type CloneRepoOptions struct { | ||||||
| 	Branch     string | 	Branch     string | ||||||
| 	Shared     bool | 	Shared     bool | ||||||
| 	NoCheckout bool | 	NoCheckout bool | ||||||
|  | 	Depth      int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Clone clones original repository to target path.
 | // Clone clones original repository to target path.
 | ||||||
|  | @ -193,6 +194,9 @@ func CloneWithArgs(from, to string, args []string, opts CloneRepoOptions) (err e | ||||||
| 	if opts.NoCheckout { | 	if opts.NoCheckout { | ||||||
| 		cmd.AddArguments("--no-checkout") | 		cmd.AddArguments("--no-checkout") | ||||||
| 	} | 	} | ||||||
|  | 	if opts.Depth > 0 { | ||||||
|  | 		cmd.AddArguments("--depth", strconv.Itoa(opts.Depth)) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(opts.Branch) > 0 { | 	if len(opts.Branch) > 0 { | ||||||
| 		cmd.AddArguments("-b", opts.Branch) | 		cmd.AddArguments("-b", opts.Branch) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue