github.com/hashicorp/go-getter/v2@v2.2.2/get.go (about)

     1  // getter is a package for downloading files or directories from a variety of
     2  // protocols.
     3  //
     4  // getter is unique in its ability to download both directories and files.
     5  // It also detects certain source strings to be protocol-specific URLs. For
     6  // example, "github.com/hashicorp/go-getter/v2" would turn into a Git URL and
     7  // use the Git protocol.
     8  //
     9  // Protocols and detectors are extensible.
    10  //
    11  // To get started, see Client.
    12  package getter
    13  
    14  import (
    15  	"bytes"
    16  	"context"
    17  	"fmt"
    18  	"net/url"
    19  	"os/exec"
    20  	"regexp"
    21  	"syscall"
    22  	"time"
    23  
    24  	cleanhttp "github.com/hashicorp/go-cleanhttp"
    25  )
    26  
    27  // Getter defines the interface that schemes must implement to download
    28  // things.
    29  type Getter interface {
    30  	// Get downloads the given URL into the given directory. This always
    31  	// assumes that we're updating and gets the latest version that it can.
    32  	//
    33  	// The directory may already exist (if we're updating). If it is in a
    34  	// format that isn't understood, an error should be returned. Get shouldn't
    35  	// simply nuke the directory.
    36  	Get(context.Context, *Request) error
    37  
    38  	// GetFile downloads the give URL into the given path. The URL must
    39  	// reference a single file. If possible, the Getter should check if
    40  	// the remote end contains the same file and no-op this operation.
    41  	GetFile(context.Context, *Request) error
    42  
    43  	// Mode returns the mode based on the given URL. This is used to
    44  	// allow clients to let the getters decide which mode to use.
    45  	Mode(context.Context, *url.URL) (Mode, error)
    46  
    47  	// Detect detects whether the Request.Src matches a known pattern to
    48  	// turn it into a proper URL, and also transforms and update Request.Src
    49  	// when necessary.
    50  	// The Getter must validate if the Request.Src is a valid URL
    51  	// with a valid scheme for the Getter, and also check if the
    52  	// current Getter is the forced one and return true if that's the case.
    53  	Detect(*Request) (bool, error)
    54  }
    55  
    56  // Getters is the mapping of scheme to the Getter implementation that will
    57  // be used to get a dependency.
    58  var Getters []Getter
    59  
    60  // forcedRegexp is the regular expression that finds Forced getters. This
    61  // syntax is schema::url, example: git::https://foo.com
    62  var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`)
    63  
    64  // httpClient is the default client to be used by HttpGetters.
    65  var httpClient = cleanhttp.DefaultClient()
    66  
    67  var DefaultClient = &Client{
    68  	Getters:       Getters,
    69  	Decompressors: Decompressors,
    70  }
    71  
    72  func init() {
    73  	httpGetter := &HttpGetter{
    74  		Netrc:                 true,
    75  		XTerraformGetDisabled: true,
    76  		HeadFirstTimeout:      10 * time.Second,
    77  		ReadTimeout:           30 * time.Second,
    78  	}
    79  
    80  	// The order of the Getters in the list may affect the result
    81  	// depending if the Request.Src is detected as valid by multiple getters
    82  	Getters = []Getter{
    83  		&GitGetter{
    84  			Detectors: []Detector{
    85  				new(GitHubDetector),
    86  				new(GitDetector),
    87  				new(BitBucketDetector),
    88  				new(GitLabDetector),
    89  			},
    90  		},
    91  		new(HgGetter),
    92  		new(SmbClientGetter),
    93  		new(SmbMountGetter),
    94  		httpGetter,
    95  		new(FileGetter),
    96  	}
    97  }
    98  
    99  // Get downloads the directory specified by src into the folder specified by
   100  // dst. If dst already exists, Get will attempt to update it.
   101  //
   102  // src is a URL, whereas dst is always just a file path to a folder. This
   103  // folder doesn't need to exist. It will be created if it doesn't exist.
   104  func Get(ctx context.Context, dst, src string) (*GetResult, error) {
   105  	req := &Request{
   106  		Src:     src,
   107  		Dst:     dst,
   108  		GetMode: ModeDir,
   109  	}
   110  	return DefaultClient.Get(ctx, req)
   111  }
   112  
   113  // GetAny downloads a URL into the given destination. Unlike Get or
   114  // GetFile, both directories and files are supported.
   115  //
   116  // dst must be a directory. If src is a file, it will be downloaded
   117  // into dst with the basename of the URL. If src is a directory or
   118  // archive, it will be unpacked directly into dst.
   119  func GetAny(ctx context.Context, dst, src string) (*GetResult, error) {
   120  	req := &Request{
   121  		Src:     src,
   122  		Dst:     dst,
   123  		GetMode: ModeAny,
   124  	}
   125  	return DefaultClient.Get(ctx, req)
   126  }
   127  
   128  // GetFile downloads the file specified by src into the path specified by
   129  // dst.
   130  func GetFile(ctx context.Context, dst, src string) (*GetResult, error) {
   131  	req := &Request{
   132  		Src:     src,
   133  		Dst:     dst,
   134  		GetMode: ModeFile,
   135  	}
   136  	return DefaultClient.Get(ctx, req)
   137  }
   138  
   139  // getRunCommand is a helper that will run a command and capture the output
   140  // in the case an error happens.
   141  func getRunCommand(cmd *exec.Cmd) error {
   142  	var buf bytes.Buffer
   143  	cmd.Stdout = &buf
   144  	cmd.Stderr = &buf
   145  	err := cmd.Run()
   146  	if err == nil {
   147  		return nil
   148  	}
   149  	if exiterr, ok := err.(*exec.ExitError); ok {
   150  		// The program has exited with an exit code != 0
   151  		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
   152  			return fmt.Errorf(
   153  				"%s exited with %d: %s",
   154  				cmd.Path,
   155  				status.ExitStatus(),
   156  				buf.String())
   157  		}
   158  	}
   159  
   160  	return fmt.Errorf("error running %s: %s", cmd.Path, buf.String())
   161  }
   162  
   163  // getForcedGetter takes a source and returns the tuple of the forced
   164  // getter and the raw URL (without the force syntax).
   165  // For example "git::https://...". returns "git" "https://".
   166  func getForcedGetter(src string) (string, string) {
   167  	var forced string
   168  	if ms := forcedRegexp.FindStringSubmatch(src); ms != nil {
   169  		forced = ms[1]
   170  		src = ms[2]
   171  	}
   172  
   173  	return forced, src
   174  }