| 267 | } |
| 268 | |
| 269 | func fetchLatestRelease( |
| 270 | ctx context.Context, |
| 271 | client HttpClient, |
| 272 | url string, |
| 273 | ) (*release, error) { |
| 274 | req, err := http.NewRequestWithContext(ctx, "GET", url, nil) |
| 275 | if err != nil { |
| 276 | return nil, err |
| 277 | } |
| 278 | |
| 279 | res, err := client.Do(req) |
| 280 | if err != nil { |
| 281 | return nil, err |
| 282 | } |
| 283 | defer res.Body.Close() |
| 284 | |
| 285 | rawBody, err := io.ReadAll(res.Body) |
| 286 | if err != nil { |
| 287 | return nil, err |
| 288 | } |
| 289 | |
| 290 | // http.Client doesn't treat non 2xx responses as error |
| 291 | if res.StatusCode >= 400 { |
| 292 | return nil, fmt.Errorf( |
| 293 | "(%d) failed to fetch latest releases:\n%s", |
| 294 | res.StatusCode, |
| 295 | string(rawBody), |
| 296 | ) |
| 297 | } |
| 298 | |
| 299 | result := &release{} |
| 300 | if err := json.Unmarshal(rawBody, result); err != nil { |
| 301 | return nil, err |
| 302 | } |
| 303 | |
| 304 | return result, nil |
| 305 | } |
| 306 | |
| 307 | func downloadFile( |
| 308 | ctx context.Context, |