Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ GO_SYSTEM_VERSION := $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.'
REQUIRE_GO_VERSION := $(GO_VERSION)

# Project version
WEAVE_VERSION := $(shell git describe --tags)
WEAVE_VERSION := $(shell git describe --tags 2>/dev/null || echo v0.0.0-dev)

# Build directory
BUILDDIR ?= $(CURDIR)/build
Expand Down
122 changes: 96 additions & 26 deletions cosmosutils/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func filterPreReleases(releases []BinaryRelease) []BinaryRelease {
func fetchReleases(url string) ([]BinaryRelease, error) {
httpClient := client.NewHTTPClient()
var releases []BinaryRelease
_, err := httpClient.Get(url, "", nil, &releases)
_, err := httpClient.Get(url, "", map[string]string{"per_page": "100"}, &releases)
if err != nil {
return nil, fmt.Errorf("failed to fetch releases: %v", err)
}
Expand Down Expand Up @@ -233,6 +233,94 @@ func GetMinitiadBinaryUrlFromLcd(httpClient *client.HTTPClient, rest string) (vm
return vm, version, url, nil
}

func getPlatformAssetURL(release BinaryRelease) (string, bool, error) {
goos, arch, err := getOSArch()
if err != nil {
return "", false, err
}

searchString := fmt.Sprintf("%s_%s.tar.gz", goos, arch)
for _, asset := range release.Assets {
if strings.Contains(asset.BrowserDownloadURL, searchString) {
return asset.BrowserDownloadURL, true, nil
}
}

return "", false, nil
}

func majorMinorVersion(version string) (string, error) {
version = strings.TrimPrefix(version, "v")
mainVersion, _ := splitVersion(version)
parts := strings.Split(mainVersion, ".")
if len(parts) < 2 {
return "", fmt.Errorf("invalid semantic version: %s", version)
}
return fmt.Sprintf("%s.%s", parts[0], parts[1]), nil
}

func selectCompatibleReleaseForVersion(releases []BinaryRelease, targetVersion string) (string, string, error) {
if len(releases) < 1 {
return "", "", fmt.Errorf("no releases found")
}

targetVersion = normalizeVersion(targetVersion)
if !semverPattern.MatchString(targetVersion) {
return "", "", fmt.Errorf("invalid version format after normalization: %q", targetVersion)
}

for _, release := range releases {
if release.TagName != targetVersion {
continue
}

downloadURL, ok, err := getPlatformAssetURL(release)
if err != nil {
return "", "", err
}
if ok {
return release.TagName, downloadURL, nil
}
}

targetSeries, err := majorMinorVersion(targetVersion)
if err != nil {
return "", "", err
}

var selectedRelease *BinaryRelease
var selectedURL string
for _, release := range releases {
if !semverPattern.MatchString(release.TagName) {
continue
}

releaseSeries, err := majorMinorVersion(release.TagName)
if err != nil || releaseSeries != targetSeries {
continue
}

downloadURL, ok, err := getPlatformAssetURL(release)
if err != nil {
return "", "", err
}
if !ok {
continue
}

if selectedRelease == nil || CompareSemVer(release.TagName, selectedRelease.TagName) {
selectedRelease = &release
selectedURL = downloadURL
}
}

if selectedRelease != nil {
return selectedRelease.TagName, selectedURL, nil
}

return "", "", fmt.Errorf("no compatible downloadable release found for chain version %s", targetVersion)
}

func detectMinitiaVM(versionString string) string {
lower := strings.ToLower(versionString)
switch {
Expand Down Expand Up @@ -502,35 +590,17 @@ func GetInitiaBinaryUrlFromLcd(httpClient *client.HTTPClient, rest string) (stri
}

version := normalizeVersion(result.ApplicationVersion.Version)
url, err := getBinaryURL(version)
releases, err := fetchReleases("https://api.github.com/repos/initia-labs/initia/releases")
if err != nil {
return "", "", err
return "", "", fmt.Errorf("failed to fetch initia releases: %w", err)
}

return version, url, nil
}

func getBinaryURL(version string) (string, error) {
goos := runtime.GOOS
goarch := runtime.GOARCH

switch goos {
case "darwin":
switch goarch {
case "amd64":
return fmt.Sprintf("https://github.com/initia-labs/initia/releases/download/%s/initia_%s_Darwin_x86_64.tar.gz", version, version), nil
case "arm64":
return fmt.Sprintf("https://github.com/initia-labs/initia/releases/download/%s/initia_%s_Darwin_aarch64.tar.gz", version, version), nil
}
case "linux":
switch goarch {
case "amd64":
return fmt.Sprintf("https://github.com/initia-labs/initia/releases/download/%s/initia_%s_Linux_x86_64.tar.gz", version, version), nil
case "arm64":
return fmt.Sprintf("https://github.com/initia-labs/initia/releases/download/%s/initia_%s_Linux_aarch64.tar.gz", version, version), nil
}
selectedVersion, url, err := selectCompatibleReleaseForVersion(releases, version)
if err != nil {
return "", "", err
}
return "", fmt.Errorf("unsupported OS or architecture: %v %v", goos, goarch)

return selectedVersion, url, nil
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

func GetInitiaBinaryPath(version string) (string, error) {
Expand Down
110 changes: 110 additions & 0 deletions cosmosutils/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,116 @@ func TestGetLatestVersionFromReleases(t *testing.T) {
}
}

func TestSelectCompatibleReleaseForVersion(t *testing.T) {
currentOS, currentArch, err := getOSArch()
if err != nil {
t.Fatalf("Failed to get OS/arch: %v", err)
}

assetURL := func(version string) string {
return fmt.Sprintf("example.com/initia_%s_%s_%s.tar.gz", version, currentOS, currentArch)
}

tests := []struct {
name string
releases []BinaryRelease
targetVersion string
expectedTag string
expectedURL string
expectedErrStr string
}{
{
name: "uses exact matching release when available",
targetVersion: "v1.3.1",
releases: []BinaryRelease{
{
TagName: "v1.3.1",
Assets: []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}{
{BrowserDownloadURL: assetURL("v1.3.1")},
},
},
},
expectedTag: "v1.3.1",
expectedURL: assetURL("v1.3.1"),
},
{
name: "falls back to highest patch in same minor series",
targetVersion: "v1.4.1",
releases: []BinaryRelease{
{
TagName: "v1.3.9",
Assets: []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}{
{BrowserDownloadURL: assetURL("v1.3.9")},
},
},
{
TagName: "v1.4.0",
Assets: []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}{
{BrowserDownloadURL: assetURL("v1.4.0")},
},
},
{
TagName: "v1.4.2",
Assets: []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}{
{BrowserDownloadURL: assetURL("v1.4.2")},
},
},
},
expectedTag: "v1.4.2",
expectedURL: assetURL("v1.4.2"),
},
{
name: "fails when no compatible downloadable release exists",
targetVersion: "v1.4.1",
releases: []BinaryRelease{
{
TagName: "v1.3.9",
Assets: []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}{
{BrowserDownloadURL: assetURL("v1.3.9")},
},
},
},
expectedErrStr: "no compatible downloadable release found for chain version v1.4.1",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tag, url, err := selectCompatibleReleaseForVersion(tt.releases, tt.targetVersion)

if tt.expectedErrStr != "" {
if err == nil {
t.Fatalf("expected error containing %q, got nil", tt.expectedErrStr)
}
if !strings.Contains(err.Error(), tt.expectedErrStr) {
t.Fatalf("expected error containing %q, got %q", tt.expectedErrStr, err.Error())
}
return
}

if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tag != tt.expectedTag {
t.Fatalf("expected tag %q, got %q", tt.expectedTag, tag)
}
if url != tt.expectedURL {
t.Fatalf("expected URL %q, got %q", tt.expectedURL, url)
}
})
}
}

func TestNormalizeVersion(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading