diff --git a/build/chrome.go b/build/chrome.go index d2a3b8c30..68efb6b11 100644 --- a/build/chrome.go +++ b/build/chrome.go @@ -1,15 +1,21 @@ package build import ( + "encoding/json" "errors" "fmt" + hv "github.com/hashicorp/go-version" + "net/http" "os" "path/filepath" + "sort" "strconv" + "strings" ) const ( - chromeDriverBinary = "chromedriver" + chromeDriverBinary = "chromedriver" + newChromeDriverBinary = "chromedriver-linux64/chromedriver" ) type Chrome struct { @@ -25,7 +31,12 @@ func (c *Chrome) Build() error { pkgTagVersion := extractVersion(pkgVersion) - driverVersion, err := c.parseChromeDriverVersion(pkgTagVersion) + chromeDriverVersions, err := fetchChromeDriverVersions() + if err != nil { + return fmt.Errorf("fetch chromedriver versions: %v", err) + } + + driverVersion, err := c.parseChromeDriverVersion(pkgTagVersion, chromeDriverVersions) if err != nil { return fmt.Errorf("parse chromedriver version: %v", err) } @@ -82,7 +93,7 @@ func (c *Chrome) Build() error { } image.BuildArgs = append(image.BuildArgs, fmt.Sprintf("VERSION=%s", pkgTagVersion)) - err = c.downloadChromeDriver(image.Dir, driverVersion) + err = c.downloadChromeDriver(image.Dir, driverVersion, chromeDriverVersions) if err != nil { return fmt.Errorf("failed to download chromedriver: %v", err) } @@ -117,25 +128,60 @@ func (c *Chrome) channelToBuildArgs() []string { } } -func (c *Chrome) parseChromeDriverVersion(pkgVersion string) (string, error) { +func (c *Chrome) parseChromeDriverVersion(pkgVersion string, chromeDriverVersions map[string]string) (string, error) { version := c.DriverVersion if version == LatestVersion { + + var matchingVersions []string + for mv := range chromeDriverVersions { + if strings.Contains(mv, pkgVersion) { + matchingVersions = append(matchingVersions, mv) + } + } + if len(matchingVersions) > 0 { + sort.SliceStable(matchingVersions, func(i, j int) bool { + l := matchingVersions[i] + r := matchingVersions[j] + lv, err := hv.NewVersion(l) + if err != nil { + return false + } + rv, err := hv.NewVersion(r) + if err != nil { + return false + } + return lv.LessThan(rv) + }) + return matchingVersions[0], nil + } + const baseUrl = "https://chromedriver.storage.googleapis.com/" v, err := c.getLatestChromeDriver(baseUrl, pkgVersion) if err != nil { return "", err } - version = v + return v, nil } return version, nil } -func (c *Chrome) downloadChromeDriver(dir string, version string) error { - u := fmt.Sprintf("http://chromedriver.storage.googleapis.com/%s/chromedriver_linux64.zip", version) - _, err := downloadDriver(u, chromeDriverBinary, dir) +func (c *Chrome) downloadChromeDriver(dir string, version string, chromeDriverVersions map[string]string) error { + u := fmt.Sprintf("https://chromedriver.storage.googleapis.com/%s/chromedriver_linux64.zip", version) + fn := chromeDriverBinary + if cdu, ok := chromeDriverVersions[version]; ok { + u = cdu + fn = newChromeDriverBinary + } + outputPath, err := downloadDriver(u, fn, dir) if err != nil { return fmt.Errorf("download chromedriver: %v", err) } + if fn == newChromeDriverBinary { + err = os.Rename(outputPath, filepath.Join(dir, chromeDriverBinary)) + if err != nil { + return fmt.Errorf("rename chromedriver: %v", err) + } + } return nil } @@ -176,3 +222,46 @@ func (c *Chrome) getLatestChromeDriver(baseUrl string, pkgVersion string) (strin } } } + +func fetchChromeDriverVersions() (map[string]string, error) { + const versionsURL = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json" + resp, err := http.Get(versionsURL) + if err != nil { + return nil, fmt.Errorf("fetch chrome versions: %v", err) + } + defer resp.Body.Close() + var cv ChromeVersions + err = json.NewDecoder(resp.Body).Decode(&cv) + if err != nil { + return nil, fmt.Errorf("decode json: %v", err) + } + ret := make(map[string]string) + const platformLinux64 = "linux64" + const chromeDriver = "chromedriver" + for _, v := range cv.Versions { + version := v.Version + if cd, ok := v.Downloads[chromeDriver]; ok { + for _, d := range cd { + u := d.URL + if u != "" && d.Platform == platformLinux64 { + ret[version] = u + } + } + } + } + return ret, nil +} + +type ChromeVersions struct { + Versions []ChromeVersion `json:"versions"` +} + +type ChromeVersion struct { + Version string `json:"version"` + Downloads map[string][]ChromeDownload `json:"downloads"` +} + +type ChromeDownload struct { + Platform string `json:"platform"` + URL string `json:"url"` +} diff --git a/go.mod b/go.mod index 1553a18bf..d9a4da1ac 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( gopkg.in/cheggaaa/pb.v1 v1.0.28 ) +require github.com/hashicorp/go-version v1.6.0 + require ( github.com/gobuffalo/here v0.6.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect diff --git a/go.sum b/go.sum index d0baf844f..1806dc46a 100644 --- a/go.sum +++ b/go.sum @@ -87,6 +87,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=