diff --git a/cmd/builder-bumper/main.go b/cmd/builder-bumper/main.go index aa24f2b..f1d5631 100644 --- a/cmd/builder-bumper/main.go +++ b/cmd/builder-bumper/main.go @@ -16,6 +16,7 @@ package main import ( "bufio" + "encoding/json" "flag" "fmt" "io" @@ -46,6 +47,10 @@ type goVersion struct { minor int } +type VersionInfo struct { + Version string `json:"version"` +} + func newGoVersion(v string) *goVersion { c := semver.Canonical("v" + v) if c == "" { @@ -101,6 +106,47 @@ func (g *goVersion) url() string { return fmt.Sprintf("https://dl.google.com/go/go%s.linux-amd64.tar.gz", g.golangVersion()) } +func fetchJSON(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +func availableVersions() []goVersion { + + const url = "https://go.dev/dl/?mode=json" + + jsonData, err := fetchJSON(url) + if err != nil { + fmt.Println("Error fetching JSON:", err) + } + + var availableVersionsJSON []VersionInfo + var availableVersions []goVersion + if err := json.Unmarshal(jsonData, &availableVersionsJSON); err != nil { + fmt.Println("Error parsing JSON:", err) + } + for i := range availableVersionsJSON { + // remove "go" from a string like "go1.22.0" + newGoVersion := newGoVersion(strings.TrimLeft("go", availableVersionsJSON[i].Version)) + availableVersions = append(availableVersions, *newGoVersion) + } + return availableVersions +} + // getSHA256 returns the SHA256 of the Go archive. func (g *goVersion) getSHA256() (string, error) { resp, err := http.Get(g.url() + ".sha256") @@ -116,40 +162,34 @@ func (g *goVersion) getSHA256() (string, error) { } // getLastMinorVersion returns the last minor version for a given Go version. -func (g *goVersion) getLastMinorVersion() (*goVersion, error) { +// if no new minor version available, it will return the given Go version back. +func (g *goVersion) getLastMinorVersion(availableVersions []goVersion) *goVersion { last := *g - for { - next := last + next := last + // let's check max 40 minor revisions for now before giving up + for next.minor < 40 { next.minor++ - resp, err := http.Head(next.url()) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode/100 != 2 { - return &last, nil + for _, availableVersion := range availableVersions { + if next == availableVersion { + return &next + } } - last = next } + return &last } // getNextMajor returns the next Go major version for a given Go version. // It returns nil if the current version is already the latest. -func (g *goVersion) getNextMajor() *goVersion { +func (g *goVersion) getNextMajor(availableVersions []goVersion) *goVersion { version := newGoVersion(g.Major() + ".0") version.major++ - resp, err := http.Head(version.url()) - if err != nil { - return nil - } - defer resp.Body.Close() - if resp.StatusCode/100 != 2 { - return nil + for _, availableVersion := range availableVersions { + if version.major == availableVersion.major { + return version + } } - - return version + return nil } // getExactVersionFromDir reads the current Go version from a directory. @@ -301,10 +341,8 @@ func updateNextMinor(dir string) (*goVersion, error) { return nil, fmt.Errorf("failed to detect current version of %s: %w", dir, err) } - next, err := current.getLastMinorVersion() - if err != nil { - return nil, err - } + next := current.getLastMinorVersion(availableVersions()) + if next.equal(current) { log.Printf("no version change for Go %s", next.golangVersion()) return nil, nil @@ -374,9 +412,12 @@ func run() error { return fmt.Errorf("Expected 2 versions of Go but got %d\n", len(dirs)) } + // Get list of available versions + availableVersions := availableVersions() + // Check if a new major Go version exists. nexts := make([]*goVersion, 0) - if next := newGoVersion(dirs[1] + ".0").getNextMajor(); next != nil { + if next := newGoVersion(dirs[1] + ".0").getNextMajor(availableVersions); next != nil { log.Printf("found a new major version of Go: %s", next) old, err := getExactVersionFromDir(dirs[0]) if err != nil {