From d563c2714c7022756279da4508ee20ad1ea57b47 Mon Sep 17 00:00:00 2001 From: Ken Robertson Date: Wed, 23 Sep 2015 11:11:05 -0700 Subject: [PATCH] Add support for downloading source artifacts from Github release This adds support for retrieving the tarball or zip source artifact from a Github release. This is done through defining a "params.include_source_tarball" or "params.include_source_zip" boolean setting. With this, the resource will download the respective source artfact into the target directory as either "source.tar.gz" or "source.zip". --- README.md | 8 +++- fakes/fake_git_hub.go | 85 +++++++++++++++++++++++++++++++++++++++++++ github.go | 23 ++++++++++++ in_command.go | 52 +++++++++++++++++++++++++- resources.go | 4 +- 5 files changed, 168 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 442a353..587f8e2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Fetches and creates versioned GitHub resources. * `repository`: *Required.* The repository name that contains the releases. -* `access_token`: *Optional.* Used for accessing a release in a private-repo +* `access_token`: *Optional.* Used for accessing a release in a private-repo during an `in` and pushing a release to a repo during an `out`. The access token you create is only required to have the `repo` or `public_repo` scope. @@ -67,6 +67,12 @@ Also creates the following files: * `globs`: *Optional.* A list of globs for files that will be downloaded from the release. If not specified, all assets will be fetched. +* `include_source_tarball`: *Optional.* Enables downloading of the source + artifact tarball for the release as `source.tar.gz`. Defaults to `false`. + +* `include_source_zip`: *Optional.* Enables downloading of the source + artifact zip for the release as `source.zip`. Defaults to `false`. + ### `out`: Publish a release. Given a name specified in `name`, a body specified in `body`, and the tag to use diff --git a/fakes/fake_git_hub.go b/fakes/fake_git_hub.go index 44998f6..0167741 100644 --- a/fakes/fake_git_hub.go +++ b/fakes/fake_git_hub.go @@ -3,6 +3,7 @@ package fakes import ( "io" + "net/url" "os" "sync" @@ -81,6 +82,24 @@ type FakeGitHub struct { result1 io.ReadCloser result2 error } + GetTarballLinkStub func(tag string) (*url.URL, error) + getTarballLinkMutex sync.RWMutex + getTarballLinkArgsForCall []struct { + tag string + } + getTarballLinkReturns struct { + result1 *url.URL + result2 error + } + GetZipballLinkStub func(tag string) (*url.URL, error) + getZipballLinkMutex sync.RWMutex + getZipballLinkArgsForCall []struct { + tag string + } + getZipballLinkReturns struct { + result1 *url.URL + result2 error + } } func (fake *FakeGitHub) ListReleases() ([]github.RepositoryRelease, error) { @@ -339,4 +358,70 @@ func (fake *FakeGitHub) DownloadReleaseAssetReturns(result1 io.ReadCloser, resul }{result1, result2} } +func (fake *FakeGitHub) GetTarballLink(tag string) (*url.URL, error) { + fake.getTarballLinkMutex.Lock() + fake.getTarballLinkArgsForCall = append(fake.getTarballLinkArgsForCall, struct { + tag string + }{tag}) + fake.getTarballLinkMutex.Unlock() + if fake.GetReleaseByTagStub != nil { + return fake.GetTarballLinkStub(tag) + } else { + return fake.getTarballLinkReturns.result1, fake.getTarballLinkReturns.result2 + } +} + +func (fake *FakeGitHub) GetTarballLinkCallCount() int { + fake.getTarballLinkMutex.RLock() + defer fake.getTarballLinkMutex.RUnlock() + return len(fake.getTarballLinkArgsForCall) +} + +func (fake *FakeGitHub) GetTarballLinkArgsForCall(i int) string { + fake.getTarballLinkMutex.RLock() + defer fake.getTarballLinkMutex.RUnlock() + return fake.getTarballLinkArgsForCall[i].tag +} + +func (fake *FakeGitHub) GetTarballLinkReturns(result1 *url.URL, result2 error) { + fake.GetTarballLinkStub = nil + fake.getTarballLinkReturns = struct { + result1 *url.URL + result2 error + }{result1, result2} +} + +func (fake *FakeGitHub) GetZipballLink(tag string) (*url.URL, error) { + fake.getZipballLinkMutex.Lock() + fake.getZipballLinkArgsForCall = append(fake.getZipballLinkArgsForCall, struct { + tag string + }{tag}) + fake.getZipballLinkMutex.Unlock() + if fake.GetReleaseByTagStub != nil { + return fake.GetZipballLinkStub(tag) + } else { + return fake.getZipballLinkReturns.result1, fake.getZipballLinkReturns.result2 + } +} + +func (fake *FakeGitHub) GetZipballLinkCallCount() int { + fake.getZipballLinkMutex.RLock() + defer fake.getZipballLinkMutex.RUnlock() + return len(fake.getZipballLinkArgsForCall) +} + +func (fake *FakeGitHub) GetZipballLinkArgsForCall(i int) string { + fake.getZipballLinkMutex.RLock() + defer fake.getZipballLinkMutex.RUnlock() + return fake.getZipballLinkArgsForCall[i].tag +} + +func (fake *FakeGitHub) GetZipballLinkReturns(result1 *url.URL, result2 error) { + fake.GetZipballLinkStub = nil + fake.getZipballLinkReturns = struct { + result1 *url.URL + result2 error + }{result1, result2} +} + var _ resource.GitHub = new(FakeGitHub) diff --git a/github.go b/github.go index e851147..f8f9592 100644 --- a/github.go +++ b/github.go @@ -23,6 +23,9 @@ type GitHub interface { UploadReleaseAsset(release github.RepositoryRelease, name string, file *os.File) error DeleteReleaseAsset(asset github.ReleaseAsset) error DownloadReleaseAsset(asset github.ReleaseAsset) (io.ReadCloser, error) + + GetTarballLink(tag string) (*url.URL, error) + GetZipballLink(tag string) (*url.URL, error) } type GitHubClient struct { @@ -170,3 +173,23 @@ func (g *GitHubClient) DownloadReleaseAsset(asset github.ReleaseAsset) (io.ReadC return res, err } + +func (g *GitHubClient) GetTarballLink(tag string) (*url.URL, error) { + opt := &github.RepositoryContentGetOptions{Ref: tag} + u, res, err := g.client.Repositories.GetArchiveLink(g.user, g.repository, github.Tarball, opt) + if err != nil { + return nil, err + } + res.Body.Close() + return u, nil +} + +func (g *GitHubClient) GetZipballLink(tag string) (*url.URL, error) { + opt := &github.RepositoryContentGetOptions{Ref: tag} + u, res, err := g.client.Repositories.GetArchiveLink(g.user, g.repository, github.Zipball, opt) + if err != nil { + return nil, err + } + res.Body.Close() + return u, nil +} diff --git a/in_command.go b/in_command.go index e95d069..e5c786e 100644 --- a/in_command.go +++ b/in_command.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "os" "path/filepath" @@ -84,12 +85,34 @@ func (c *InCommand) Run(destDir string, request InRequest) (InResponse, error) { fmt.Fprintf(c.writer, "downloading asset: %s\n", *asset.Name) - err := c.downloadFile(asset, path) + err := c.downloadAsset(asset, path) if err != nil { return InResponse{}, err } } + if request.Params.IncludeSourceTarball { + u, err := c.github.GetTarballLink(request.Version.Tag) + if err != nil { + return InResponse{}, err + } + fmt.Fprintln(c.writer, "downloading source tarball to source.tar.gz") + if err := c.downloadFile(u.String(), filepath.Join(destDir, "source.tar.gz")); err != nil { + return InResponse{}, err + } + } + + if request.Params.IncludeSourceZip { + u, err := c.github.GetZipballLink(request.Version.Tag) + if err != nil { + return InResponse{}, err + } + fmt.Fprintln(c.writer, "downloading source zip to source.zip") + if err := c.downloadFile(u.String(), filepath.Join(destDir, "source.zip")); err != nil { + return InResponse{}, err + } + } + return InResponse{ Version: Version{ Tag: *foundRelease.TagName, @@ -98,7 +121,7 @@ func (c *InCommand) Run(destDir string, request InRequest) (InResponse, error) { }, nil } -func (c *InCommand) downloadFile(asset github.ReleaseAsset, destPath string) error { +func (c *InCommand) downloadAsset(asset github.ReleaseAsset, destPath string) error { out, err := os.Create(destPath) if err != nil { return err @@ -118,3 +141,28 @@ func (c *InCommand) downloadFile(asset github.ReleaseAsset, destPath string) err return nil } + +func (c *InCommand) downloadFile(url, destPath string) error { + out, err := os.Create(destPath) + if err != nil { + return err + } + defer out.Close() + + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to download file: HTTP status %d: %s", resp.StatusCode, resp.Status) + } + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} diff --git a/resources.go b/resources.go index 9cb67df..8d601fe 100644 --- a/resources.go +++ b/resources.go @@ -20,7 +20,9 @@ type InRequest struct { } type InParams struct { - Globs []string `json:"globs"` + Globs []string `json:"globs"` + IncludeSourceTarball bool `json:"include_source_tarball"` + IncludeSourceZip bool `json:"include_source_zip"` } type InResponse struct {