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
33 changes: 24 additions & 9 deletions sn-manager/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/config"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/github"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/updater"
"github.com/LumeraProtocol/supernode/v2/sn-manager/internal/utils"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -33,10 +34,10 @@ func runCheck(cmd *cobra.Command, args []string) error {
// Create GitHub client
client := github.NewClient(config.GitHubRepo)

// Get latest release
release, err := client.GetLatestRelease()
// Get latest stable release
release, err := client.GetLatestStableRelease()
if err != nil {
return fmt.Errorf("failed to check for updates: %w", err)
return fmt.Errorf("failed to check for stable updates: %w", err)
}

fmt.Printf("\nLatest release: %s\n", release.TagName)
Expand All @@ -46,19 +47,33 @@ func runCheck(cmd *cobra.Command, args []string) error {
cmp := utils.CompareVersions(cfg.Updates.CurrentVersion, release.TagName)

if cmp < 0 {
fmt.Printf("\n✓ Update available: %s → %s\n", cfg.Updates.CurrentVersion, release.TagName)
fmt.Printf("Published: %s\n", release.PublishedAt.Format("2006-01-02 15:04:05"))
// Use the same logic as auto-updater to determine update eligibility
managerHome := config.GetManagerHome()
autoUpdater := updater.New(managerHome, cfg)
wouldAutoUpdate := autoUpdater.ShouldUpdate(cfg.Updates.CurrentVersion, release.TagName)

if wouldAutoUpdate {
fmt.Printf("\n✓ Update available: %s → %s\n", cfg.Updates.CurrentVersion, release.TagName)
fmt.Printf("Published: %s\n", release.PublishedAt.Format("2006-01-02 15:04:05"))
fmt.Println("\n✓ This update will be applied automatically if auto-upgrade is enabled")
fmt.Println(" Or manually with: sn-manager get")
} else {
fmt.Printf("\n⚠ Major update available: %s → %s\n", cfg.Updates.CurrentVersion, release.TagName)
fmt.Printf("Published: %s\n", release.PublishedAt.Format("2006-01-02 15:04:05"))
fmt.Println("\n⚠ Major version updates require manual installation:")
fmt.Printf(" sn-manager get %s\n", release.TagName)
fmt.Printf(" sn-manager use %s\n", release.TagName)
fmt.Println("\n⚠ Auto-updater will not automatically install major version updates")
}

if release.Body != "" {
fmt.Println("\nRelease notes:")
fmt.Println(release.Body)
}

fmt.Println("\nTo download this version, run: sn-manager get")
} else if cmp == 0 {
fmt.Println("\n✓ You are running the latest version")
fmt.Println("\n✓ You are running the latest stable version")
} else {
fmt.Printf("\n⚠ You are running a newer version than the latest release\n")
fmt.Printf("\n⚠ You are running a newer version than the latest stable release\n")
}

return nil
Expand Down
6 changes: 3 additions & 3 deletions sn-manager/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ func runInit(cmd *cobra.Command, args []string) error {
versionMgr := version.NewManager(managerHome)
client := github.NewClient(config.GitHubRepo)

// Get latest release
release, err := client.GetLatestRelease()
// Get latest stable release
release, err := client.GetLatestStableRelease()
if err != nil {
return fmt.Errorf("failed to get latest release: %w", err)
return fmt.Errorf("failed to get latest stable release: %w", err)
}

targetVersion := release.TagName
Expand Down
4 changes: 2 additions & 2 deletions sn-manager/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,9 @@ func ensureBinaryExists(home string, cfg *config.Config) error {
fmt.Println("No SuperNode binary found. Downloading latest version...")

client := github.NewClient(config.GitHubRepo)
release, err := client.GetLatestRelease()
release, err := client.GetLatestStableRelease()
if err != nil {
return fmt.Errorf("failed to get latest release: %w", err)
return fmt.Errorf("failed to get latest stable release: %w", err)
}

targetVersion := release.TagName
Expand Down
1 change: 1 addition & 0 deletions sn-manager/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/golang/mock v1.6.0
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
go.uber.org/mock v0.5.2
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
2 changes: 2 additions & 0 deletions sn-manager/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
Expand Down
20 changes: 19 additions & 1 deletion sn-manager/internal/github/client.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:generate mockgen -destination=client_mock.go -package=github -source=client.go
//go:generate go run go.uber.org/mock/mockgen -destination=client_mock.go -package=github -source=client.go

package github

Expand All @@ -15,6 +15,7 @@ import (

type GithubClient interface {
GetLatestRelease() (*Release, error)
GetLatestStableRelease() (*Release, error)
ListReleases() ([]*Release, error)
GetRelease(tag string) (*Release, error)
GetSupernodeDownloadURL(version string) (string, error)
Expand Down Expand Up @@ -124,6 +125,23 @@ func (c *Client) ListReleases() ([]*Release, error) {
return releases, nil
}

// GetLatestStableRelease fetches the latest stable (non-prerelease, non-draft) release from GitHub
func (c *Client) GetLatestStableRelease() (*Release, error) {
releases, err := c.ListReleases()
if err != nil {
return nil, fmt.Errorf("failed to list releases: %w", err)
}

// Filter for stable releases (not draft, not prerelease)
for _, release := range releases {
if !release.Draft && !release.Prerelease {
return release, nil
}
}

return nil, fmt.Errorf("no stable releases found")
}

// GetRelease fetches a specific release by tag
func (c *Client) GetRelease(tag string) (*Release, error) {
url := fmt.Sprintf("https://api.github.com/repos/%s/releases/tags/%s", c.repo, tag)
Expand Down
29 changes: 25 additions & 4 deletions sn-manager/internal/github/client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions sn-manager/internal/updater/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (u *AutoUpdater) checkAndUpdate(ctx context.Context) {

log.Printf("Version comparison: current=%s, latest=%s", currentVersion, latestVersion)

if !u.shouldUpdate(currentVersion, latestVersion) {
if !u.ShouldUpdate(currentVersion, latestVersion) {
log.Printf("Current version %s is up to date", currentVersion)
return
}
Expand All @@ -116,7 +116,7 @@ func (u *AutoUpdater) checkAndUpdate(ctx context.Context) {
log.Printf("Updated to %s", latestVersion)
}

func (u *AutoUpdater) shouldUpdate(current, latest string) bool {
func (u *AutoUpdater) ShouldUpdate(current, latest string) bool {
current = strings.TrimPrefix(current, "v")
latest = strings.TrimPrefix(latest, "v")

Expand All @@ -140,10 +140,10 @@ func (u *AutoUpdater) shouldUpdate(current, latest string) bool {
return false
}

// Only update within same major.minor version
if currentParts[0] != latestParts[0] || currentParts[1] != latestParts[1] {
log.Printf("Major/minor version mismatch, skipping update: %s.%s vs %s.%s",
currentParts[0], currentParts[1], latestParts[0], latestParts[1])
// Only update within same major version (allow minor and patch updates)
if currentParts[0] != latestParts[0] {
log.Printf("Major version mismatch, skipping update: %s vs %s",
currentParts[0], latestParts[0])
return false
}

Expand Down
20 changes: 10 additions & 10 deletions sn-manager/internal/updater/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"testing"
"time"

"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -91,8 +91,8 @@ func TestAutoUpdater_ShouldUpdate(t *testing.T) {
{"patch_update", "v1.0.0", "v1.0.1", true},
{"patch_update_no_prefix", "1.0.0", "1.0.1", true},

// Minor version updates (should NOT update based on current logic)
{"minor_update", "v1.0.0", "v1.1.0", false},
// Minor version updates (should update within same major version)
{"minor_update", "v1.0.0", "v1.1.0", true},
{"major_update", "v1.0.0", "v2.0.0", false},

// Same version (should not update)
Expand All @@ -109,7 +109,7 @@ func TestAutoUpdater_ShouldUpdate(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := updater.shouldUpdate(tt.current, tt.latest)
result := updater.ShouldUpdate(tt.current, tt.latest)
assert.Equal(t, tt.expected, result, "shouldUpdate(%s, %s) = %v, want %v", tt.current, tt.latest, result, tt.expected)
})
}
Expand Down Expand Up @@ -288,11 +288,11 @@ func TestAutoUpdater_CheckAndUpdate(t *testing.T) {
expectUpdate: false,
},
{
name: "minor_version_update_should_skip",
name: "minor_version_update_should_proceed",
currentVersion: "v1.0.0",
latestVersion: "v1.1.0",
gatewayIdle: true,
expectUpdate: false,
expectUpdate: true,
},
}

Expand Down Expand Up @@ -1198,11 +1198,11 @@ func TestAutoUpdater_UpdatePolicyLogic(t *testing.T) {
description: "Patch updates (1.2.3 -> 1.2.4) should be allowed",
},
{
name: "minor_update_blocked",
name: "minor_update_allowed",
currentVersion: "v1.2.3",
latestVersion: "v1.3.0",
shouldUpdate: false,
description: "Minor updates (1.2.x -> 1.3.x) should be blocked",
shouldUpdate: true,
description: "Minor updates (1.2.x -> 1.3.x) should be allowed within same major version",
},
{
name: "major_update_blocked",
Expand All @@ -1225,7 +1225,7 @@ func TestAutoUpdater_UpdatePolicyLogic(t *testing.T) {
cfg := createTestConfig(t, homeDir, scenario.currentVersion, true, 3600)
updater := New(homeDir, cfg)

result := updater.shouldUpdate(scenario.currentVersion, scenario.latestVersion)
result := updater.ShouldUpdate(scenario.currentVersion, scenario.latestVersion)
assert.Equal(t, scenario.shouldUpdate, result, scenario.description)
})
}
Expand Down