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
25 changes: 18 additions & 7 deletions cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,28 @@ func downloadAndReplaceBinary(downloadURL string) error {
return fmt.Errorf("failed to get user home directory: %v", err)
}

tarballPath := filepath.Join(homeDir, common.WeaveDataDirectory, "weave-binary.tar.gz")
extractedPath := filepath.Join(homeDir, common.WeaveDataDirectory)
binaryPath := filepath.Join(extractedPath, "weave")
// Extract into a private temp directory so FindBinaryDir cannot match a
// stale weave binary that may already exist under the shared data root.
tempDir, err := os.MkdirTemp(filepath.Join(homeDir, common.WeaveDataDirectory), "weave-upgrade-*")
if err != nil {
return fmt.Errorf("failed to create temp extraction directory: %v", err)
}
defer func() {
_ = io.DeleteDirectory(tempDir)
}()

tarballPath := filepath.Join(tempDir, "weave-binary.tar.gz")
fmt.Printf("⬇️ Downloading from %s...\n", downloadURL)

if err = io.DownloadAndExtractTarGz(downloadURL, tarballPath, extractedPath); err != nil {
if err = io.DownloadAndExtractTarGz(downloadURL, tarballPath, tempDir); err != nil {
return fmt.Errorf("failed to download and extract binary: %v", err)
}
defer func() {
_ = io.DeleteFile(binaryPath)
}()

binaryDir, err := cosmosutils.FindBinaryDir(tempDir, "weave")
if err != nil {
return fmt.Errorf("could not locate weave binary after extraction: %w", err)
}
binaryPath := filepath.Join(binaryDir, "weave")

if err = doReplace(binaryPath); err != nil {
return fmt.Errorf("failed to replace the new weave binary: %v", err)
Expand Down
76 changes: 47 additions & 29 deletions cosmosutils/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,54 +259,72 @@ func getMinitiadBinaryURL(vm, version string) (string, error) {
), nil
}

func GetMinitiadBinaryPath(vm, version string) (string, error) {
userHome, err := os.UserHomeDir()
// FindBinaryDir walks versionDir to find the directory that contains the named
// executable. This avoids hardcoding assumptions about how a release tarball is
// structured, so the code stays correct even if a future tarball places the
// binary inside a subdirectory.
func FindBinaryDir(versionDir, binaryName string) (string, error) {
var result string
err := filepath.Walk(versionDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == binaryName && info.Mode()&0o111 != 0 {
result = filepath.Dir(path)
return filepath.SkipAll
}
return nil
})
if err != nil {
return "", fmt.Errorf("failed to get user home directory: %v", err)
return "", fmt.Errorf("failed to search for binary %q in %s: %w", binaryName, versionDir, err)
}
extractedPath := filepath.Join(userHome, common.WeaveDataDirectory, fmt.Sprintf("mini%s@%s", vm, version))

switch runtime.GOOS {
case "linux":
return filepath.Join(extractedPath, fmt.Sprintf("mini%s_%s", vm, version), "minitiad"), nil
case "darwin":
return filepath.Join(extractedPath, "minitiad"), nil
default:
return "", fmt.Errorf("unsupported OS: %v", runtime.GOOS)
if result == "" {
return "", fmt.Errorf("binary %q not found in %s", binaryName, versionDir)
}
return result, nil
}

func InstallMinitiadBinary(vm, version, url, binaryPath string) error {
// EnsureMinitiadBinary guarantees the minitiad binary for the given vm/version
// is present and returns its full path. It first checks the version directory
// with FindBinaryDir so it tolerates any tarball layout; only if the binary is
// absent does it download and re-extract. The caller therefore never needs to
// construct or assume a hardcoded sub-path inside the version directory.
func EnsureMinitiadBinary(vm, version, url string) (string, error) {
userHome, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get user home directory: %v", err)
return "", fmt.Errorf("failed to get user home directory: %v", err)
}
tarballPath := filepath.Join(userHome, common.WeaveDataDirectory, "minitia.tar.gz")

extractedPath := filepath.Join(userHome, common.WeaveDataDirectory, fmt.Sprintf("mini%s@%s", vm, version))

if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
if _, err := os.Stat(extractedPath); os.IsNotExist(err) {
err := os.MkdirAll(extractedPath, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create weave data directory: %v", err)
}
binaryDir, findErr := FindBinaryDir(extractedPath, "minitiad")
if findErr != nil {
// Binary not present yet — download and extract.
if err := os.MkdirAll(extractedPath, os.ModePerm); err != nil {
return "", fmt.Errorf("failed to create version directory: %v", err)
}

if err = io.DownloadAndExtractTarGz(url, tarballPath, extractedPath); err != nil {
return fmt.Errorf("failed to download and extract binary: %v", err)
tarballPath := filepath.Join(userHome, common.WeaveDataDirectory, "minitia.tar.gz")
if err := io.DownloadAndExtractTarGz(url, tarballPath, extractedPath); err != nil {
return "", fmt.Errorf("failed to download and extract minitiad binary: %v", err)
}

err = os.Chmod(binaryPath, 0755)
binaryDir, err = FindBinaryDir(extractedPath, "minitiad")
if err != nil {
return fmt.Errorf("failed to set permissions for binary: %v", err)
return "", fmt.Errorf("minitiad binary not found after extraction in %s: %w", extractedPath, err)
}
}

binaryPath := filepath.Join(binaryDir, "minitiad")
if err := os.Chmod(binaryPath, 0o755); err != nil {
return "", fmt.Errorf("failed to set permissions for minitiad binary: %v", err)
}

if vm == "move" || vm == "wasm" {
return io.SetLibraryPaths(filepath.Dir(binaryPath))
if err := io.SetLibraryPaths(binaryDir); err != nil {
return "", err
}
}

return nil
return binaryPath, nil
}

func GetLatestOPInitBotVersion() (string, string, error) {
Expand Down
138 changes: 138 additions & 0 deletions cosmosutils/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cosmosutils

import (
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -295,3 +297,139 @@ func TestFilterPreReleases(t *testing.T) {
})
}
}

// createExecutable creates a file with 0755 permissions at the given path.
func createExecutable(t *testing.T, path string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte("#!/bin/sh\n"), 0o755); err != nil {
t.Fatal(err)
}
}

func TestFindBinaryDir(t *testing.T) {
tests := []struct {
name string
layout func(root string)
binaryName string
wantRel string // expected result relative to root, "" means error
}{
{
name: "binary at root of version dir",
layout: func(root string) {
createExecutable(t, filepath.Join(root, "minitiad"))
},
binaryName: "minitiad",
wantRel: ".",
},
{
name: "binary in a subdirectory",
layout: func(root string) {
createExecutable(t, filepath.Join(root, "minimove_v0.6.0", "minitiad"))
},
binaryName: "minitiad",
wantRel: "minimove_v0.6.0",
},
{
name: "binary deeply nested",
layout: func(root string) {
createExecutable(t, filepath.Join(root, "a", "b", "c", "minitiad"))
},
binaryName: "minitiad",
wantRel: filepath.Join("a", "b", "c"),
},
{
name: "non-executable file is ignored",
layout: func(root string) {
os.MkdirAll(root, 0o755)
os.WriteFile(filepath.Join(root, "minitiad"), []byte("data"), 0o644)
},
binaryName: "minitiad",
wantRel: "",
},
{
name: "wrong name is ignored",
layout: func(root string) {
createExecutable(t, filepath.Join(root, "cosmovisor"))
},
binaryName: "minitiad",
wantRel: "",
},
{
name: "empty directory",
layout: func(root string) {
os.MkdirAll(root, 0o755)
},
binaryName: "minitiad",
wantRel: "",
},
{
name: "multiple matches returns first found",
layout: func(root string) {
createExecutable(t, filepath.Join(root, "minitiad"))
createExecutable(t, filepath.Join(root, "sub", "minitiad"))
},
binaryName: "minitiad",
wantRel: ".", // Walk is lexical; root comes before "sub/"
},
{
name: "nonexistent directory",
layout: func(_ string) {
// intentionally don't create anything
},
binaryName: "minitiad",
wantRel: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
root := filepath.Join(t.TempDir(), "version")
tt.layout(root)

dir, err := FindBinaryDir(root, tt.binaryName)

if tt.wantRel == "" {
if err == nil {
t.Fatalf("expected error, got dir=%q", dir)
}
return
}

if err != nil {
t.Fatalf("unexpected error: %v", err)
}

want := filepath.Join(root, tt.wantRel)
if dir != want {
t.Errorf("got %q, want %q", dir, want)
}
})
}
}

func TestEnsureMinitiadBinary_NestedLayout(t *testing.T) {
origHome := os.Getenv("HOME")
tmpHome := t.TempDir()
os.Setenv("HOME", tmpHome)
defer os.Setenv("HOME", origHome)

vm, version := "move", "v0.5.0"
versionDir := filepath.Join(tmpHome, ".weave", "data", fmt.Sprintf("mini%s@%s", vm, version))

// Stage binary inside a nested subdirectory (simulates a tarball that
// extracts into minimove_v0.5.0/minitiad).
createExecutable(t, filepath.Join(versionDir, "minimove_v0.5.0", "minitiad"))

binaryPath, err := EnsureMinitiadBinary(vm, version, "http://should-not-be-called")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

want := filepath.Join(versionDir, "minimove_v0.5.0", "minitiad")
if binaryPath != want {
t.Errorf("got %q, want %q", binaryPath, want)
}
}
30 changes: 2 additions & 28 deletions cosmosutils/cli_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ package cosmosutils
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/initia-labs/weave/client"
"github.com/initia-labs/weave/common"
"github.com/initia-labs/weave/io"
"github.com/initia-labs/weave/types"
)

