fix: update antigravity version lookup

This commit is contained in:
sususu98
2026-06-09 11:32:57 +08:00
parent fabf06154f
commit c7cc3a1a4d
4 changed files with 339 additions and 3 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ var (
type LogFormatter struct{}
// logFieldOrder defines the display order for common log fields.
var logFieldOrder = []string{"provider", "model", "mode", "budget", "level", "original_mode", "original_value", "min", "max", "clamped_to", "error"}
var logFieldOrder = []string{"provider", "model", "version", "mode", "budget", "level", "original_mode", "original_value", "min", "max", "clamped_to", "error"}
// Format renders a single log entry with custom formatting.
func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
+27
View File
@@ -0,0 +1,27 @@
package logging
import (
"strings"
"testing"
"time"
log "github.com/sirupsen/logrus"
)
func TestLogFormatterPrintsVersionField(t *testing.T) {
entry := log.NewEntry(log.New())
entry.Time = time.Date(2026, 6, 9, 11, 10, 2, 0, time.Local)
entry.Level = log.InfoLevel
entry.Message = "fetched latest antigravity version"
entry.Data["version"] = "2.1.0"
formatted, errFormat := (&LogFormatter{}).Format(entry)
if errFormat != nil {
t.Fatalf("Format() error = %v", errFormat)
}
line := string(formatted)
if !strings.Contains(line, "version=2.1.0") {
t.Fatalf("formatted line %q missing version field", line)
}
}
+163 -2
View File
@@ -4,9 +4,11 @@ package misc
import (
"context"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"time"
@@ -15,19 +17,36 @@ import (
)
const (
antigravityReleasesURL = "https://antigravity-auto-updater-974169037036.us-central1.run.app/releases"
antigravityFallbackVersion = "1.21.9"
antigravityFallbackVersion = "2.1.0"
antigravityVersionCacheTTL = 6 * time.Hour
antigravityFetchTimeout = 10 * time.Second
AntigravityNodeAPIClientUA = "google-api-nodejs-client/10.3.0"
AntigravityGoogAPIClientUA = "gl-node/22.21.1"
)
var (
antigravityHubGCSListURL = "https://storage.googleapis.com/antigravity-public/?prefix=antigravity-hub/&delimiter=/"
antigravityReleasesURL = "https://antigravity-auto-updater-974169037036.us-central1.run.app/releases"
)
type antigravityRelease struct {
Version string `json:"version"`
ExecutionID string `json:"execution_id"`
}
type antigravityHubGCSList struct {
CommonPrefixes []antigravityHubGCSPrefix `xml:"CommonPrefixes"`
}
type antigravityHubGCSPrefix struct {
Prefix string `xml:"Prefix"`
}
type antigravitySemVersion struct {
raw string
parts [3]int
}
var (
cachedAntigravityVersion = antigravityFallbackVersion
antigravityVersionMu sync.RWMutex
@@ -176,6 +195,55 @@ func fetchAntigravityLatestVersion(ctx context.Context) (string, error) {
client := &http.Client{Timeout: antigravityFetchTimeout}
version, errHub := fetchAntigravityHubGCSLatestVersion(ctx, client)
if errHub == nil {
return version, nil
}
log.WithError(errHub).Debug("failed to fetch antigravity hub GCS version, trying legacy releases API")
version, errLegacy := fetchAntigravityLegacyLatestVersion(ctx, client)
if errLegacy == nil {
return version, nil
}
return "", fmt.Errorf("fetch antigravity hub GCS version: %v; fetch legacy releases: %w", errHub, errLegacy)
}
func fetchAntigravityHubGCSLatestVersion(ctx context.Context, client *http.Client) (string, error) {
httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodGet, antigravityHubGCSListURL, nil)
if errReq != nil {
return "", fmt.Errorf("build antigravity hub GCS request: %w", errReq)
}
resp, errDo := client.Do(httpReq)
if errDo != nil {
return "", fmt.Errorf("fetch antigravity hub GCS list: %w", errDo)
}
defer func() {
if errClose := resp.Body.Close(); errClose != nil {
log.WithError(errClose).Warn("antigravity hub GCS response body close error")
}
}()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("antigravity hub GCS list returned status %d", resp.StatusCode)
}
var list antigravityHubGCSList
if errDecode := xml.NewDecoder(resp.Body).Decode(&list); errDecode != nil {
return "", fmt.Errorf("decode antigravity hub GCS list: %w", errDecode)
}
prefixes := make([]string, 0, len(list.CommonPrefixes))
for _, commonPrefix := range list.CommonPrefixes {
prefixes = append(prefixes, commonPrefix.Prefix)
}
return latestAntigravityHubVersionFromPrefixes(prefixes)
}
func fetchAntigravityLegacyLatestVersion(ctx context.Context, client *http.Client) (string, error) {
httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodGet, antigravityReleasesURL, nil)
if errReq != nil {
return "", fmt.Errorf("build antigravity releases request: %w", errReq)
@@ -211,3 +279,96 @@ func fetchAntigravityLatestVersion(ctx context.Context) (string, error) {
return version, nil
}
func latestAntigravityHubVersionFromPrefixes(prefixes []string) (string, error) {
var best antigravitySemVersion
found := false
for _, prefix := range prefixes {
version, ok := antigravityHubVersionFromPrefix(prefix)
if !ok {
continue
}
semVersion, ok := parseAntigravitySemVersion(version)
if !ok {
continue
}
if !found || compareAntigravitySemVersion(semVersion, best) > 0 {
best = semVersion
found = true
}
}
if !found {
return "", errors.New("antigravity hub GCS list contained no version prefixes")
}
return best.raw, nil
}
func antigravityHubVersionFromPrefix(prefix string) (string, bool) {
const hubPrefix = "antigravity-hub/"
prefix = strings.TrimSpace(prefix)
prefix = strings.TrimSuffix(prefix, "/")
if !strings.HasPrefix(prefix, hubPrefix) {
return "", false
}
name := strings.TrimPrefix(prefix, hubPrefix)
separator := strings.LastIndex(name, "-")
if separator <= 0 || separator == len(name)-1 {
return "", false
}
version := strings.TrimSpace(name[:separator])
executionID := name[separator+1:]
if version == "" || executionID == "" {
return "", false
}
for _, ch := range executionID {
if ch < '0' || ch > '9' {
return "", false
}
}
return version, true
}
func parseAntigravitySemVersion(version string) (antigravitySemVersion, bool) {
parts := strings.Split(version, ".")
if len(parts) != 3 {
return antigravitySemVersion{}, false
}
semVersion := antigravitySemVersion{raw: version}
for i, part := range parts {
if part == "" {
return antigravitySemVersion{}, false
}
for _, ch := range part {
if ch < '0' || ch > '9' {
return antigravitySemVersion{}, false
}
}
value, errParse := strconv.Atoi(part)
if errParse != nil {
return antigravitySemVersion{}, false
}
semVersion.parts[i] = value
}
return semVersion, true
}
func compareAntigravitySemVersion(left antigravitySemVersion, right antigravitySemVersion) int {
for i := range left.parts {
if left.parts[i] > right.parts[i] {
return 1
}
if left.parts[i] < right.parts[i] {
return -1
}
}
return 0
}
+148
View File
@@ -0,0 +1,148 @@
package misc
import (
"context"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"time"
)
func overrideAntigravityVersionURLsForTest(t *testing.T, hubURL string, legacyURL string) func() {
t.Helper()
oldHubURL := antigravityHubGCSListURL
oldLegacyURL := antigravityReleasesURL
antigravityHubGCSListURL = hubURL
antigravityReleasesURL = legacyURL
return func() {
antigravityHubGCSListURL = oldHubURL
antigravityReleasesURL = oldLegacyURL
}
}
func overrideAntigravityVersionCacheForTest(t *testing.T, version string, expiry time.Time) func() {
t.Helper()
antigravityVersionMu.Lock()
oldVersion := cachedAntigravityVersion
oldExpiry := antigravityVersionExpiry
cachedAntigravityVersion = version
antigravityVersionExpiry = expiry
antigravityVersionMu.Unlock()
return func() {
antigravityVersionMu.Lock()
cachedAntigravityVersion = oldVersion
antigravityVersionExpiry = oldExpiry
antigravityVersionMu.Unlock()
}
}
func TestAntigravityLatestVersionUsesCurrentHubFallback(t *testing.T) {
restore := overrideAntigravityVersionCacheForTest(t, "", time.Time{})
defer restore()
version := AntigravityLatestVersion()
if version != "2.1.0" {
t.Fatalf("AntigravityLatestVersion() = %q, want %q", version, "2.1.0")
}
}
func TestFetchAntigravityLatestVersionPrefersHubGCSList(t *testing.T) {
var legacyRequests atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/gcs":
w.Header().Set("Content-Type", "application/xml")
_, _ = w.Write([]byte(`<?xml version='1.0' encoding='UTF-8'?>
<ListBucketResult xmlns='http://doc.s3.amazonaws.com/2006-03-01'>
<CommonPrefixes><Prefix>antigravity-hub/2.0.9-4666288509943808/</Prefix></CommonPrefixes>
<CommonPrefixes><Prefix>antigravity-hub/2.0.11-6560309696135168/</Prefix></CommonPrefixes>
<CommonPrefixes><Prefix>antigravity-hub/2.1.0-6066040229199872/</Prefix></CommonPrefixes>
</ListBucketResult>`))
case "/legacy":
legacyRequests.Add(1)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[{"version":"9.9.9","execution_id":"1"}]`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
restore := overrideAntigravityVersionURLsForTest(t, server.URL+"/gcs", server.URL+"/legacy")
defer restore()
version, errFetch := fetchAntigravityLatestVersion(context.Background())
if errFetch != nil {
t.Fatalf("fetchAntigravityLatestVersion() error = %v", errFetch)
}
if version != "2.1.0" {
t.Fatalf("fetchAntigravityLatestVersion() = %q, want %q", version, "2.1.0")
}
if got := legacyRequests.Load(); got != 0 {
t.Fatalf("legacy releases API requests = %d, want 0", got)
}
}
func TestFetchAntigravityLatestVersionFallsBackToLegacyReleases(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/gcs":
http.Error(w, "temporary outage", http.StatusInternalServerError)
case "/legacy":
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[{"version":"2.0.0","execution_id":"6324554176528384"}]`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
restore := overrideAntigravityVersionURLsForTest(t, server.URL+"/gcs", server.URL+"/legacy")
defer restore()
version, errFetch := fetchAntigravityLatestVersion(context.Background())
if errFetch != nil {
t.Fatalf("fetchAntigravityLatestVersion() error = %v", errFetch)
}
if version != "2.0.0" {
t.Fatalf("fetchAntigravityLatestVersion() = %q, want %q", version, "2.0.0")
}
}
func TestLatestAntigravityHubVersionFromPrefixesSortsByNumericSemver(t *testing.T) {
prefixes := []string{
"antigravity-hub/2.0.9-4666288509943808/",
"antigravity-hub/2.0.10-5119448496078848/",
"antigravity-hub/2.0.11-6560309696135168/",
"antigravity-hub/not-a-version/",
}
version, errParse := latestAntigravityHubVersionFromPrefixes(prefixes)
if errParse != nil {
t.Fatalf("latestAntigravityHubVersionFromPrefixes() error = %v", errParse)
}
if version != "2.0.11" {
t.Fatalf("latestAntigravityHubVersionFromPrefixes() = %q, want %q", version, "2.0.11")
}
}
func TestLatestAntigravityHubVersionFromPrefixesIgnoresSignedVersionParts(t *testing.T) {
prefixes := []string{
"antigravity-hub/9.+9.9-4666288509943808/",
"antigravity-hub/2.1.0-6066040229199872/",
}
version, errParse := latestAntigravityHubVersionFromPrefixes(prefixes)
if errParse != nil {
t.Fatalf("latestAntigravityHubVersionFromPrefixes() error = %v", errParse)
}
if version != "2.1.0" {
t.Fatalf("latestAntigravityHubVersionFromPrefixes() = %q, want %q", version, "2.1.0")
}
}