Skip to content

Commit cca52c6

Browse files
committed
fix: include cmd_update.go in build
1 parent b40ce58 commit cca52c6

File tree

2 files changed

+204
-1
lines changed

2 files changed

+204
-1
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
scripts/build/pchaind
2-
push-validator
2+
/push-validator
3+
/push-validator.backup

cmd/push-validator/cmd_update.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/pushchain/push-validator-cli/internal/update"
12+
ui "github.com/pushchain/push-validator-cli/internal/ui"
13+
"github.com/spf13/cobra"
14+
)
15+
16+
func init() {
17+
var (
18+
checkOnly bool
19+
force bool
20+
version string
21+
skipVerify bool
22+
)
23+
24+
updateCmd := &cobra.Command{
25+
Use: "update",
26+
Short: "Update push-validator to the latest version",
27+
Long: `Check for and install the latest version of push-validator.
28+
29+
The update command downloads pre-built binaries from GitHub Releases,
30+
verifies the checksum, and replaces the current binary.
31+
32+
Examples:
33+
push-validator update # Update to latest version
34+
push-validator update --check # Check only, don't install
35+
push-validator update --force # Skip confirmation
36+
push-validator update --version v1.2.0 # Install specific version`,
37+
RunE: func(cmd *cobra.Command, args []string) error {
38+
p := ui.NewPrinter(flagOutput)
39+
40+
// Create updater
41+
updater, err := update.NewUpdater(Version)
42+
if err != nil {
43+
return fmt.Errorf("failed to initialize updater: %w", err)
44+
}
45+
46+
// Fetch release (latest or specific version)
47+
var release *update.Release
48+
if version != "" {
49+
p.Info(fmt.Sprintf("Fetching release %s...", version))
50+
release, err = update.FetchReleaseByTag(version)
51+
} else {
52+
p.Info("Checking for updates...")
53+
release, err = update.FetchLatestRelease()
54+
}
55+
if err != nil {
56+
return fmt.Errorf("failed to fetch release: %w", err)
57+
}
58+
59+
latestVersion := strings.TrimPrefix(release.TagName, "v")
60+
currentVersion := strings.TrimPrefix(Version, "v")
61+
62+
// Check if update needed
63+
if !force && !update.IsNewerVersion(Version, release.TagName) {
64+
p.Success(fmt.Sprintf("Already up to date (v%s)", currentVersion))
65+
return nil
66+
}
67+
68+
// Show update info
69+
fmt.Println()
70+
p.Info(fmt.Sprintf("Update available: v%s → v%s", currentVersion, latestVersion))
71+
72+
// Show changelog (first 10 lines)
73+
if release.Body != "" {
74+
fmt.Println()
75+
fmt.Println("Changelog:")
76+
lines := strings.Split(release.Body, "\n")
77+
maxLines := 10
78+
if len(lines) < maxLines {
79+
maxLines = len(lines)
80+
}
81+
for _, line := range lines[:maxLines] {
82+
fmt.Printf(" %s\n", line)
83+
}
84+
if len(lines) > 10 {
85+
fmt.Printf(" ... (see %s for full changelog)\n", release.HTMLURL)
86+
}
87+
}
88+
fmt.Println()
89+
90+
// Check only mode
91+
if checkOnly {
92+
p.Info("Run 'push-validator update' to install")
93+
return nil
94+
}
95+
96+
// Confirm update (skip if --force or --yes flag)
97+
if !force && !flagYes {
98+
fmt.Print("Update now? [Y/n]: ")
99+
var response string
100+
fmt.Scanln(&response)
101+
response = strings.ToLower(strings.TrimSpace(response))
102+
if response != "" && response != "y" && response != "yes" {
103+
p.Warn("Update cancelled")
104+
return nil
105+
}
106+
}
107+
108+
// Find binary for current platform
109+
asset, err := update.GetAssetForPlatform(release)
110+
if err != nil {
111+
return err
112+
}
113+
114+
// Download with progress
115+
p.Info(fmt.Sprintf("Downloading %s...", asset.Name))
116+
archiveData, err := updater.Download(asset, func(downloaded, total int64) {
117+
if total > 0 {
118+
pct := float64(downloaded) / float64(total) * 100
119+
fmt.Printf("\r Downloading... %.1f%%", pct)
120+
}
121+
})
122+
if err != nil {
123+
return fmt.Errorf("download failed: %w", err)
124+
}
125+
fmt.Println() // Clear progress line
126+
127+
// Verify checksum
128+
if !skipVerify {
129+
p.Info("Verifying checksum...")
130+
if err := updater.VerifyChecksum(archiveData, release, asset.Name); err != nil {
131+
return fmt.Errorf("checksum verification failed: %w", err)
132+
}
133+
p.Success("Checksum verified")
134+
} else {
135+
p.Warn("Skipping checksum verification (not recommended)")
136+
}
137+
138+
// Extract binary
139+
p.Info("Extracting binary...")
140+
binaryData, err := updater.ExtractBinary(archiveData)
141+
if err != nil {
142+
return fmt.Errorf("extraction failed: %w", err)
143+
}
144+
145+
// Install
146+
p.Info("Installing...")
147+
if err := updater.Install(binaryData); err != nil {
148+
return fmt.Errorf("installation failed: %w", err)
149+
}
150+
151+
// Verify new binary
152+
p.Info("Verifying installation...")
153+
verifyCmd := exec.Command(updater.BinaryPath, "version")
154+
var stdout bytes.Buffer
155+
verifyCmd.Stdout = &stdout
156+
if err := verifyCmd.Run(); err != nil {
157+
p.Warn("Verification failed, rolling back...")
158+
if rbErr := updater.Rollback(); rbErr != nil {
159+
return fmt.Errorf("rollback failed: %w (original error: %v)", rbErr, err)
160+
}
161+
return fmt.Errorf("new binary verification failed, rolled back: %w", err)
162+
}
163+
164+
fmt.Println()
165+
p.Success(fmt.Sprintf("Updated to v%s", latestVersion))
166+
fmt.Println()
167+
168+
// Check if node is running and suggest restart
169+
if isNodeRunning() {
170+
p.Info("Node is running. Run 'push-validator restart' to use the new version.")
171+
}
172+
173+
return nil
174+
},
175+
}
176+
177+
updateCmd.Flags().BoolVar(&checkOnly, "check", false, "Only check for updates, don't install")
178+
updateCmd.Flags().BoolVar(&force, "force", false, "Skip confirmation prompt")
179+
updateCmd.Flags().StringVar(&version, "version", "", "Install specific version (e.g., v1.2.0)")
180+
updateCmd.Flags().BoolVar(&skipVerify, "no-verify", false, "Skip checksum verification (not recommended)")
181+
182+
rootCmd.AddCommand(updateCmd)
183+
}
184+
185+
// isNodeRunning checks if the validator node is currently running
186+
func isNodeRunning() bool {
187+
cfg := loadCfg()
188+
189+
// Check pchaind PID file
190+
pidFile := filepath.Join(cfg.HomeDir, "pchaind.pid")
191+
if _, err := os.Stat(pidFile); err == nil {
192+
return true
193+
}
194+
195+
// Check cosmovisor PID file
196+
cosmovisorPid := filepath.Join(cfg.HomeDir, "cosmovisor.pid")
197+
if _, err := os.Stat(cosmovisorPid); err == nil {
198+
return true
199+
}
200+
201+
return false
202+
}

0 commit comments

Comments
 (0)