Merge pull request #10947 from cli/gitulisca/handling-download-of-unpublished-release

Handle missing archive URLs on release download
This commit is contained in:
William Martin 2025-05-08 15:03:06 +02:00 committed by GitHub
commit 202a4f3c5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 233 additions and 35 deletions

View file

@ -165,10 +165,24 @@ func downloadRun(opts *DownloadOptions) error {
var toDownload []shared.ReleaseAsset
isArchive := false
if opts.ArchiveType != "" {
var archiveURL = release.ZipballURL
var archiveURL string
if opts.ArchiveType == "tar.gz" {
archiveURL = release.TarballURL
} else {
archiveURL = release.ZipballURL
}
if archiveURL == "" {
errMessage := fmt.Sprintf(
"release %q with tag %q, does not have a %q archive asset.",
release.Name, release.TagName, opts.ArchiveType,
)
if release.IsDraft {
errMessage += " Most likely, this is because it is a draft."
}
return errors.New(errMessage)
}
// create pseudo-Asset with no name and pointing to ZipBallURL or TarBallURL
toDownload = append(toDownload, shared.ReleaseAsset{APIURL: archiveURL})
isArchive = true

View file

@ -183,6 +183,7 @@ func Test_downloadRun(t *testing.T) {
name string
isTTY bool
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
@ -196,6 +197,24 @@ func Test_downloadRun(t *testing.T) {
Destination: ".",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
reg.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
reg.Register(httpmock.REST("GET", "assets/5678"), httpmock.StringResponse(`5678`))
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -213,6 +232,23 @@ func Test_downloadRun(t *testing.T) {
Destination: "tmp/assets",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
reg.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -229,6 +265,20 @@ func Test_downloadRun(t *testing.T) {
Destination: ".",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
},
wantStdout: ``,
wantStderr: ``,
wantErr: "no assets match the file pattern",
@ -242,6 +292,30 @@ func Test_downloadRun(t *testing.T) {
Destination: "tmp/packages",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(
httpmock.REST(
"GET",
"repos/OWNER/REPO/zipball/v1.2.3",
),
httpmock.WithHeader(
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=zipball.zip",
),
)
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -257,6 +331,30 @@ func Test_downloadRun(t *testing.T) {
Destination: "tmp/packages",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(
httpmock.REST(
"GET",
"repos/OWNER/REPO/tarball/v1.2.3",
),
httpmock.WithHeader(
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
),
)
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -273,6 +371,30 @@ func Test_downloadRun(t *testing.T) {
Concurrency: 2,
ArchiveType: "tar.gz",
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(
httpmock.REST(
"GET",
"repos/OWNER/REPO/tarball/v1.2.3",
),
httpmock.WithHeader(
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
),
)
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -289,6 +411,22 @@ func Test_downloadRun(t *testing.T) {
Concurrency: 2,
FilePatterns: []string{"*windows-32bit.zip"},
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
},
wantStdout: ``,
wantStderr: ``,
wantFiles: []string{
@ -305,9 +443,85 @@ func Test_downloadRun(t *testing.T) {
Concurrency: 2,
FilePatterns: []string{"*windows-32bit.zip"},
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
},
wantStdout: `1234`,
wantStderr: ``,
},
{
name: "draft release with null tarball_url and zipball_url",
isTTY: true,
opts: DownloadOptions{
TagName: "v1.2.3",
ArchiveType: "tar.gz",
Destination: "tmp/packages",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"tag_name": "v1.2.3",
"name": "patch-36",
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": null,
"zipball_url": null,
"draft": true
}`)
},
wantStdout: ``,
wantStderr: ``,
wantErr: "release \"patch-36\" with tag \"v1.2.3\", does not have a \"tar.gz\" archive asset. Most likely, this is because it is a draft.",
},
{
name: "non-draft release with null tarball_url and zipball_url",
isTTY: true,
opts: DownloadOptions{
TagName: "v1.2.3",
ArchiveType: "tar.gz",
Destination: "tmp/packages",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"tag_name": "v1.2.3",
"name": "patch-36",
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": null,
"zipball_url": null,
"draft": false
}`)
},
wantStdout: ``,
wantStderr: ``,
wantErr: "release \"patch-36\" with tag \"v1.2.3\", does not have a \"tar.gz\" archive asset.",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -324,41 +538,11 @@ func Test_downloadRun(t *testing.T) {
ios.SetStderrTTY(tt.isTTY)
fakeHTTP := &httpmock.Registry{}
shared.StubFetchRelease(t, fakeHTTP, "OWNER", "REPO", tt.opts.TagName, `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
}`)
fakeHTTP.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
fakeHTTP.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
fakeHTTP.Register(httpmock.REST("GET", "assets/5678"), httpmock.StringResponse(`5678`))
defer fakeHTTP.Verify(t)
fakeHTTP.Register(
httpmock.REST(
"GET",
"repos/OWNER/REPO/tarball/v1.2.3",
),
httpmock.WithHeader(
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
),
)
fakeHTTP.Register(
httpmock.REST(
"GET",
"repos/OWNER/REPO/zipball/v1.2.3",
),
httpmock.WithHeader(
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=zipball.zip",
),
)
if tt.httpStubs != nil {
tt.httpStubs(fakeHTTP)
}
tt.opts.IO = ios
tt.opts.HttpClient = func() (*http.Client, error) {