зеркало из https://github.com/mislav/hub.git
Add `hub release download` command that downloads all assets.
This commit is contained in:
Родитель
f1a1c1e38c
Коммит
c57cb62ef9
|
@ -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)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче