grafana/pkg/build/versions/version.go
2023-01-23 11:32:51 +01:00

161 lines
3.6 KiB
Go

package versions
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"regexp"
"strconv"
"github.com/Masterminds/semver/v3"
)
var (
reGrafanaTag = regexp.MustCompile(`^v(\d+\.\d+\.\d+$)`)
reGrafanaTagBeta = regexp.MustCompile(`^v(\d+\.\d+\.\d+-beta)`)
reGrafanaTagCustom = regexp.MustCompile(`^v(\d+\.\d+\.\d+-\w+)`)
)
const (
Latest = "latest"
Next = "next"
Test = "test"
)
type Version struct {
Version string
Channel string
}
type VersionFromAPI struct {
Version string `json:"version"`
}
type LatestGcomAPI = string
const (
LatestStableVersionURL LatestGcomAPI = "https://grafana.com/api/grafana/versions/stable"
LatestBetaVersionURL LatestGcomAPI = "https://grafana.com/api/grafana/versions/beta"
)
func GetLatestVersion(url LatestGcomAPI) (string, error) {
// nolint:gosec
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Printf("Failed to close body: %s", err.Error())
}
}()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("server returned non 200 status code: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var apiResponse VersionFromAPI
err = json.Unmarshal(body, &apiResponse)
if err != nil {
return "", err
}
return apiResponse.Version, nil
}
// IsGreaterThanOrEqual semantically checks whether newVersion is greater than or equal to stableVersion.
func IsGreaterThanOrEqual(newVersion, stableVersion string) (bool, error) {
v1SemVer, err := semver.NewVersion(newVersion)
if err != nil {
return isGreaterThanOrEqualFourDigit(newVersion, stableVersion)
}
v2SemVer, err := semver.NewVersion(stableVersion)
if err != nil {
return isGreaterThanOrEqualFourDigit(newVersion, stableVersion)
}
comp := v1SemVer.Compare(v2SemVer)
switch comp {
case -1:
return false, nil
case 1, 0:
return true, nil
default:
return true, fmt.Errorf("unknown comparison value between scemantic versions, err: %q", err)
}
}
var fourDigitRe = regexp.MustCompile(`(\d+\.\d+\.\d+)\.(\d+)`)
func parseFourDigit(version string) (*semver.Version, int, error) {
matches := fourDigitRe.FindStringSubmatch(version)
if len(matches) < 2 {
semVer, err := semver.NewVersion(version)
if err != nil {
return nil, 0, err
}
return semVer, 0, nil
}
semVer, err := semver.NewVersion(matches[1])
if err != nil {
return nil, 0, err
}
i, err := strconv.Atoi(matches[2])
if err != nil {
return nil, 0, err
}
return semVer, i, nil
}
func isGreaterThanOrEqualFourDigit(newVersion, stableVersion string) (bool, error) {
newVersionSemVer, newVersionSemVerNo, err := parseFourDigit(newVersion)
if err != nil {
return false, err
}
stableVersionSemVer, stableVersionSemVerNo, err := parseFourDigit(stableVersion)
if err != nil {
return false, err
}
if stableVersionSemVer.Original() != newVersionSemVer.Original() {
return IsGreaterThanOrEqual(newVersionSemVer.Original(), stableVersionSemVer.Original())
}
return newVersionSemVerNo >= stableVersionSemVerNo, nil
}
func GetVersion(tag string) (*Version, error) {
var version Version
switch {
case reGrafanaTag.MatchString(tag):
version = Version{
Version: reGrafanaTag.FindStringSubmatch(tag)[1],
Channel: Latest,
}
case reGrafanaTagBeta.MatchString(tag):
version = Version{
Version: reGrafanaTagBeta.FindStringSubmatch(tag)[1],
Channel: Next,
}
case reGrafanaTagCustom.MatchString(tag):
version = Version{
Version: reGrafanaTagCustom.FindStringSubmatch(tag)[1],
Channel: Test,
}
default:
return nil, fmt.Errorf("%s not a supported Grafana version, exitting", tag)
}
return &version, nil
}