Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/wfctl/download_progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,14 @@ func (p *downloadProgress) emit(prefix string) {

func formatDownloadBytes(n int64) string {
const unit = 1024
const prefixes = "KMGTPE"
if n < unit {
return fmt.Sprintf("%d B", n)
}
div, exp := int64(unit), 0
for next := n / unit; next >= unit && exp < 4; next /= unit {
for next := n / unit; next >= unit && exp < len(prefixes)-1; next /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %ciB", float64(n)/float64(div), "KMGTPE"[exp])
return fmt.Sprintf("%.1f %ciB", float64(n)/float64(div), prefixes[exp])
}
14 changes: 13 additions & 1 deletion cmd/wfctl/plugin_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -1122,9 +1122,9 @@ func downloadGitHubReleaseAsset(owner, repo, tag, filename, token string) ([]byt
neturl.PathEscape(tag),
)
metadataCtx, metadataCancel := context.WithTimeout(context.Background(), gitHubReleaseMetadataTimeout)
defer metadataCancel()
req, err := http.NewRequestWithContext(metadataCtx, http.MethodGet, releaseURL, nil)
if err != nil {
metadataCancel()
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
Expand All @@ -1134,10 +1134,13 @@ func downloadGitHubReleaseAsset(owner, repo, tag, filename, token string) ([]byt

resp, err := gitHubAPIClient.Do(req)
if err != nil {
closeResponseBody(resp)
metadataCancel()
return nil, fmt.Errorf("GitHub releases API: %w", err)
}
defer resp.Body.Close()
Comment thread
intel352 marked this conversation as resolved.
if resp.StatusCode != http.StatusOK {
metadataCancel()
return nil, fmt.Errorf("GitHub releases API: HTTP %d for %s/%s@%s", resp.StatusCode, owner, repo, tag)
}

Expand All @@ -1148,6 +1151,7 @@ func downloadGitHubReleaseAsset(owner, repo, tag, filename, token string) ([]byt
} `json:"assets"`
}
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
metadataCancel()
return nil, fmt.Errorf("decode GitHub release response: %w", err)
}
metadataCancel()
Expand Down Expand Up @@ -1183,6 +1187,7 @@ func downloadGitHubReleaseAsset(owner, repo, tag, filename, token string) ([]byt

resp2, err := gitHubAPIClient.Do(req2)
if err != nil {
closeResponseBody(resp2)
return nil, fmt.Errorf("GitHub asset download API: %w", err)
}
defer resp2.Body.Close()
Expand Down Expand Up @@ -1224,6 +1229,7 @@ func downloadURL(rawURL string) ([]byte, error) {
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
closeResponseBody(resp)
return nil, err
}
defer resp.Body.Close()
Expand All @@ -1233,6 +1239,12 @@ func downloadURL(rawURL string) ([]byte, error) {
return readDownloadBodyWithProgress(resp.Body, resp.ContentLength)
}

func closeResponseBody(resp *http.Response) {
if resp != nil && resp.Body != nil {
_ = resp.Body.Close()
}
}

// verifyChecksum checks that data matches the expected SHA256 hex string.
func verifyChecksum(data []byte, expected string) error {
h := sha256.Sum256(data)
Expand Down
25 changes: 24 additions & 1 deletion cmd/wfctl/plugin_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ func TestDownloadURL_DirectGetUsesBoundedRequestContext(t *testing.T) {
}
t.Cleanup(func() { http.DefaultClient = orig })

got, err := downloadURL("https://example.com/plugin.tar.gz")
var got []byte
_, err := captureStderr(t, func() error {
var err error
got, err = downloadURL("https://example.com/plugin.tar.gz")
return err
})
if err != nil {
t.Fatalf("downloadURL: %v", err)
}
Expand Down Expand Up @@ -170,6 +175,14 @@ func TestDownloadURL_LargeDirectDownloadEmitsProgress(t *testing.T) {
}
}

func TestCloseResponseBodyClosesNonNilBody(t *testing.T) {
body := &trackingReadCloser{Reader: strings.NewReader("metadata")}
closeResponseBody(&http.Response{Body: body})
if !body.closed {
t.Fatal("response body was not closed")
}
}

// TestDownloadURL_GitHubAuthHeader verifies that downloadURL injects a Bearer
// Authorization header for non-release github.com URLs (direct-download path)
// using the first non-empty token env var (RELEASES_TOKEN > GH_TOKEN >
Expand Down Expand Up @@ -514,6 +527,16 @@ func TestDownloadURL_PrivateReleaseAssetUsesFreshAssetDownloadDeadline(t *testin
}
}

type trackingReadCloser struct {
*strings.Reader
closed bool
}

func (r *trackingReadCloser) Close() error {
r.closed = true
return nil
}

// TestDownloadURL_PublicReleaseNoToken verifies that when no token is set,
// downloadURL falls back to a plain GET for release download URLs (public repos).
func TestDownloadURL_PublicReleaseNoToken(t *testing.T) {
Expand Down
Loading