Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.11.1
go.bug.st/cleanup v1.0.0
go.bug.st/downloader/v2 v2.2.0
go.bug.st/f v0.4.0
go.bug.st/relaxed-semver v0.15.0
google.golang.org/grpc v1.77.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
Expand All @@ -183,6 +184,8 @@ github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.bug.st/cleanup v1.0.0 h1:XVj1HZxkBXeq3gMT7ijWUpHyIC1j8XAoNSyQ06CskgA=
go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk=
go.bug.st/downloader/v2 v2.2.0 h1:Y0jSuDISNhrzePkrAWqz9xUC3xol9hqZo/+tz1D4EqY=
go.bug.st/downloader/v2 v2.2.0/go.mod h1:VZW2V1iGKV8rJL2ZEGIDzzBeKowYv34AedJz13RzVII=
go.bug.st/f v0.4.0 h1:Vstqb950nMA+PhAlRxUw8QL1ntHy/gXHNyyzjkQLJ10=
go.bug.st/f v0.4.0/go.mod h1:bMo23205ll7UW63KwO1ut5RdlJ9JK8RyEEr88CmOF5Y=
go.bug.st/relaxed-semver v0.15.0 h1:w37+SYQPxF53RQO7QZZuPIMaPouOifdaP0B1ktst2nA=
Expand Down
59 changes: 59 additions & 0 deletions internal/updater/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package updater

import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
Expand All @@ -24,8 +25,14 @@ import (
"io"
"net/http"
"net/url"
"time"

"github.com/arduino/go-paths-helper"
"go.bug.st/downloader/v2"
"go.bug.st/f"

"github.com/arduino/arduino-flasher-cli/cmd/i18n"
"github.com/arduino/arduino-flasher-cli/rpc/cc/arduino/flasher/v1"
)

var baseURL = f.Must(url.Parse("https://downloads.arduino.cc"))
Expand Down Expand Up @@ -127,3 +134,55 @@ func (c *Client) FetchZip(ctx context.Context, zipURL string) (io.ReadCloser, in
}
return resp.Body, resp.ContentLength, nil
}

// DownloadFile downloads a file from a URL into the specified path. An optional config and options may be passed (or nil to use the defaults).
// A DownloadProgressCB callback function must be passed to monitor download progress.
// If a not empty queryParameter is passed, it is appended to the URL for analysis purposes.
func DownloadFile(ctx context.Context, path *paths.Path, rel *Release, downloadCB flasher.DownloadProgressCB, config downloader.Config, options ...downloader.DownloadOptions) (returnedError error) {
downloadCB.Start(rel.Url, rel.Version)
defer func() {
if returnedError == nil {
downloadCB.End(true, "")
} else {
downloadCB.End(false, returnedError.Error())
}
}()

d, err := downloader.DownloadWithConfigAndContext(ctx, path.String(), rel.Url, config, options...)
if err != nil {
return err
}

err = d.RunAndPoll(func(downloaded int64) {
downloadCB.Update(downloaded, d.Size())
}, 250*time.Millisecond)
if err != nil {
return err
}

// The URL is not reachable for some reason
if d.Resp.StatusCode >= 400 && d.Resp.StatusCode <= 599 {
msg := i18n.Tr("Server responded with: %s", d.Resp.Status)
return fmt.Errorf("%s", msg)
}

// Check the hash
checksum := sha256.New()
tmpZipFile, err := path.Open()
if err != nil {
return fmt.Errorf("could not open archive: %w", err)
}
defer tmpZipFile.Close()

_, err = io.Copy(checksum, tmpZipFile)
if err != nil {
return err
}
if sha256Byte, err := hex.DecodeString(rel.Sha256); err != nil {
return fmt.Errorf("could not convert sha256 from hex to bytes: %w", err)
} else if s := checksum.Sum(nil); !bytes.Equal(s, sha256Byte) {
return fmt.Errorf("bad hash: %x (expected %x)", s, sha256Byte)
}

return nil
}
Loading