mirror of
https://github.com/rommapp/grout.git
synced 2026-04-23 06:54:36 +00:00
Add new release channel to tie to RomM version.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
ENVIRONMENT=DEV
|
||||
# GROUT_VERSION=
|
||||
|
||||
# Window Size overrides (defaults to full)
|
||||
# WINDOW_WIDTH=1024
|
||||
|
||||
+8
-2
@@ -146,9 +146,9 @@ func buildFSM(config *internal.Config, c cfw.CFW, platforms []romm.Platform, qui
|
||||
})
|
||||
}
|
||||
|
||||
// Start auto-update check on first platform menu view
|
||||
autoUpdateOnce.Do(func() {
|
||||
autoUpdate = update.NewAutoUpdate(currentCFW, config.ReleaseChannel)
|
||||
host, _ := gaba.Get[romm.Host](ctx)
|
||||
autoUpdate = update.NewAutoUpdate(currentCFW, config.ReleaseChannel, &host)
|
||||
ui.AddStatusBarIcon(autoUpdate.Icon())
|
||||
autoUpdate.Start()
|
||||
})
|
||||
@@ -661,6 +661,10 @@ func buildFSM(config *internal.Config, c cfw.CFW, platforms []romm.Platform, qui
|
||||
nav.AdvancedSettingsPos.Index = result.Value.LastSelectedIndex
|
||||
nav.AdvancedSettingsPos.VisibleStartIndex = result.Value.LastVisibleStartIndex
|
||||
|
||||
if result.ExitCode == gaba.ExitCodeSuccess && autoUpdate != nil {
|
||||
autoUpdate.Recheck(config.ReleaseChannel)
|
||||
}
|
||||
|
||||
return result.Value, result.ExitCode
|
||||
}).
|
||||
On(gaba.ExitCodeSuccess, settings).
|
||||
@@ -958,11 +962,13 @@ func buildFSM(config *internal.Config, c cfw.CFW, platforms []romm.Platform, qui
|
||||
|
||||
gaba.AddState(fsm, updateCheck, func(ctx *gaba.Context) (ui.UpdateOutput, gaba.ExitCode) {
|
||||
currentCFW, _ := gaba.Get[cfw.CFW](ctx)
|
||||
host, _ := gaba.Get[romm.Host](ctx)
|
||||
|
||||
screen := ui.NewUpdateScreen()
|
||||
result, err := screen.Draw(ui.UpdateInput{
|
||||
CFW: currentCFW,
|
||||
ReleaseChannel: config.ReleaseChannel,
|
||||
Host: &host,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
+4
-3
@@ -17,8 +17,9 @@ import (
|
||||
type ReleaseChannel string
|
||||
|
||||
const (
|
||||
ReleaseChannelStable ReleaseChannel = "stable"
|
||||
ReleaseChannelBeta ReleaseChannel = "beta"
|
||||
ReleaseChannelMatchRomM ReleaseChannel = "match_romm"
|
||||
ReleaseChannelStable ReleaseChannel = "stable"
|
||||
ReleaseChannelBeta ReleaseChannel = "beta"
|
||||
)
|
||||
|
||||
var kidModeEnabled atomic.Bool
|
||||
@@ -138,7 +139,7 @@ func SaveConfig(config *Config) error {
|
||||
}
|
||||
|
||||
if config.ReleaseChannel == "" {
|
||||
config.ReleaseChannel = ReleaseChannelStable
|
||||
config.ReleaseChannel = ReleaseChannelMatchRomM
|
||||
}
|
||||
|
||||
gaba.SetRawLogLevel(config.LogLevel)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package romm
|
||||
|
||||
type HeartbeatResponse struct {
|
||||
System struct {
|
||||
Version string `json:"VERSION"`
|
||||
} `json:"SYSTEM"`
|
||||
}
|
||||
|
||||
func (c *Client) GetHeartbeat() (HeartbeatResponse, error) {
|
||||
var heartbeat HeartbeatResponse
|
||||
err := c.doRequest("GET", endpointHeartbeat, nil, nil, &heartbeat)
|
||||
return heartbeat, err
|
||||
}
|
||||
@@ -143,6 +143,7 @@ func (s *AdvancedSettingsScreen) buildMenuItems(config *internal.Config) []gaba.
|
||||
{
|
||||
Item: gaba.MenuItem{Text: i18n.Localize(&goi18n.Message{ID: "settings_release_channel", Other: "Release Channel"}, nil)},
|
||||
Options: []gaba.Option{
|
||||
{DisplayName: i18n.Localize(&goi18n.Message{ID: "release_match_romm", Other: "Match RomM"}, nil), Value: internal.ReleaseChannelMatchRomM},
|
||||
{DisplayName: i18n.Localize(&goi18n.Message{ID: "release_stable", Other: "Stable"}, nil), Value: internal.ReleaseChannelStable},
|
||||
{DisplayName: i18n.Localize(&goi18n.Message{ID: "release_beta", Other: "Beta"}, nil), Value: internal.ReleaseChannelBeta},
|
||||
},
|
||||
|
||||
+4
-2
@@ -258,10 +258,12 @@ func logLevelToIndex(level string) int {
|
||||
|
||||
func releaseChannelToIndex(releaseChannel internal.ReleaseChannel) int {
|
||||
switch releaseChannel {
|
||||
case internal.ReleaseChannelStable:
|
||||
case internal.ReleaseChannelMatchRomM:
|
||||
return 0
|
||||
case internal.ReleaseChannelBeta:
|
||||
case internal.ReleaseChannelStable:
|
||||
return 1
|
||||
case internal.ReleaseChannelBeta:
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
+3
-1
@@ -6,6 +6,7 @@ import (
|
||||
"grout/cfw"
|
||||
"grout/internal"
|
||||
"grout/internal/stringutil"
|
||||
"grout/romm"
|
||||
"grout/update"
|
||||
|
||||
gaba "github.com/BrandonKowalski/gabagool/v2/pkg/gabagool"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
type UpdateInput struct {
|
||||
CFW cfw.CFW
|
||||
ReleaseChannel internal.ReleaseChannel
|
||||
Host *romm.Host
|
||||
}
|
||||
|
||||
type UpdateOutput struct {
|
||||
@@ -43,7 +45,7 @@ func (s *UpdateScreen) Draw(input UpdateInput) (ScreenResult[UpdateOutput], erro
|
||||
ShowThemeBackground: true,
|
||||
},
|
||||
func() (interface{}, error) {
|
||||
updateInfo, checkErr = update.CheckForUpdate(input.CFW, input.ReleaseChannel)
|
||||
updateInfo, checkErr = update.CheckForUpdate(input.CFW, input.ReleaseChannel, input.Host)
|
||||
return nil, checkErr
|
||||
},
|
||||
)
|
||||
|
||||
+20
-2
@@ -3,6 +3,7 @@ package update
|
||||
import (
|
||||
"grout/cfw"
|
||||
"grout/internal"
|
||||
"grout/romm"
|
||||
"sync/atomic"
|
||||
|
||||
gaba "github.com/BrandonKowalski/gabagool/v2/pkg/gabagool"
|
||||
@@ -13,6 +14,7 @@ const updateIcon = "\U000F06B0"
|
||||
type AutoUpdate struct {
|
||||
cfwType cfw.CFW
|
||||
releaseChannel internal.ReleaseChannel
|
||||
host *romm.Host
|
||||
icon *gaba.DynamicStatusBarIcon
|
||||
running atomic.Bool
|
||||
updateAvailable atomic.Bool
|
||||
@@ -20,10 +22,11 @@ type AutoUpdate struct {
|
||||
updateInfo *Info
|
||||
}
|
||||
|
||||
func NewAutoUpdate(c cfw.CFW, r internal.ReleaseChannel) *AutoUpdate {
|
||||
func NewAutoUpdate(c cfw.CFW, r internal.ReleaseChannel, host *romm.Host) *AutoUpdate {
|
||||
return &AutoUpdate{
|
||||
cfwType: c,
|
||||
releaseChannel: r,
|
||||
host: host,
|
||||
icon: gaba.NewDynamicStatusBarIcon(""), // Start empty, will show icon if update available
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
@@ -53,6 +56,21 @@ func (a *AutoUpdate) UpdateInfo() *Info {
|
||||
return a.updateInfo
|
||||
}
|
||||
|
||||
// Recheck updates the release channel and re-runs the update check.
|
||||
// This should be called when the user changes the release channel in settings.
|
||||
func (a *AutoUpdate) Recheck(releaseChannel internal.ReleaseChannel) {
|
||||
if a.running.Load() {
|
||||
return // Already running, skip
|
||||
}
|
||||
|
||||
a.releaseChannel = releaseChannel
|
||||
a.updateAvailable.Store(false)
|
||||
a.updateInfo = nil
|
||||
a.icon.SetText("") // Clear the icon
|
||||
|
||||
a.Start()
|
||||
}
|
||||
|
||||
func (a *AutoUpdate) run() {
|
||||
logger := gaba.GetLogger()
|
||||
defer func() {
|
||||
@@ -62,7 +80,7 @@ func (a *AutoUpdate) run() {
|
||||
|
||||
logger.Debug("AutoUpdate: Checking for updates in background")
|
||||
|
||||
info, err := CheckForUpdate(a.cfwType, a.releaseChannel)
|
||||
info, err := CheckForUpdate(a.cfwType, a.releaseChannel, a.host)
|
||||
if err != nil {
|
||||
logger.Debug("AutoUpdate: Failed to check for updates", "error", err)
|
||||
return
|
||||
|
||||
@@ -95,3 +95,76 @@ func (r *GitHubRelease) FindAsset(name string) *GitHubAsset {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchReleaseForRomMVersion fetches the latest Grout release that matches
|
||||
// the first 3 semver components (major.minor.patch) of the given RomM version.
|
||||
// For example, if rommVersion is "4.6.0-alpha.3", this will find Grout releases
|
||||
// like "4.6.0", "4.6.0.1", "4.6.0-beta.1", etc.
|
||||
func FetchReleaseForRomMVersion(rommVersion string) (*GitHubRelease, error) {
|
||||
rommVer, err := ParseVersion(rommVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse RomM version: %w", err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/repos/%s/%s/releases", githubAPIURL, repoOwner, repoName)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: defaultTimeout,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/vnd.github+json")
|
||||
req.Header.Set("User-Agent", "Grout-Updater")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch release: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, fmt.Errorf("no releases found")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var releases []GitHubRelease
|
||||
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if len(releases) == 0 {
|
||||
return nil, fmt.Errorf("no releases found")
|
||||
}
|
||||
|
||||
// Find the latest release that matches the RomM version's major.minor.patch
|
||||
for _, release := range releases {
|
||||
if release.Draft {
|
||||
continue
|
||||
}
|
||||
|
||||
releaseVer, err := ParseVersion(release.TagName)
|
||||
if err != nil {
|
||||
gaba.GetLogger().Debug("skipping release with unparseable version", "tag", release.TagName, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if major.minor.patch match
|
||||
if releaseVer.Major == rommVer.Major &&
|
||||
releaseVer.Minor == rommVer.Minor &&
|
||||
releaseVer.Patch == rommVer.Patch {
|
||||
gaba.GetLogger().Debug("found matching release for RomM version",
|
||||
"rommVersion", rommVersion, "release", release.TagName)
|
||||
return &release, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no Grout release found matching RomM version %d.%d.%d",
|
||||
rommVer.Major, rommVer.Minor, rommVer.Patch)
|
||||
}
|
||||
|
||||
+33
-4
@@ -5,12 +5,14 @@ import (
|
||||
"grout/cfw"
|
||||
"grout/internal"
|
||||
"grout/internal/constants"
|
||||
"grout/romm"
|
||||
"grout/version"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
gaba "github.com/BrandonKowalski/gabagool/v2/pkg/gabagool"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
@@ -32,7 +34,10 @@ func GetAssetName(c cfw.CFW) string {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckForUpdate(c cfw.CFW, releaseChannel internal.ReleaseChannel) (*Info, error) {
|
||||
// CheckForUpdate checks for available updates based on the release channel.
|
||||
// For ReleaseChannelMatchRomM, the host parameter is required to fetch the RomM version.
|
||||
// For other channels, the host parameter is optional and ignored.
|
||||
func CheckForUpdate(c cfw.CFW, releaseChannel internal.ReleaseChannel, host *romm.Host) (*Info, error) {
|
||||
currentVersion := version.Get().Version
|
||||
|
||||
if currentVersion == "dev" {
|
||||
@@ -42,9 +47,33 @@ func CheckForUpdate(c cfw.CFW, releaseChannel internal.ReleaseChannel) (*Info, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
release, err := FetchLatestRelease(releaseChannel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check for updates: %w", err)
|
||||
var release *GitHubRelease
|
||||
var err error
|
||||
|
||||
if releaseChannel == internal.ReleaseChannelMatchRomM {
|
||||
if host == nil {
|
||||
return nil, fmt.Errorf("host is required for Match RomM release channel")
|
||||
}
|
||||
|
||||
// Fetch RomM version from heartbeat
|
||||
client := romm.NewClientFromHost(*host)
|
||||
heartbeat, err := client.GetHeartbeat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get RomM version: %w", err)
|
||||
}
|
||||
|
||||
gaba.GetLogger().Debug("fetched RomM version for update check", "version", heartbeat.System.Version)
|
||||
|
||||
// Find a Grout release matching the RomM version
|
||||
release, err = FetchReleaseForRomMVersion(heartbeat.System.Version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find matching release: %w", err)
|
||||
}
|
||||
} else {
|
||||
release, err = FetchLatestRelease(releaseChannel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check for updates: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
info := &Info{
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestCompareVersions(t *testing.T) {
|
||||
{"2.0.0", "1.0.0", 1, "major version older"},
|
||||
{"1.0.0", "1.0.0", 0, "same version"},
|
||||
|
||||
// Beta vs full release - THE KEY FIX
|
||||
// Beta vs full release
|
||||
{"v1.2.0-beta.1", "v1.2.0", -1, "beta should recognize full release as newer"},
|
||||
{"v1.2.0", "v1.2.0-beta.1", 1, "full release should be newer than beta"},
|
||||
{"1.2.0-beta.1", "1.2.0", -1, "beta should recognize full release as newer (no v prefix)"},
|
||||
|
||||
+7
-1
@@ -1,5 +1,7 @@
|
||||
package version
|
||||
|
||||
import "os"
|
||||
|
||||
var (
|
||||
Version = "dev"
|
||||
GitCommit = "unknown"
|
||||
@@ -13,8 +15,12 @@ type BuildInfo struct {
|
||||
}
|
||||
|
||||
func Get() BuildInfo {
|
||||
v := Version
|
||||
if override := os.Getenv("GROUT_VERSION"); override != "" {
|
||||
v = override
|
||||
}
|
||||
return BuildInfo{
|
||||
Version: Version,
|
||||
Version: v,
|
||||
GitCommit: GitCommit,
|
||||
BuildDate: BuildDate,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user