github.com/kubeshop/testkube@v1.17.23/pkg/git/checkout.go (about)

     1  // TODO consider checkout by some go-git library to limit external deps in docker container
     2  // in time writing code "github.com/src-d/go-git" didn't have any filter options
     3  // "github.com/go-git/go-git/v5" also
     4  package git
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/kubeshop/testkube/pkg/executor/output"
    12  	"github.com/kubeshop/testkube/pkg/process"
    13  )
    14  
    15  // CheckoutCommit checks out specific commit
    16  func CheckoutCommit(uri, authHeader, path, commit, dir string) (err error) {
    17  	repoDir := dir + "/repo"
    18  	if err = os.Mkdir(repoDir, 0750); err != nil {
    19  		return err
    20  	}
    21  
    22  	if _, err = process.ExecuteInDir(
    23  		repoDir,
    24  		"git",
    25  		"init",
    26  	); err != nil {
    27  		return err
    28  	}
    29  
    30  	if _, err = process.ExecuteInDir(
    31  		repoDir,
    32  		"git",
    33  		"remote",
    34  		"add",
    35  		"origin",
    36  		uri,
    37  	); err != nil {
    38  		return err
    39  	}
    40  
    41  	args := []string{}
    42  	// Appends the HTTP Authorization header to the git clone args to
    43  	// authenticate using a bearer token. More info:
    44  	// https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html
    45  	if authHeader != "" {
    46  		args = append(args, "-c", fmt.Sprintf("http.extraHeader='%s'", authHeader))
    47  	}
    48  
    49  	args = append(args, "fetch", "--depth", "1", "origin", commit)
    50  	_, err = process.ExecuteInDir(
    51  		repoDir,
    52  		"git",
    53  		args...,
    54  	)
    55  	output.PrintLogf("Git parameters: %s", strings.Join(obfuscateArgs(args, uri, authHeader), " "))
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	if path != "" {
    61  		if _, err = process.ExecuteInDir(
    62  			repoDir,
    63  			"git",
    64  			"sparse-checkout",
    65  			"set",
    66  			path,
    67  		); err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	if _, err = process.ExecuteInDir(
    73  		repoDir,
    74  		"git",
    75  		"checkout",
    76  		"FETCH_HEAD",
    77  	); err != nil {
    78  		return err
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // Checkout will checkout directory from Git repository
    85  func Checkout(uri, authHeader, branch, commit, dir string) (outputDir string, err error) {
    86  	tmpDir := dir
    87  	if tmpDir == "" {
    88  		tmpDir, err = os.MkdirTemp("", "git-checkout")
    89  		if err != nil {
    90  			return "", err
    91  		}
    92  	}
    93  
    94  	if commit == "" {
    95  		args := []string{"clone"}
    96  		if branch != "" {
    97  			args = append(args, "-b", branch)
    98  		}
    99  
   100  		// Appends the HTTP Authorization header to the git clone args to
   101  		// authenticate using a bearer token. More info:
   102  		// https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html
   103  		if authHeader != "" {
   104  			args = append(args, "-c", fmt.Sprintf("http.extraHeader='%s'", authHeader))
   105  		}
   106  
   107  		args = append(args, "--depth", "1", uri, "repo")
   108  		_, err = process.ExecuteInDir(
   109  			tmpDir,
   110  			"git",
   111  			args...,
   112  		)
   113  		output.PrintLogf("Git parameters: %s", strings.Join(obfuscateArgs(args, uri, authHeader), " "))
   114  		if err != nil {
   115  			return "", err
   116  		}
   117  	} else {
   118  		if err = CheckoutCommit(uri, authHeader, "", commit, tmpDir); err != nil {
   119  			return "", err
   120  		}
   121  	}
   122  
   123  	return tmpDir + "/repo/", nil
   124  }
   125  
   126  // PartialCheckout will checkout only given directory from Git repository
   127  func PartialCheckout(uri, authHeader, path, branch, commit, dir string) (outputDir string, err error) {
   128  	tmpDir := dir
   129  	if tmpDir == "" {
   130  		tmpDir, err = os.MkdirTemp("", "git-sparse-checkout")
   131  		if err != nil {
   132  			return "", err
   133  		}
   134  	}
   135  
   136  	if commit == "" {
   137  		args := []string{"clone"}
   138  		if branch != "" {
   139  			args = append(args, "-b", branch)
   140  		}
   141  
   142  		// Appends the HTTP Authorization header to the git clone args to
   143  		// authenticate using a bearer token. More info:
   144  		// https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html
   145  		if authHeader != "" {
   146  			args = append(args, "-c", fmt.Sprintf("http.extraHeader='%s'", authHeader))
   147  		}
   148  
   149  		args = append(args, "--depth", "1", "--filter", "blob:none", "--sparse", uri, "repo")
   150  		_, err = process.ExecuteInDir(
   151  			tmpDir,
   152  			"git",
   153  			args...,
   154  		)
   155  		output.PrintLogf("Git parameters: %s", strings.Join(obfuscateArgs(args, uri, authHeader), " "))
   156  		if err != nil {
   157  			return "", err
   158  		}
   159  
   160  		_, err = process.ExecuteInDir(
   161  			tmpDir+"/repo",
   162  			"git",
   163  			"sparse-checkout",
   164  			"set",
   165  			"--no-cone",
   166  			path,
   167  		)
   168  		if err != nil {
   169  			return "", err
   170  		}
   171  	} else {
   172  		if err = CheckoutCommit(uri, authHeader, path, commit, tmpDir); err != nil {
   173  			return "", err
   174  		}
   175  	}
   176  
   177  	return tmpDir + "/repo/" + path, nil
   178  }
   179  
   180  func obfuscateArgs(args []string, uri, authHeader string) []string {
   181  	for i := range args {
   182  		for _, value := range []string{uri, authHeader} {
   183  			if value != "" {
   184  				args[i] = strings.ReplaceAll(args[i], value, strings.Repeat("*", len(value)))
   185  			}
   186  		}
   187  	}
   188  
   189  	return args
   190  }