diff --git a/cmd/git-init/main.go b/cmd/git-init/main.go index afbb43ab81b..b01d0380a1b 100644 --- a/cmd/git-init/main.go +++ b/cmd/git-init/main.go @@ -39,6 +39,7 @@ func init() { flag.BoolVar(&fetchSpec.Submodules, "submodules", true, "Initialize and fetch Git submodules") flag.UintVar(&fetchSpec.Depth, "depth", 1, "Perform a shallow clone to this depth") flag.StringVar(&terminationMessagePath, "terminationMessagePath", "/tekton/termination", "Location of file containing termination message") + flag.StringVar(&fetchSpec.SparseCheckoutDirectories, "sparseCheckoutDirectories", "", "String of directory patterns separated by a comma") } func main() { diff --git a/pkg/git/git.go b/pkg/git/git.go index 4f10d2d8917..41c39eaad9f 100644 --- a/pkg/git/git.go +++ b/pkg/git/git.go @@ -59,16 +59,17 @@ func run(logger *zap.SugaredLogger, dir string, args ...string) (string, error) // FetchSpec describes how to initialize and fetch from a Git repository. type FetchSpec struct { - URL string - Revision string - Refspec string - Path string - Depth uint - Submodules bool - SSLVerify bool - HTTPProxy string - HTTPSProxy string - NOProxy string + URL string + Revision string + Refspec string + Path string + Depth uint + Submodules bool + SSLVerify bool + HTTPProxy string + HTTPSProxy string + NOProxy string + SparseCheckoutDirectories string } // Fetch fetches the specified git repository at the revision into path, using the refspec to fetch if provided. @@ -88,6 +89,9 @@ func Fetch(logger *zap.SugaredLogger, spec FetchSpec) error { } else if _, err := run(logger, "", "init"); err != nil { return err } + if err := configSparseCheckout(logger, spec); err != nil { + return err + } trimmedURL := strings.TrimSpace(spec.URL) if _, err := run(logger, "", "remote", "add", "origin", trimmedURL); err != nil { return err @@ -281,3 +285,36 @@ func ValidateGitSSHURLFormat(url string) bool { } return false } + +func configSparseCheckout(logger *zap.SugaredLogger, spec FetchSpec) error { + if spec.SparseCheckoutDirectories != "" { + if _, err := run(logger, "", "config", "core.sparsecheckout", "true"); err != nil { + return err + } + + dirPatterns := strings.Split(spec.SparseCheckoutDirectories, ",") + + cwd, err := os.Getwd() + if err != nil { + logger.Errorf("failed to get current directory: %v", err) + return err + } + file, err := os.OpenFile(filepath.Join(cwd, ".git/info/sparse-checkout"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + logger.Errorf("failed to open sparse-checkout file: %v", err) + return err + } + for _, pattern := range dirPatterns { + if _, err := file.WriteString(pattern + "\n"); err != nil { + file.Close() + logger.Errorf("failed to write to sparse-checkout file: %v", err) + return err + } + } + if err := file.Close(); err != nil { + logger.Errorf("failed to close sparse-checkout file: %v", err) + return err + } + } + return nil +} diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go index 9ef5bb372e8..a7bf9182896 100644 --- a/pkg/git/git_test.go +++ b/pkg/git/git_test.go @@ -16,11 +16,13 @@ limitations under the License. package git import ( + "bufio" "io/ioutil" "os" "strings" "testing" + "github.com/google/go-cmp/cmp" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" ) @@ -175,16 +177,34 @@ func TestFetch(t *testing.T) { logMessage: "Successfully cloned", wantErr: false, spec: FetchSpec{ - URL: "", - Revision: "", - Refspec: "", - Path: "", - Depth: 0, - Submodules: false, - SSLVerify: false, - HTTPProxy: "", - HTTPSProxy: "", - NOProxy: "", + URL: "", + Revision: "", + Refspec: "", + Path: "", + Depth: 0, + Submodules: false, + SSLVerify: false, + HTTPProxy: "", + HTTPSProxy: "", + NOProxy: "", + SparseCheckoutDirectories: "", + }, + }, { + name: "test-clone-with-sparse-checkout", + logMessage: "Successfully cloned", + wantErr: false, + spec: FetchSpec{ + URL: "", + Revision: "", + Refspec: "", + Path: "", + Depth: 0, + Submodules: false, + SSLVerify: false, + HTTPProxy: "", + HTTPSProxy: "", + NOProxy: "", + SparseCheckoutDirectories: "a,b/c", }, }, } @@ -206,6 +226,27 @@ func TestFetch(t *testing.T) { t.Errorf("Fetch() error = %v, wantErr %v", err, tt.wantErr) } + if tt.spec.SparseCheckoutDirectories != "" { + dirPatterns := strings.Split(tt.spec.SparseCheckoutDirectories, ",") + + sparseFile, err := os.Open(".git/info/sparse-checkout") + if err != nil { + t.Fatal("Unable to read sparse-checkout file") + } + defer sparseFile.Close() + + var sparsePatterns []string + + scanner := bufio.NewScanner(sparseFile) + for scanner.Scan() { + sparsePatterns = append(sparsePatterns, scanner.Text()) + } + + if cmp.Diff(dirPatterns, sparsePatterns) != "" { + t.Errorf("directory patterns and sparse-checkout patterns do not match") + } + } + if tt.logMessage != "" { takeAll := log.TakeAll() if len(takeAll) == 0 {