Add `hub release download` command that downloads all assets.

This commit is contained in:
Glenn Pratt 2016-02-05 17:24:01 -06:00
Родитель f1a1c1e38c
Коммит c57cb62ef9
4 изменённых файлов: 148 добавлений и 2 удалений

Просмотреть файл

@ -2,6 +2,7 @@ package commands
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -46,6 +47,9 @@ With '--include-drafts', include draft releases in the listing.
pre-populated with current release title and body. To re-use existing title
and body unchanged, pass '-m ""'.
* _download_:
Download the assets attached to release for the specified <TAG>.
## Options:
-d, --draft
Create a draft release.
@ -64,7 +68,7 @@ With '--include-drafts', include draft releases in the listing.
-F, --file <FILE>
Read the release title and description from <FILE>.
-c, --commitish <TARGET>
A SHA, tag, or branch name to attach the release to (default: current branch).
@ -92,6 +96,11 @@ hub(1), git-tag(1)
Run: editRelease,
}
cmdDownloadRelease = &Command{
Key: "download",
Run: downloadRelease,
}
flagReleaseIncludeDrafts,
flagReleaseShowDownloads,
flagReleaseDraft,
@ -126,6 +135,7 @@ func init() {
cmdRelease.Use(cmdShowRelease)
cmdRelease.Use(cmdCreateRelease)
cmdRelease.Use(cmdEditRelease)
cmdRelease.Use(cmdDownloadRelease)
CmdRunner.Use(cmdRelease)
}
@ -195,6 +205,52 @@ func showRelease(cmd *Command, args *Args) {
os.Exit(0)
}
func downloadRelease(cmd *Command, args *Args) {
tagName := cmd.Arg(0)
if tagName == "" {
utils.Check(fmt.Errorf("Missing argument TAG"))
}
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.MainProject()
utils.Check(err)
gh := github.NewClient(project.Host)
release, err := gh.FetchRelease(project, tagName)
utils.Check(err)
for _, asset := range release.Assets {
ui.Printf("Downloading %s ...\n", asset.Name)
err := downloadReleaseAsset(asset, gh)
utils.Check(err)
}
os.Exit(0)
}
func downloadReleaseAsset(asset github.ReleaseAsset, gh *github.Client) (err error) {
assetReader, err := gh.DownloadReleaseAsset(asset.ApiUrl)
if err != nil {
return
}
defer assetReader.Close()
assetFile, err := os.OpenFile(asset.Name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
return
}
defer assetFile.Close()
_, err = io.Copy(assetFile, assetReader)
if err != nil {
return
}
return
}
func createRelease(cmd *Command, args *Args) {
tagName := cmd.Arg(0)
if tagName == "" {

Просмотреть файл

@ -294,3 +294,46 @@ MARKDOWN
"""
Attaching release asset `hello-1.2.0.tar.gz'...\n
"""
Scenario: Download a release asset.
Given the GitHub API server:
"""
get('/repos/mislav/will_paginate/releases') {
json [
{ url: 'https://api.github.com/repos/mislav/will_paginate/releases/123',
upload_url: 'https://api.github.com/uploads/assets{?name,label}',
tag_name: 'v1.2.0',
name: 'will_paginate 1.2.0',
draft: true,
prerelease: false,
assets: [
{ url: 'https://api.github.com/repos/mislav/will_paginate/assets/9876',
name: 'hello-1.2.0.tar.gz',
},
],
},
]
}
get('/repos/mislav/will_paginate/assets/9876') {
halt 401 unless request.env['HTTP_AUTHORIZATION'] == 'token OTOKEN'
halt 415 unless request.accept?('application/octet-stream')
status 302
headers['Location'] = 'https://github-cloud.s3.amazonaws.com/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz'
""
}
get('/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz', :host_name => 'github-cloud.s3.amazonaws.com') {
halt 400 unless request.env['HTTP_AUTHORIZATION'].nil?
halt 415 unless request.accept?('application/octet-stream')
headers['Content-Type'] = 'application/octet-stream'
"ASSET_TARBALL"
}
"""
When I successfully run `hub release download v1.2.0`
Then the output should contain exactly:
"""
Downloading hello-1.2.0.tar.gz ...\n
"""
And the file "hello-1.2.0.tar.gz" should contain exactly:
"""
ASSET_TARBALL
"""

Просмотреть файл

@ -371,6 +371,20 @@ func (client *Client) DeleteReleaseAsset(asset *ReleaseAsset) (err error) {
return
}
func (client *Client) DownloadReleaseAsset(url string) (asset io.ReadCloser, err error) {
api, err := client.simpleApi()
if err != nil {
return
}
resp, err := api.GetFile(url, "application/octet-stream")
if err = checkStatus(200, "downloading asset", resp, err); err != nil {
return
}
return resp.Body, err
}
type CIStatusResponse struct {
State string `json:"state"`
Statuses []CIStatus `json:"statuses"`

Просмотреть файл

@ -3,6 +3,7 @@ package github
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -135,7 +136,33 @@ func newHttpClient(testHost string, verbose bool) *http.Client {
Out: ui.Stderr,
Colorized: ui.IsTerminal(os.Stderr),
}
return &http.Client{Transport: tr}
// Implement CheckRedirect callback to fix issues with net/http.
fixupCheckRedirect := func(req *http.Request, via []*http.Request) error {
// net/http doesn't send a Host header on redirect requests.
// TODO: Find or file a Go bug.
if req.Host == "" {
req.Host = req.URL.Host
}
// Maintain headers after redirect.
// https://github.com/golang/go/issues/4800
for key, val := range via[0].Header {
if req.Host != via[0].Host && strings.EqualFold(key, "Authorization") {
continue
}
req.Header[key] = val
}
// remainder should match http/Client.defaultCheckRedirect()
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
return &http.Client{Transport: tr, CheckRedirect: fixupCheckRedirect}
}
func cloneRequest(req *http.Request) *http.Request {
@ -219,6 +246,12 @@ func (c *simpleClient) Get(path string) (*simpleResponse, error) {
return c.performRequest("GET", path, nil, nil)
}
func (c *simpleClient) GetFile(path string, mimeType string) (*simpleResponse, error) {
return c.performRequest("GET", path, nil, func(req *http.Request) {
req.Header.Set("Accept", mimeType)
})
}
func (c *simpleClient) Delete(path string) (*simpleResponse, error) {
return c.performRequest("DELETE", path, nil, nil)
}