diff --git a/util/downloader/download.go b/util/downloader/download.go index 59ea6997f..7a8eb9be9 100644 --- a/util/downloader/download.go +++ b/util/downloader/download.go @@ -5,7 +5,6 @@ import ( "os" "path" "path/filepath" - "strconv" "strings" "github.com/abiosoft/colima/config" @@ -19,57 +18,6 @@ type ( guestActions = environment.GuestActions ) -type SHA struct { - URL string // url to download the shasum file (if Digest is empty) - Size int // one of 256 or 512 - Digest string // shasum -} - -func (s SHA) validate(host hostActions, url, cacheFilename string) error { - if s.URL == "" && s.Digest == "" { - return fmt.Errorf("error validating SHA: one of Digest or URL must be set") - } - - if s.Digest != "" { - s.Digest = strings.TrimPrefix(s.Digest, fmt.Sprintf("sha%d:", s.Size)) - } - - filename := func() string { - if url == "" { - return "" - } - split := strings.Split(url, "/") - return split[len(split)-1] - }() - dir, cacheFilename := filepath.Split(cacheFilename) - - var script string - - if s.Digest == "" { - script = strings.NewReplacer( - "{dir}", dir, - "{url}", s.URL, - "{filename}", filename, - "{size}", strconv.Itoa(s.Size), - "{cache_filename}", cacheFilename, - ).Replace( - `cd {dir} && echo "$(curl -sL {url} | grep ' {filename}$' | awk -F' ' '{print $1}') {cache_filename}" | shasum -a {size} --check --status`, - ) - } else { - script = strings.NewReplacer( - "{dir}", dir, - "{digest}", s.Digest, - "{filename}", filename, - "{size}", strconv.Itoa(s.Size), - "{cache_filename}", cacheFilename, - ).Replace( - `cd {dir} && echo "{digest} {cache_filename}" | shasum -a {size} --check --status`, - ) - } - - return host.Run("sh", "-c", script) -} - // Request is download request type Request struct { URL string // request URL @@ -146,7 +94,7 @@ func (d downloader) downloadFile(r Request) (err error) { // validate download if sha is present if r.SHA != nil { - if err := r.SHA.validate(d.host, r.URL, cacheDownloadingFilename); err != nil { + if err := r.SHA.validateDownload(d.host, r.URL, cacheDownloadingFilename); err != nil { // move file to allow subsequent re-download // error discarded, would not be actioned anyways diff --git a/util/downloader/sha.go b/util/downloader/sha.go new file mode 100644 index 000000000..fead072cc --- /dev/null +++ b/util/downloader/sha.go @@ -0,0 +1,75 @@ +package downloader + +import ( + "fmt" + "path/filepath" + "strconv" + "strings" +) + +// SHA is the shasum of a file. +type SHA struct { + Digest string // shasum + URL string // url to download the shasum file (if Digest is empty) + Size int // one of 256 or 512 +} + +// Validate validates the SHA of the file. +func (s SHA) Validate(host hostActions, file string) error { + dir, filename := filepath.Split(file) + + script := strings.NewReplacer( + "{dir}", dir, + "{digest}", s.Digest, + "{size}", strconv.Itoa(s.Size), + "{filename}", filename, + ).Replace( + `cd {dir} && echo "{digest} {filename}" | shasum -a {size} --check --status`, + ) + + return host.Run("sh", "-c", script) +} + +func (s SHA) validateDownload(host hostActions, url string, filename string) error { + if s.URL == "" && s.Digest == "" { + return fmt.Errorf("error validating SHA: one of Digest or URL must be set") + } + + if s.Digest != "" { + s.Digest = strings.TrimPrefix(s.Digest, fmt.Sprintf("sha%d:", s.Size)) + } + + // fetch digest from URL if empty + if s.Digest == "" { + // retrieve the filename from the download url. + filename := func() string { + if url == "" { + return "" + } + split := strings.Split(url, "/") + return split[len(split)-1] + }() + + digest, err := fetchSHAFromURL(host, s.URL, filename) + if err != nil { + return err + } + s.Digest = digest + } + + return s.Validate(host, filename) +} + +func fetchSHAFromURL(host hostActions, url, filename string) (string, error) { + script := strings.NewReplacer( + "{url}", url, + "{filename}", filename, + ).Replace( + "curl -sL {url} | grep ' {filename}$' | awk -F' ' '{print $1}'", + ) + sha, err := host.RunOutput("sh", "-c", script) + if err != nil { + return "", fmt.Errorf("error retrieving sha from url '%s': %w", url, err) + } + return strings.TrimSpace(sha), nil +}