From c9a5b0a6b24130d607c1b00f82fd1d0f5cecd842 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Wed, 24 Jul 2019 22:16:00 -0400 Subject: [PATCH 01/16] Pass -n flag to git clone There's an explicit git checkout command issued moments later, so there's no need to create a working copy during the clone. --- pkg/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index d2cd6f3..60fc997 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -36,7 +36,7 @@ func NewGitPackage(source *spec.GitSource) Interface { } func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVersion string, err error) { - cmd := exec.CommandContext(ctx, "git", "clone", p.Source.Remote, dir) + cmd := exec.CommandContext(ctx, "git", "clone", "-n", p.Source.Remote, dir) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr From 30d79295666969002248e2563f494f904570cb45 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Wed, 24 Jul 2019 22:17:11 -0400 Subject: [PATCH 02/16] Use git sparse checkout whenever possible If a SubDir is configured for the package, everything but that directory will be thrown away after the package is installed. --- pkg/git.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/git.go b/pkg/git.go index 60fc997..d199ff9 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -17,6 +17,7 @@ package pkg import ( "bytes" "context" + "io/ioutil" "os" "os/exec" "path" @@ -45,6 +46,20 @@ func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVers return "", err } + if p.Source.Subdir != "" { + cmd = exec.CommandContext(ctx, "git", "config", "core.sparsecheckout", "true") + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Dir = dir + err = cmd.Run() + if err != nil { + return "", err + } + glob := []byte(p.Source.Subdir + "/*\n") + ioutil.WriteFile(dir + "/.git/info/sparse-checkout", glob, 0644) + } + cmd = exec.CommandContext(ctx, "git", "-c", "advice.detachedHead=false", "checkout", version) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout From 671f860a1912ba45fa489354f84ec82a6f11ae96 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Wed, 24 Jul 2019 22:52:06 -0400 Subject: [PATCH 03/16] Shallow fetch for Git packages If the server supports it, fetch a specific revision with --depth 1. Otherwise, fall back to the normal fetch. This replaces the previous "clone" operation. The bandwidth and time savings can be significant depending on the history of the repository (number of commits). --- pkg/git.go | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/git.go b/pkg/git.go index d199ff9..2f53860 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -37,15 +37,47 @@ func NewGitPackage(source *spec.GitSource) Interface { } func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVersion string, err error) { - cmd := exec.CommandContext(ctx, "git", "clone", "-n", p.Source.Remote, dir) + cmd := exec.CommandContext(ctx, "git", "init") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + cmd.Dir = dir err = cmd.Run() if err != nil { return "", err } + cmd = exec.CommandContext(ctx, "git", "remote", "add", "origin", p.Source.Remote) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Dir = dir + err = cmd.Run() + if err != nil { + return "", err + } + + // Attempt shallow fetch at specific revision + cmd = exec.CommandContext(ctx, "git", "fetch", "--depth", "1", "origin", version) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Dir = dir + err = cmd.Run() + if err != nil { + // Fall back to normal fetch (all revisions) + cmd = exec.CommandContext(ctx, "git", "fetch", "origin") + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Dir = dir + err = cmd.Run() + if err != nil { + return "", err + } + } + + // If a Subdir is specificied, a sparsecheckout is sufficient if p.Source.Subdir != "" { cmd = exec.CommandContext(ctx, "git", "config", "core.sparsecheckout", "true") cmd.Stdin = os.Stdin @@ -55,9 +87,9 @@ func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVers err = cmd.Run() if err != nil { return "", err - } + } glob := []byte(p.Source.Subdir + "/*\n") - ioutil.WriteFile(dir + "/.git/info/sparse-checkout", glob, 0644) + ioutil.WriteFile(dir+"/.git/info/sparse-checkout", glob, 0644) } cmd = exec.CommandContext(ctx, "git", "-c", "advice.detachedHead=false", "checkout", version) From ba93fb74c3b59f4b823d3b0bd35f828803e19e65 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Fri, 26 Jul 2019 08:05:50 -0400 Subject: [PATCH 04/16] Download tarball archives from GitHub (wip) --- pkg/git.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pkg/git.go b/pkg/git.go index 2f53860..399cad0 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -17,13 +17,17 @@ package pkg import ( "bytes" "context" + "fmt" + "io" "io/ioutil" + "net/http" "os" "os/exec" "path" "strings" "github.com/jsonnet-bundler/jsonnet-bundler/spec" + "github.com/fatih/color" ) type GitPackage struct { @@ -36,7 +40,49 @@ func NewGitPackage(source *spec.GitSource) Interface { } } +func DownloadFile(filepath string, url string) error { + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + return err +} + func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVersion string, err error) { + if strings.HasPrefix(p.Source.Remote, "https://github.com/") { + archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) + archiveFilepath := fmt.Sprintf("%s.tar.gz", dir) + err := DownloadFile(archiveFilepath, archiveUrl); + if err != nil { + return "", err; + } + color.Cyan("GET %s OK", archiveUrl); + cmd := exec.CommandContext(ctx, "tar", "xvf", archiveFilepath) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + return "", err + } + color.Cyan("Untar %s OK", archiveFilepath); + // TODO resolve git refs using GitHub API + commitHash := version + return commitHash, nil + } + cmd := exec.CommandContext(ctx, "git", "init") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout From 134b21a473c9f3f493db478edf0dd09cb70d69b0 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Tue, 30 Jul 2019 21:49:32 -0400 Subject: [PATCH 05/16] use Go-native untar and gunzip support to unpack github archives --- pkg/git.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/pkg/git.go b/pkg/git.go index 399cad0..e569628 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -15,7 +15,9 @@ package pkg import ( + "archive/tar" "bytes" + "compress/gzip" "context" "fmt" "io" @@ -24,10 +26,11 @@ import ( "os" "os/exec" "path" + "path/filepath" "strings" - "github.com/jsonnet-bundler/jsonnet-bundler/spec" "github.com/fatih/color" + "github.com/jsonnet-bundler/jsonnet-bundler/spec" ) type GitPackage struct { @@ -60,24 +63,86 @@ func DownloadFile(filepath string, url string) error { return err } +func gzipUntar(dst string, r io.Reader, subDir string) error { + gzr, err := gzip.NewReader(r) + if err != nil { + return err + } + defer gzr.Close() + + tr := tar.NewReader(gzr) + + for { + header, err := tr.Next() + switch { + case err == io.EOF: + return nil + + case err != nil: + return err + + case header == nil: + continue + } + + // strip the two first components of the path + parts := strings.SplitAfterN(header.Name, "/", 2) + if len(parts) < 2 { + continue + } + suffix := parts[1] + prefix := dst + + // reconstruct the target parh for the archive entry + target := filepath.Join(prefix, suffix) + + // if subdir is provided and target is not under it, skip it + subDirPath := filepath.Join(prefix, subDir) + if subDir != "" && !strings.HasPrefix(target, subDirPath) { + continue + } + + // check the file type + switch header.Typeflag { + + // create directories as needed + case tar.TypeDir: + if _, err := os.Stat(target); err != nil { + if err := os.MkdirAll(target, 0755); err != nil { + return err + } + } + + case tar.TypeReg: + f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + if err != nil { + return err + } + + // copy over contents + if _, err := io.Copy(f, tr); err != nil { + return err + } + f.Close() + } + } +} + func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVersion string, err error) { + // Optimization for GitHub sources: download a tarball archive of the requested + // version instead of cloning the entire repository. Resolves the version to a + // commit SHA using the GitHub API. if strings.HasPrefix(p.Source.Remote, "https://github.com/") { archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) archiveFilepath := fmt.Sprintf("%s.tar.gz", dir) - err := DownloadFile(archiveFilepath, archiveUrl); - if err != nil { - return "", err; - } - color.Cyan("GET %s OK", archiveUrl); - cmd := exec.CommandContext(ctx, "tar", "xvf", archiveFilepath) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() + err := DownloadFile(archiveFilepath, archiveUrl) if err != nil { return "", err } - color.Cyan("Untar %s OK", archiveFilepath); + color.Cyan("GET %s OK", archiveUrl) + r, err := os.Open(archiveFilepath) + err = gzipUntar(dir, r, p.Source.Subdir) + // TODO resolve git refs using GitHub API commitHash := version return commitHash, nil @@ -123,7 +188,8 @@ func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVers } } - // If a Subdir is specificied, a sparsecheckout is sufficient + // Sparse checkout optimization: if a Subdir is specificied, + // there is no need to do a full checkout if p.Source.Subdir != "" { cmd = exec.CommandContext(ctx, "git", "config", "core.sparsecheckout", "true") cmd.Stdin = os.Stdin From 5e742d53398d4da03c4880f4f14ca2c8fca2ae0c Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Tue, 30 Jul 2019 22:50:39 -0400 Subject: [PATCH 06/16] Resolve commit SHA1 from GitHub archive ETag header --- pkg/git.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/pkg/git.go b/pkg/git.go index e569628..798bacc 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -19,6 +19,7 @@ import ( "bytes" "compress/gzip" "context" + "errors" "fmt" "io" "io/ioutil" @@ -27,6 +28,7 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "strings" "github.com/fatih/color" @@ -43,24 +45,40 @@ func NewGitPackage(source *spec.GitSource) Interface { } } -func DownloadFile(filepath string, url string) error { +func downloadGitHubArchive(filepath string, url string) (string, error) { // Get the data resp, err := http.Get(url) if err != nil { - return err + return "", err } + color.Cyan("GET %s %d", url, resp.StatusCode) + + // GitHub conveniently uses the commit SHA1 at the ETag + // signature for the archive. This is needed when doing `jb update` + // to resolve a ref (ie. "master") to a commit SHA1 for the lock file + etagValue := resp.Header.Get(http.CanonicalHeaderKey("ETag")) + commitShaPattern, _ := regexp.Compile("^\"([0-9a-f]{40})\"$") + m := commitShaPattern.FindStringSubmatch(etagValue) + if len(m) < 2 { + return "", errors.New(fmt.Sprintf("unexpected etag format: %s", etagValue)) + } + commitSha := m[1] defer resp.Body.Close() // Create the file out, err := os.Create(filepath) if err != nil { - return err + return "", err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) - return err + if err != nil { + return "", err + } + + return commitSha, nil } func gzipUntar(dst string, r io.Reader, subDir string) error { @@ -135,17 +153,15 @@ func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVers if strings.HasPrefix(p.Source.Remote, "https://github.com/") { archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) archiveFilepath := fmt.Sprintf("%s.tar.gz", dir) - err := DownloadFile(archiveFilepath, archiveUrl) + + defer os.Remove(archiveFilepath) + commitSha, err := downloadGitHubArchive(archiveFilepath, archiveUrl) if err != nil { return "", err } - color.Cyan("GET %s OK", archiveUrl) r, err := os.Open(archiveFilepath) err = gzipUntar(dir, r, p.Source.Subdir) - - // TODO resolve git refs using GitHub API - commitHash := version - return commitHash, nil + return commitSha, nil } cmd := exec.CommandContext(ctx, "git", "init") From 1fe94dcf91266f130cfa60ff727f8f23c0fcd8f8 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 10:26:47 -0400 Subject: [PATCH 07/16] fix "errors" pkg double import --- pkg/git.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index ca66df5..2a2f6fe 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -19,7 +19,6 @@ import ( "bytes" "compress/gzip" "context" - "errors" "fmt" "io" "io/ioutil" From b597b161733f4b62e200af40a342ef6c1b80fdd5 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 10:32:26 -0400 Subject: [PATCH 08/16] use filepath.Join instead of string concatenation --- pkg/git.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/git.go b/pkg/git.go index 2a2f6fe..d337824 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -212,7 +212,7 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st } } - // Sparse checkout optimization: if a Subdir is specificied, + // Sparse checkout optimization: if a Subdir is specified, // there is no need to do a full checkout if p.Source.Subdir != "" { cmd = exec.CommandContext(ctx, "git", "config", "core.sparsecheckout", "true") @@ -224,8 +224,12 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st if err != nil { return "", err } + glob := []byte(p.Source.Subdir + "/*\n") - ioutil.WriteFile(tmpDir+"/.git/info/sparse-checkout", glob, 0644) + err = ioutil.WriteFile(filepath.Join(tmpDir, ".git", "info", "sparse-checkout"), glob, 0644) + if err != nil { + return "", err + } } cmd = exec.CommandContext(ctx, "git", "-c", "advice.detachedHead=false", "checkout", version) From 93c18a7d4dd4eb8ee073382911db78f1a2b28860 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 10:56:47 -0400 Subject: [PATCH 09/16] motivate the explicit f.Close() --- pkg/git.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/git.go b/pkg/git.go index d337824..b8d39c7 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -141,6 +141,10 @@ func gzipUntar(dst string, r io.Reader, subDir string) error { if _, err := io.Copy(f, tr); err != nil { return err } + + // Explicitly release the file handle inside the inner loop + // Using defer would accumulate an unbounded quantity of + // handles and release them all at once at function end. f.Close() } } From 5b12e9c7987ea9728b9d8baf656e703d611d2948 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 10:57:02 -0400 Subject: [PATCH 10/16] additional error checking --- pkg/git.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/git.go b/pkg/git.go index b8d39c7..50d518a 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -171,8 +171,17 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st if err != nil { return "", err } + r, err := os.Open(archiveFilepath) + if err != nil { + return "", err + } + err = gzipUntar(tmpDir, r, p.Source.Subdir) + if err != nil { + return "", err + } + return commitSha, nil } From 76d85b1f49a5f890698019163c664e4f180645a8 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 11:04:49 -0400 Subject: [PATCH 11/16] honor header.Mode for directories found in tarballs --- pkg/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index 50d518a..e0ec7f4 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -126,7 +126,7 @@ func gzipUntar(dst string, r io.Reader, subDir string) error { // create directories as needed case tar.TypeDir: if _, err := os.Stat(target); err != nil { - if err := os.MkdirAll(target, 0755); err != nil { + if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil { return err } } From 524c820a940be962ab3a787b4b1987daf6e8a292 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 11:23:15 -0400 Subject: [PATCH 12/16] use regex instead of prefix match to detect github remotes this will allow the optimization to work for both ssh and https --- pkg/git.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index e0ec7f4..e7ebfb5 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -162,7 +162,8 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st // Optimization for GitHub sources: download a tarball archive of the requested // version instead of cloning the entire repository. Resolves the version to a // commit SHA using the GitHub API. - if strings.HasPrefix(p.Source.Remote, "https://github.com/") { + isGitHubRemote, err := regexp.MatchString(`^(https|ssh)://github\.com/.+$`, p.Source.Remote) + if isGitHubRemote { archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) archiveFilepath := fmt.Sprintf("%s.tar.gz", tmpDir) From 2b485512c0985a79639737e08ae4095e8d89486f Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 11:40:09 -0400 Subject: [PATCH 13/16] improve github archive download error reporting --- pkg/git.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index e7ebfb5..5a9e65e 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -52,15 +52,22 @@ func downloadGitHubArchive(filepath string, url string) (string, error) { return "", err } color.Cyan("GET %s %d", url, resp.StatusCode) + if resp.StatusCode != 200 { + return "", errors.New(fmt.Sprintf("unexpected status code %d", resp.StatusCode)) + } // GitHub conveniently uses the commit SHA1 at the ETag // signature for the archive. This is needed when doing `jb update` // to resolve a ref (ie. "master") to a commit SHA1 for the lock file etagValue := resp.Header.Get(http.CanonicalHeaderKey("ETag")) + if etagValue == "" { + return "", errors.New("ETag header is missing from response") + } + commitShaPattern, _ := regexp.Compile("^\"([0-9a-f]{40})\"$") m := commitShaPattern.FindStringSubmatch(etagValue) if len(m) < 2 { - return "", errors.New(fmt.Sprintf("unexpected etag format: %s", etagValue)) + return "", errors.New(fmt.Sprintf("etag value \"%s\" does not look like a SHA1", etagValue)) } commitSha := m[1] defer resp.Body.Close() From 741e7f316fb3e82f1b700322807973b5d3a419cb Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 11:41:08 -0400 Subject: [PATCH 14/16] fall back to git if github archive download fails for any reason --- pkg/git.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/git.go b/pkg/git.go index 5a9e65e..e02f53c 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -167,30 +167,31 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st defer os.RemoveAll(tmpDir) // Optimization for GitHub sources: download a tarball archive of the requested - // version instead of cloning the entire repository. Resolves the version to a - // commit SHA using the GitHub API. + // version instead of cloning the entire repository. The SHA1 is discovered through + // the ETag header included in the response. isGitHubRemote, err := regexp.MatchString(`^(https|ssh)://github\.com/.+$`, p.Source.Remote) if isGitHubRemote { - archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) + archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gzz", p.Source.Remote, version) archiveFilepath := fmt.Sprintf("%s.tar.gz", tmpDir) defer os.Remove(archiveFilepath) commitSha, err := downloadGitHubArchive(archiveFilepath, archiveUrl) - if err != nil { - return "", err + if err == nil { + r, err := os.Open(archiveFilepath) + defer r.Close() + if err == nil { + err = gzipUntar(tmpDir, r, p.Source.Subdir) + } } - r, err := os.Open(archiveFilepath) - if err != nil { - return "", err + if err == nil { + return commitSha, nil } - err = gzipUntar(tmpDir, r, p.Source.Subdir) - if err != nil { - return "", err - } - - return commitSha, nil + // The repository may be private or the archive download may not work + // for other reasons. In any case, fall back to the slower git-based installation. + color.Yellow("archive install failed: %s", err) + color.Yellow("retrying with git...") } cmd := exec.CommandContext(ctx, "git", "init") From 41278d8ef436a00026ee62635a1fe531b2fa7747 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sat, 28 Sep 2019 11:43:10 -0400 Subject: [PATCH 15/16] undo typo in tarball url format --- pkg/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/git.go b/pkg/git.go index e02f53c..fb6f153 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -171,7 +171,7 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st // the ETag header included in the response. isGitHubRemote, err := regexp.MatchString(`^(https|ssh)://github\.com/.+$`, p.Source.Remote) if isGitHubRemote { - archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gzz", p.Source.Remote, version) + archiveUrl := fmt.Sprintf("%s/archive/%s.tar.gz", p.Source.Remote, version) archiveFilepath := fmt.Sprintf("%s.tar.gz", tmpDir) defer os.Remove(archiveFilepath) From 50eac0e70c985e9ebf9e0571da8eb9ecd9754dd0 Mon Sep 17 00:00:00 2001 From: Benoit Gagnon Date: Sun, 29 Sep 2019 17:24:47 -0400 Subject: [PATCH 16/16] move the extracted archive into place before exiting Install --- pkg/git.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/git.go b/pkg/git.go index fb6f153..25907e4 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -180,7 +180,14 @@ func (p *GitPackage) Install(ctx context.Context, name, dir, version string) (st r, err := os.Open(archiveFilepath) defer r.Close() if err == nil { + // Extract the sub-directory (if any) from the archive + // If none specified, the entire archive is unpacked err = gzipUntar(tmpDir, r, p.Source.Subdir) + + // Move the extracted directory to its final destination + if err == nil { + err = os.Rename(path.Join(tmpDir, p.Source.Subdir), destPath) + } } }