New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add hub release download
command that downloads all assets.
#1103
Conversation
78d701c
to
a9c587e
Compare
a9c587e
to
2d761b8
Compare
I'm thinking for completeness this should have some args:
Not sure what the default should be (or perhaps there shouldn't be one). A filter for --assets might be nice, but I don't need one 😎 |
3c38b59
to
9331204
Compare
return | ||
} | ||
|
||
asset, err = api.GetBody(url, "application/octet-stream") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we avoid adding a new GetBody
function and simply use api.Get
to obtain a response object and return its .Body
property from this function? I think it will result in less overall added code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mislav Get()
doesn't appear to allow setting the Accept header. Should I change it's signature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mislav I just assumed the answer was yes. See new diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get()
doesn't appear to allow setting the Accept header. Should I change it's signature?
Oh I see. I don't think we should be changing Get signature across the board. I forgot that Get doesn't support a configure block. I would go back to your initial idea of creating a specialized method.
07e0675
to
c5f8f78
Compare
Thanks for your pull request! This is a good start, but we'll have to polish this command's outside API a bit. First of all, what would be the use-case for this command? Will people usually want to download all assets, or just a specific one? Will the GET http request need to follow redirects? Etc. Thanks for your patience! |
|
I had this idea that supporting a
Not necessarily a blocker for this PR, but I would definitely like to reach a decision before this subcommand hits the next feature release.
I don't think it does… the |
@mislav it is following redirects. The final URL is available in the response: response.Request.URL.String() A test, just adding a couple prints to the current code (sensitive parts changed):
|
return | ||
} | ||
|
||
result, err := api.Get(url, func(req *http.Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use performRequest
so we don't have to change Get()
signature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not directly because performRequest isn't exported, but I added a GetFile()
.
Oh wow, I never realized this API client would follow redirects automatically. I'm not sure where it happens, but it's awesome if it works. Could you add a cuke for this feature to |
c5f8f78
to
5e15816
Compare
|
||
ui.Printf("Downloading %s ...\n", asset.Name) | ||
|
||
assetFile, err := os.OpenFile(asset.Name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about os.CreateFile
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you mean os.Create
, it truncates an existing file. I fail if there is an existing file (O_EXCL
). Ideally I would append (n) if the file already exists like a browser might do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, makes sense. How about using 0644
as default permissions?
Few nits, but otherwise this is coming together really nicely! 👍 thanks for your patience |
Thanks for the comments. I'm poking at the cucumber test as I get free time - though I don't have much the next few days. |
Added cucumber scenario. I'll address your comments as a get more free time. |
1c304d0
to
0769f95
Compare
@mislav addressed your comments. |
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') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you append the :host_name => 'github-cloud.s3.amazonaws.com'
constraint to this route so it serves as an assertion that this route was called with the correct host name and not api.github.com
? Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mislav good idea, just tried.
I get a 404. If I return the host header in the current method, it's 127.0.0.1. I'm not sure what's happening here but I suspect it's an artifact of the testing framework.
get('/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz') {
headers['Content-Type'] = 'application/octet-stream'
request.host
}
expected: "ASSET_TARBALL"
got: "127.0.0.1"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange. That should work, since we're using :host_name
constraint elsewhere in the test suite. When debugging, I think you should probably look at request.headers['host']
or request.env['HTTP_HOST']
since request.host
is not guaranteed to be accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hrmm, maybe the Go client isn't redirecting correctly. I'll try changing the code to match the redirect tooling I see in Get()
though it might be a few days before I do that.
tcpflow output:
127.000.000.001.53925-127.000.000.001.53924: GET /__identify__ HTTP/1.1
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
User-Agent: Ruby
Host: 127.0.0.1:53924
127.000.000.001.53925-127.000.000.001.53924: GET /__identify__ HTTP/1.1
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
User-Agent: Ruby
Host: 127.0.0.1:53924
127.000.000.001.53924-127.000.000.001.53925: HTTP/1.1 200 OK
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Content-Length: 14
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53925: HTTP/1.1 200 OK
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Content-Length: 14
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53925: 70296040667240
127.000.000.001.53924-127.000.000.001.53925: 70296040667240
127.000.000.001.53928-127.000.000.001.53924: GET /repos/mislav/will_paginate/releases HTTP/1.1
Host: api.github.com
User-Agent: Go-http-client/1.1
Authorization: token OTOKEN
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53928-127.000.000.001.53924: GET /repos/mislav/will_paginate/releases HTTP/1.1
Host: api.github.com
User-Agent: Go-http-client/1.1
Authorization: token OTOKEN
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53924-127.000.000.001.53928: HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Content-Length: 332
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53928: HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Content-Length: 332
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53928: [{"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"}]}]
127.000.000.001.53924-127.000.000.001.53928: [{"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"}]}]
127.000.000.001.53929-127.000.000.001.53924: GET /repos/mislav/will_paginate/assets/9876 HTTP/1.1
Host: api.github.com
User-Agent: Go-http-client/1.1
Authorization: token OTOKEN
Content-Type: application/octet-stream
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53929-127.000.000.001.53924: GET /repos/mislav/will_paginate/assets/9876 HTTP/1.1
Host: api.github.com
User-Agent: Go-http-client/1.1
Authorization: token OTOKEN
Content-Type: application/octet-stream
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53924-127.000.000.001.53929: HTTP/1.1 302 Found
Content-Type: application/json;charset=utf-8
Location: https://github-cloud.s3.amazonaws.com/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz
Content-Length: 2
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53929: HTTP/1.1 302 Found
Content-Type: application/json;charset=utf-8
Location: https://github-cloud.s3.amazonaws.com/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz
Content-Length: 2
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53929: {}
127.000.000.001.53924-127.000.000.001.53929: {}
127.000.000.001.53929-127.000.000.001.53924: GET /releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz HTTP/1.1
Host: 127.0.0.1:53924
User-Agent: Go-http-client/1.1
Referer: https://api.github.com/repos/mislav/will_paginate/assets/9876
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53929-127.000.000.001.53924: GET /releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz HTTP/1.1
Host: 127.0.0.1:53924
User-Agent: Go-http-client/1.1
Referer: https://api.github.com/repos/mislav/will_paginate/assets/9876
X-Original-Port: 80
X-Original-Scheme: https
Accept-Encoding: gzip
127.000.000.001.53924-127.000.000.001.53929: HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1238
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53929: HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 1238
Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
Date: Wed, 17 Feb 2016 15:13:53 GMT
Connection: Keep-Alive
127.000.000.001.53924-127.000.000.001.53929: {
"GATEWAY_INTERFACE": "CGI/1.1",
"PATH_INFO": "/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"QUERY_STRING": "",
"REMOTE_ADDR": "127.0.0.1",
"REMOTE_HOST": "localhost",
"REQUEST_METHOD": "GET",
"REQUEST_URI": "http://127.0.0.1:53924/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"SCRIPT_NAME": "",
"SERVER_NAME": "127.0.0.1",
"SERVER_PORT": "53924",
"SERVER_PROTOCOL": "HTTP/1.1",
"SERVER_SOFTWARE": "WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)",
"HTTP_HOST": "127.0.0.1:53924",
"HTTP_USER_AGENT": "Go-http-client/1.1",
"HTTP_REFERER": "https://api.github.com/repos/mislav/will_paginate/assets/9876",
"HTTP_X_ORIGINAL_PORT": "80",
"HTTP_X_ORIGINAL_SCHEME": "https",
"HTTP_ACCEPT_ENCODING": "gzip",
"rack.version": [
1,
1
],
"rack.input": "#<StringIO:0x007fde2505e988>",
"rack.errors": "#<IO:0x007fde240c6520>",
"rack.multithread": true,
"rack.multiprocess": false,
"rack.run_once": false,
"rack.url_scheme": "http",
"HTTP_VERSION": "HTTP/1.1",
"REQUEST_PATH": "/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"rack.logger": "#<Rack::NullLogger:0x007fde2510f120>",
"rack.request.query_string": "",
"rack.request.query_hash": {
}
}
127.000.000.001.53924-127.000.000.001.53929: {
"GATEWAY_INTERFACE": "CGI/1.1",
"PATH_INFO": "/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"QUERY_STRING": "",
"REMOTE_ADDR": "127.0.0.1",
"REMOTE_HOST": "localhost",
"REQUEST_METHOD": "GET",
"REQUEST_URI": "http://127.0.0.1:53924/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"SCRIPT_NAME": "",
"SERVER_NAME": "127.0.0.1",
"SERVER_PORT": "53924",
"SERVER_PROTOCOL": "HTTP/1.1",
"SERVER_SOFTWARE": "WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)",
"HTTP_HOST": "127.0.0.1:53924",
"HTTP_USER_AGENT": "Go-http-client/1.1",
"HTTP_REFERER": "https://api.github.com/repos/mislav/will_paginate/assets/9876",
"HTTP_X_ORIGINAL_PORT": "80",
"HTTP_X_ORIGINAL_SCHEME": "https",
"HTTP_ACCEPT_ENCODING": "gzip",
"rack.version": [
1,
1
],
"rack.input": "#<StringIO:0x007fde2505e988>",
"rack.errors": "#<IO:0x007fde240c6520>",
"rack.multithread": true,
"rack.multiprocess": false,
"rack.run_once": false,
"rack.url_scheme": "http",
"HTTP_VERSION": "HTTP/1.1",
"REQUEST_PATH": "/releases/12204602/22ea221a-cf2f-11e2-222a-b3a3c3b3aa3a.gz",
"rack.logger": "#<Rack::NullLogger:0x007fde2510f120>",
"rack.request.query_string": "",
"rack.request.query_hash": {
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah seems that Go isn't handling 302 correctly. At first I thought that their HTTP client doesn't do redirects automatically at all, but now it turns out that it does, but messes it up. This might be a problem.
We should probably provide our own CheckRedirect
implementation. Think you can take a stab at that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mislav I tried, but it doesn't seem to get called. afb17d8
EDIT: It is called during the cucumber tests...argh...
Interestingly, this worked with real GH releases for a week or so after I opened this PR. Today, it no longer works - probably because of the host header, though I'm not totally sure. I verified cURL has a correct host header while following the redirects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, fixed! Last issues with S3 was pebkac. S3 doesn't seem to care about the host header so far (probably because they don't use SNI). I also implemented redirect header preservation to match cURL behavior. It's not strictly required if you are concerned about it.
afb17d8
to
c995586
Compare
c995586
to
22a4660
Compare
// Maintain headers after redirect. | ||
// https://github.com/golang/go/issues/4800 | ||
for key, val := range via[0].Header { | ||
if key == "Authorization" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do a case-insensitive comparison here. Also, could we maintain even this header if the redirect is to the same host?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Awesome work on the redirect stuff! Still will come in handy for various different operations in the future 😸 |
22a4660
to
8c09faf
Compare
@mislav addressed your comments. |
for _, asset := range release.Assets { | ||
assetReader, apiError := gh.DownloadReleaseAsset(asset.ApiUrl) | ||
utils.Check(apiError) | ||
defer assetReader.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could close the assetReader
on each loop is finished instead of waiting for the whole program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracted to a function.
8c09faf
to
e63c2ac
Compare
@jingweno I addressed your comments. |
e63c2ac
to
c57cb62
Compare
Add `hub release download` command that downloads all assets.
@glennpratt Thanks for all the hard work! |
Woohoo. Thank you for all the guidance. |
This is probably overly simplistic, it downloads all assets if there are any, writes them in the current directory and prints the names.
I was lazy and avoided touching octokit.
Addresses #1102