Skip to content

Commit 80598bf

Browse files
committed
feat: Enhance update banner and release checker to support multiple new versions
1 parent 94253ed commit 80598bf

5 files changed

Lines changed: 140 additions & 35 deletions

File tree

frontend/src/features/apiInfo/update-banner.tsx

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
DialogClose,
55
DialogContent,
66
DialogFooter,
7+
DialogDescription,
78
DialogHeader,
89
DialogTitle,
910
DialogTrigger,
@@ -38,22 +39,31 @@ export const UpdateBanner = () => {
3839
Available new update
3940
</Button>
4041
</DialogTrigger>
41-
<DialogContent>
42-
<DialogHeader>
43-
<DialogTitle>🚀 Available new update</DialogTitle>
42+
<DialogContent className="flex flex-col gap-0 p-0 max-h-[85vh] sm:max-h-[min(840px,95vh)] sm:max-w-2xl [&>button:last-child]:top-3.5">
43+
<DialogHeader className="contents space-y-0 text-left">
44+
<DialogTitle className="border-b px-6 py-4 text-base">
45+
🚀 Available new update
46+
</DialogTitle>
4447
</DialogHeader>
45-
<div className="changelog">
46-
<h2>
47-
Version:{" "}
48-
<span className="text-accent-foreground font-semibold">
49-
{serverStore.serverInfo?.available_update?.tag_name}
50-
</span>
51-
</h2>
52-
<Markdown remarkPlugins={[remarkGfm]}>
53-
{serverStore.serverInfo?.available_update?.description}
54-
</Markdown>
55-
</div>
56-
<DialogFooter>
48+
49+
<DialogDescription asChild>
50+
<div className="flex-1 changelog overflow-y-auto overscroll-contain p-6">
51+
{/* Current version */}
52+
<div className="mb-3 text-lg font-semibold">
53+
Current version:{" "}
54+
<span className="text-zinc-500">
55+
{serverStore.serverInfo?.version}
56+
</span>
57+
</div>
58+
59+
{/* New version */}
60+
<Markdown remarkPlugins={[remarkGfm]}>
61+
{serverStore.serverInfo?.available_update?.description}
62+
</Markdown>
63+
</div>
64+
</DialogDescription>
65+
66+
<DialogFooter className="flex-shrink-0 border-t px-6 py-4 sm:items-center">
5767
<DialogClose asChild>
5868
<Button variant="outline">Close</Button>
5969
</DialogClose>

frontend/src/shared/styles/index.css

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ body {
157157

158158
.changelog {
159159
color: var(--color-foreground);
160-
@apply max-w-full overflow-y-scroll;
160+
@apply max-w-full;
161+
162+
h1 {
163+
@apply text-lg font-semibold mb-3;
164+
}
161165

162166
h2 {
163167
@apply text-lg font-semibold mb-2;
@@ -171,10 +175,14 @@ body {
171175
}
172176

173177
p {
174-
@apply p-2;
178+
@apply py-2;
175179
}
176180

177181
a {
178182
@apply text-sm text-blue-500 transition-colors hover:text-blue-700 hover:underline;
179183
}
184+
185+
hr {
186+
@apply my-4;
187+
}
180188
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/sxwebdev/sentinel
33
go 1.24.5
44

55
require (
6+
github.com/Masterminds/semver/v3 v3.4.0
67
github.com/containrrr/shoutrrr v0.8.0
78
github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994
89
github.com/dromara/carbon/v2 v2.6.11

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
22
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
33
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
4-
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
5-
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
4+
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
5+
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
66
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
77
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
88
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=

internal/web/release_checker.go

Lines changed: 102 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ package web
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
8+
"strings"
79
"time"
10+
11+
"github.com/Masterminds/semver/v3"
812
)
913

1014
// checkNewVersion checks if a new version is available from github releases
@@ -31,15 +35,16 @@ func (s *Server) checkNewVersionWrapper(ctx context.Context) {
3135
}
3236
}
3337

34-
// checkNewVersion checks if a new version is available from GitHub releases
38+
// checkNewVersion checks if new versions are available from GitHub releases
3539
func (s *Server) checkNewVersion() error {
36-
s.logger.Info("Checking for new version...")
40+
s.logger.Info("Checking for new versions...")
3741

38-
// get latest release from GitHub
42+
// Get all releases from GitHub
3943
httpClient := http.DefaultClient
4044
httpClient.Timeout = time.Second * 10
4145

42-
resp, err := httpClient.Get("https://api.github.com/repos/sxwebdev/sentinel/releases/latest")
46+
// Get multiple pages of releases to ensure we don't miss any
47+
resp, err := httpClient.Get("https://api.github.com/repos/sxwebdev/sentinel/releases?per_page=100")
4348
if err != nil {
4449
return nil
4550
}
@@ -49,25 +54,106 @@ func (s *Server) checkNewVersion() error {
4954
return nil
5055
}
5156

52-
var releaseInfo struct {
53-
TagName string `json:"tag_name"`
54-
Body string `json:"body"`
57+
var releases []struct {
58+
TagName string `json:"tag_name"`
59+
Body string `json:"body"`
60+
PublishedAt string `json:"published_at"`
61+
Draft bool `json:"draft"`
62+
Prerelease bool `json:"prerelease"`
5563
}
56-
if err := json.NewDecoder(resp.Body).Decode(&releaseInfo); err != nil {
64+
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
5765
return nil
5866
}
5967

60-
// Compare with current version
61-
if releaseInfo.TagName != s.serverInfo.Version {
62-
s.logger.Infof("New version available: %s", releaseInfo.TagName)
68+
// Parse current version
69+
var currentVersion *semver.Version
70+
71+
// Handle special cases like "local", "dev", etc.
72+
if s.serverInfo.Version == "local" || s.serverInfo.Version == "dev" || s.serverInfo.Version == "" {
73+
// For development versions, consider them as version 0.0.0
74+
s.logger.Debugf("Development version detected (%s), treating as 0.0.0 for comparison", s.serverInfo.Version)
75+
currentVersion, err = semver.NewVersion("0.0.0")
76+
} else {
77+
currentVersion, err = semver.NewVersion(s.serverInfo.Version)
78+
}
79+
80+
if err != nil {
81+
s.logger.Warnf("Failed to parse current version %s: %v", s.serverInfo.Version, err)
82+
return nil
83+
}
84+
85+
// Find newer releases (excluding drafts and prereleases)
86+
var newerReleases []struct {
87+
TagName string
88+
Body string
89+
PublishedAt string
90+
Version *semver.Version
91+
}
92+
93+
for _, release := range releases {
94+
// Skip drafts and prereleases
95+
if release.Draft || release.Prerelease {
96+
continue
97+
}
98+
99+
// Parse release version
100+
releaseVersion, err := semver.NewVersion(release.TagName)
101+
if err != nil {
102+
continue // Skip invalid version tags
103+
}
63104

64-
s.serverInfo.AvailableUpdate = &AvailableUpdate{
65-
IsAvailableManual: s.config.Upgrader.IsEnabled,
66-
TagName: releaseInfo.TagName,
67-
URL: "https://github.com/sxwebdev/sentinel/releases/tag/" + releaseInfo.TagName,
68-
Description: releaseInfo.Body,
105+
// Check if release is newer than current version
106+
if releaseVersion.GreaterThan(currentVersion) {
107+
newerReleases = append(newerReleases, struct {
108+
TagName string
109+
Body string
110+
PublishedAt string
111+
Version *semver.Version
112+
}{
113+
TagName: release.TagName,
114+
Body: release.Body,
115+
PublishedAt: release.PublishedAt,
116+
Version: releaseVersion,
117+
})
69118
}
70119
}
71120

121+
// If no newer releases found, clear the update info
122+
if len(newerReleases) == 0 {
123+
s.serverInfo.AvailableUpdate = nil
124+
return nil
125+
}
126+
127+
// Sort newer releases by version (oldest to newest)
128+
// sort.Slice(newerReleases, func(i, j int) bool {
129+
// return newerReleases[i].Version.LessThan(newerReleases[j].Version)
130+
// })
131+
132+
// Get the latest version info
133+
latestRelease := newerReleases[len(newerReleases)-1]
134+
135+
// Combine descriptions from all newer releases
136+
var combinedBody strings.Builder
137+
combinedBody.WriteString(fmt.Sprintf("# Found %d newer version(s):\n\n", len(newerReleases)))
138+
139+
for _, release := range newerReleases {
140+
combinedBody.WriteString(fmt.Sprintf("## Version %s\n", release.TagName))
141+
if release.Body != "" {
142+
combinedBody.WriteString(release.Body)
143+
} else {
144+
combinedBody.WriteString("No release notes available.")
145+
}
146+
combinedBody.WriteString("\n\n---\n\n")
147+
}
148+
149+
s.logger.Infof("Found %d newer version(s), latest: %s", len(newerReleases), latestRelease.TagName)
150+
151+
s.serverInfo.AvailableUpdate = &AvailableUpdate{
152+
IsAvailableManual: s.config.Upgrader.IsEnabled,
153+
TagName: latestRelease.TagName,
154+
URL: "https://github.com/sxwebdev/sentinel/releases/tag/" + latestRelease.TagName,
155+
Description: combinedBody.String(),
156+
}
157+
72158
return nil
73159
}

0 commit comments

Comments
 (0)