217 lines
5.1 KiB
Go
217 lines
5.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/coreos/go-semver/semver"
|
||
|
"golang.org/x/net/html"
|
||
|
)
|
||
|
|
||
|
type FileInfo struct {
|
||
|
FileName string
|
||
|
Regex string
|
||
|
Replace string
|
||
|
}
|
||
|
|
||
|
func (f FileInfo) Update() error {
|
||
|
b, err := os.ReadFile(f.FileName)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
re := regexp.MustCompile(f.Regex)
|
||
|
newContents := re.ReplaceAll(b, []byte(f.Replace))
|
||
|
|
||
|
err = os.WriteFile(f.FileName, newContents, 0640)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// removePatch cleans version from "1.20.1" to "1.20" (think go.mod entry)
|
||
|
func removePatch(version string) string {
|
||
|
verInfo := semver.New(version)
|
||
|
return fmt.Sprintf("%d.%d", verInfo.Major, verInfo.Minor)
|
||
|
}
|
||
|
|
||
|
// findHash will search the downloads table for the hashes matching the artifacts list
|
||
|
func findHashes(body io.Reader, version string) (map[string]string, error) {
|
||
|
htmlTokens := html.NewTokenizer(body)
|
||
|
artifacts := []string{
|
||
|
fmt.Sprintf("go%s.linux-amd64.tar.gz", version),
|
||
|
fmt.Sprintf("go%s.darwin-arm64.tar.gz", version),
|
||
|
fmt.Sprintf("go%s.darwin-amd64.tar.gz", version),
|
||
|
}
|
||
|
|
||
|
var insideDownloadTable bool
|
||
|
var currentRow string
|
||
|
hashes := make(map[string]string)
|
||
|
|
||
|
for {
|
||
|
tokenType := htmlTokens.Next()
|
||
|
|
||
|
// if it's an error token, we either reached
|
||
|
// the end of the file, or the HTML was malformed
|
||
|
if tokenType == html.ErrorToken {
|
||
|
err := htmlTokens.Err()
|
||
|
if errors.Is(err, io.EOF) {
|
||
|
// end of the file, break out of the loop
|
||
|
break
|
||
|
}
|
||
|
return nil, htmlTokens.Err()
|
||
|
}
|
||
|
|
||
|
if tokenType == html.StartTagToken {
|
||
|
// get the token
|
||
|
token := htmlTokens.Token()
|
||
|
if token.Data == "table" && len(token.Attr) == 1 && token.Attr[0].Val == "downloadtable" {
|
||
|
insideDownloadTable = true
|
||
|
}
|
||
|
|
||
|
if insideDownloadTable && token.Data == "a" && len(token.Attr) == 2 {
|
||
|
for _, f := range artifacts {
|
||
|
// Check if the current row matches a desired file
|
||
|
if strings.Contains(token.Attr[1].Val, f) {
|
||
|
currentRow = f
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if currentRow != "" && token.Data == "tt" {
|
||
|
// the next token should be the page title
|
||
|
tokenType = htmlTokens.Next()
|
||
|
// just make sure it's actually a text token
|
||
|
if tokenType == html.TextToken {
|
||
|
hashes[currentRow] = htmlTokens.Token().Data
|
||
|
currentRow = ""
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Found a hash for each filename
|
||
|
if len(hashes) == len(artifacts) {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
// Reached end of table
|
||
|
if tokenType == html.EndTagToken && htmlTokens.Token().Data == "table" {
|
||
|
if len(hashes) == 0 {
|
||
|
return nil, fmt.Errorf("could not find version %q on downloads page", version)
|
||
|
}
|
||
|
|
||
|
return nil, fmt.Errorf("only found %d hashes expected %d: %v", len(hashes), len(artifacts), hashes)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hashes, nil
|
||
|
}
|
||
|
|
||
|
func getHashes(version string) (map[string]string, error) {
|
||
|
resp, err := http.Get(`https://go.dev/dl/`)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
return findHashes(resp.Body, version)
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
version := os.Args[1]
|
||
|
// Handle situation user accidentally provides version as "v1.19.2"
|
||
|
if strings.HasPrefix(version, "v") {
|
||
|
version = strings.TrimLeft(version, "v")
|
||
|
}
|
||
|
|
||
|
hashes, err := getHashes(version)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
for file, hash := range hashes {
|
||
|
fmt.Printf("%s %s\n", hash, file)
|
||
|
}
|
||
|
|
||
|
noPatchVersion := removePatch(version)
|
||
|
|
||
|
files := []FileInfo{
|
||
|
{
|
||
|
FileName: ".circleci/config.yml",
|
||
|
Regex: `(quay\.io\/influxdb\/telegraf-ci):(\d.\d*.\d)`,
|
||
|
Replace: "$1:" + version,
|
||
|
},
|
||
|
{
|
||
|
FileName: "go.mod",
|
||
|
Regex: `(go)\s(\d.\d*)`,
|
||
|
Replace: "$1 " + noPatchVersion,
|
||
|
},
|
||
|
{
|
||
|
FileName: "Makefile",
|
||
|
Regex: `(quay\.io\/influxdb\/telegraf-ci):(\d.\d*.\d)`,
|
||
|
Replace: "$1:" + version,
|
||
|
},
|
||
|
{
|
||
|
FileName: "README.md",
|
||
|
Regex: `(Telegraf requires Go version) (\d.\d*)`,
|
||
|
Replace: "$1 " + noPatchVersion,
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/ci.docker",
|
||
|
Regex: `(FROM golang):(\d.\d*.\d)`,
|
||
|
Replace: "$1:" + version,
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_linux.sh",
|
||
|
Regex: `(GO_VERSION)=("\d.\d*.\d")`,
|
||
|
Replace: fmt.Sprintf("$1=%q", version),
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_mac.sh",
|
||
|
Regex: `(GO_VERSION)=("\d.\d*.\d")`,
|
||
|
Replace: fmt.Sprintf("$1=%q", version),
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_windows.sh",
|
||
|
Regex: `(GO_VERSION)=("\d.\d*.\d")`,
|
||
|
Replace: fmt.Sprintf("$1=%q", version),
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_linux.sh",
|
||
|
Regex: `(GO_VERSION_SHA)=".*"`,
|
||
|
Replace: fmt.Sprintf("$1=%q", hashes[fmt.Sprintf("go%s.linux-amd64.tar.gz", version)]),
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_mac.sh",
|
||
|
Regex: `(GO_VERSION_SHA_arm64)=".*"`,
|
||
|
Replace: fmt.Sprintf("$1=%q", hashes[fmt.Sprintf("go%s.darwin-arm64.tar.gz", version)]),
|
||
|
},
|
||
|
{
|
||
|
FileName: "scripts/installgo_mac.sh",
|
||
|
Regex: `(GO_VERSION_SHA_amd64)=".*"`,
|
||
|
Replace: fmt.Sprintf("$1=%q", hashes[fmt.Sprintf("go%s.darwin-amd64.tar.gz", version)]),
|
||
|
},
|
||
|
{
|
||
|
FileName: ".github/workflows/readme-linter.yml",
|
||
|
Regex: `(go-version): '\d.\d*.\d'`,
|
||
|
Replace: fmt.Sprintf("$1: '%s'", version),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, f := range files {
|
||
|
fmt.Printf("Updating %s \n", f.FileName)
|
||
|
err := f.Update()
|
||
|
if err != nil {
|
||
|
log.Panic(err)
|
||
|
}
|
||
|
}
|
||
|
}
|