mirror of
https://gh-proxy.org/https://github.com/klinsmaya/CLIProxyAPI.git
synced 2026-11-08 17:36:47 +08:00
Merge pull request #3773 from sususu98/fix/antigravity-version-upstream-dev
fix: update antigravity version lookup
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user