feat: enhance version comparison logic in update check and refactor tunnel command functions

Kim committed Jun 1, 2026 at 13:45 UTC 552fc4fc12038637ba2f27f8e73b6f734f133395
4 files changed +12 -27
cmd/portal-tunnel/installer/update.go
+8 -1
@@ -14,6 +14,8 @@ import (
14 "strings"
15 "time"
16
17 + "golang.org/x/mod/semver"
18 +
19 "github.com/gosuda/portal-tunnel/v2/types"
20 "github.com/gosuda/portal-tunnel/v2/utils"
21 )
@@ -48,7 +50,12 @@ func StartUpdateCheck(currentVersion string) {
50 segments := strings.Split(strings.TrimPrefix(parsed.Path, "/"), "/")
51 if len(segments) >= 5 {
52 version := segments[4]
51 - if strings.HasPrefix(version, "v") && version != currentVersion {
53 + current := strings.TrimSpace(currentVersion)
54 + if current != "" && !strings.HasPrefix(current, "v") {
55 + current = "v" + current
56 + }
57 + latestValid := strings.HasPrefix(version, "v") && semver.IsValid(version)
58 + if latestValid && semver.IsValid(current) && semver.Compare(version, current) > 0 {
59 fmt.Fprintf(os.Stderr, "\nA new version is available: %s. Run 'portal update' to upgrade.\n", version)
60 }
61 }
frontend/src/lib/tunnelCommand.test.ts
+2 -3
@@ -31,7 +31,7 @@ describe("tunnelCommand", () => {
31 expect(command).not.toContain("\n --name");
32 });
33
34 - it("wraps display commands before relays while keeping copied commands flat", () => {
34 + it("keeps display and copied commands flat", () => {
35 const options = {
36 currentOrigin: "https://relay.example.com",
37 target: "localhost:3000",
@@ -54,8 +54,7 @@ describe("tunnelCommand", () => {
54 [
55 `$ProgressPreference = 'SilentlyContinue'`,
56 `irm https://relay.example.com/api/install.ps1 | iex`,
57 - `portal expose localhost:3000 --name my-app`,
58 - `--relays https://relay.example.com --discovery=false --thumbnail https://example.com/thumb.png`,
57 + `portal expose localhost:3000 --name my-app --relays https://relay.example.com --discovery=false --thumbnail https://example.com/thumb.png`,
58 ].join("\n")
59 );
60 });
frontend/src/lib/tunnelCommand.ts
+1 -22
@@ -70,7 +70,7 @@ export function buildTunnelDisplayCommand({
70 udpPort,
71 });
72
73 - return joinTunnelDisplayCommand(installLine, exposeHead, exposeOptions);
73 + return joinTunnelCommand(installLine, exposeHead, exposeOptions);
74 }
75
76 function buildTunnelCommandParts({
@@ -234,24 +234,3 @@ function joinTunnelCommand(
234 ): string {
235 return [installLine, [exposeHead, ...exposeOptions].join(" ")].join("\n");
236 }
237 -
238 -function joinTunnelDisplayCommand(
239 - installLine: string,
240 - exposeHead: string,
241 - exposeOptions: string[]
242 -): string {
243 - const relayIndex = exposeOptions.findIndex((option) =>
244 - option.startsWith("--relays ")
245 - );
246 -
247 - if (relayIndex < 0) {
248 - return joinTunnelCommand(installLine, exposeHead, exposeOptions);
249 - }
250 -
251 - const exposeLines = [
252 - [exposeHead, ...exposeOptions.slice(0, relayIndex)].join(" "),
253 - exposeOptions.slice(relayIndex).join(" "),
254 - ];
255 -
256 - return [installLine, exposeLines.join("\n")].join("\n");
257 -}
go.mod
+1 -1
@@ -33,6 +33,7 @@ require (
33 github.com/vultr/govultr/v3 v3.30.0
34 github.com/x402-foundation/x402/go v0.0.0-20260526081544-8cf020c5335e
35 golang.org/x/crypto v0.51.0
36 + golang.org/x/mod v0.35.0
37 golang.org/x/net v0.55.0
38 golang.org/x/oauth2 v0.36.0
39 golang.org/x/sync v0.20.0
@@ -145,7 +146,6 @@ require (
146 go.opentelemetry.io/otel/metric v1.43.0 // indirect
147 go.opentelemetry.io/otel/trace v1.43.0 // indirect
148 go.yaml.in/yaml/v2 v2.4.2 // indirect
148 - golang.org/x/mod v0.35.0 // indirect
149 golang.org/x/text v0.37.0 // indirect
150 golang.org/x/time v0.15.0 // indirect
151 golang.org/x/tools v0.44.0 // indirect