Expand Down Expand Up @@ -73,31 +69,9 @@ func NewMinitiadQuerier() (*MinitiadQuerier, error) {
return nil, err
}

userHome, err := os.UserHomeDir()
binaryPath, err := EnsureMinitiadBinary(DefaultMinitiadQuerierVM, version, downloadURL)
if err != nil {
return nil, fmt.Errorf("failed to get user home dir: %v", err)
}
weaveDataPath := filepath.Join(userHome, common.WeaveDataDirectory)
tarballPath := filepath.Join(weaveDataPath, "minitia.tar.gz")
extractedPath := filepath.Join(weaveDataPath, fmt.Sprintf("mini%s@%s", DefaultMinitiadQuerierVM, version))
binaryPath := filepath.Join(extractedPath, DefaultMinitiadQuerierAppName)

if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
if _, err := os.Stat(extractedPath); os.IsNotExist(err) {
err := os.MkdirAll(extractedPath, os.ModePerm)
if err != nil {
return nil, fmt.Errorf("failed to create weave data directory: %v", err)
}
}

if err = io.DownloadAndExtractTarGz(downloadURL, tarballPath, extractedPath); err != nil {
return nil, fmt.Errorf("failed to download minitia binary: %v", err)
}

err = os.Chmod(binaryPath, 0755)
if err != nil {
return nil, fmt.Errorf("failed to set permissions for binary: %v", err)
}
return nil, fmt.Errorf("failed to ensure minitiad binary: %v", err)
}

return &MinitiadQuerier{binaryPath: binaryPath}, nil
Expand Down
6 changes: 1 addition & 5 deletions cosmosutils/cli_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,7 @@ func NewMinitiadTxExecutor(rest string) (*MinitiadTxExecutor, error) {
if err != nil {
return nil, err
}
binaryPath, err := GetMinitiadBinaryPath(vm, version)
if err != nil {
return nil, err
}
err = InstallMinitiadBinary(vm, version, url, binaryPath)
binaryPath, err := EnsureMinitiadBinary(vm, version, url)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions cosmosutils/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ func RecoverKeyFromMnemonicWithCoinType(appName, keyname, mnemonic string, coinT
if coinType == 0 {
return "", fmt.Errorf("coin type must be explicitly provided (60 or 118), got 0")
}

coinTypeStr := fmt.Sprintf("%d", coinType)
keyType := "secp256k1"
if coinType == 60 {
keyType = "eth_secp256k1"
}

cmd = exec.Command(appName, "keys", "add", keyname, "--coin-type", coinTypeStr, "--key-type", keyType, "--recover", "--keyring-backend", "test", "--output", "json")
}

Expand Down
Loading
Loading