@@ -5,24 +5,44 @@ import java.net.URL
55import java.util.Properties
66import java.util.logging.Logger
77
8+ /* *
9+ * Resolved release channel that [UpdateChecker] should probe. The user-facing
10+ * preference (with `OFF`) lives in the GUI layer; this enum only carries the
11+ * two channels that actually map to a network probe.
12+ */
13+ enum class ReleaseChannel { STABLE , DEV }
14+
15+ data class UpdateInfo (
16+ val currentVersion : String ,
17+ val latestVersion : String ,
18+ val crossesDevToStable : Boolean ,
19+ val downloadLink : String ,
20+ )
21+
822object UpdateChecker {
9- fun check (logger : Logger ): String? {
23+ /* *
24+ * Probe GitHub for the latest version on the given channel. When [channel]
25+ * is null, derives the channel from the running build's version (legacy
26+ * behavior — preserved so the CLI's existing call site keeps working).
27+ * Synchronous — call from a background dispatcher.
28+ */
29+ fun checkInfo (logger : Logger , channel : ReleaseChannel ? = null): UpdateInfo ? {
1030 try {
11- // Current version of this CLI.
1231 val currentVersion = javaClass.getResourceAsStream(" /app/morphe/cli/version.properties" )
1332 ?.use { stream ->
1433 Properties ().apply { load(stream) }.getProperty(" version" )
1534 } ? : return null
1635
17- // Check if the user is using dev or stable release.
1836 val isDev = currentVersion.contains(" dev" )
37+ val resolvedChannel = channel ? : if (isDev) ReleaseChannel .DEV else ReleaseChannel .STABLE
1938
20- val url = if (isDev) {
21- // If on dev and a new stable release is available, then this
22- // ref still is correct because after a stable release dev branch is same as main.
23- " https://raw.githubusercontent.com/MorpheApp/morphe-cli/refs/heads/dev/gradle.properties"
24- } else {
25- " https://raw.githubusercontent.com/MorpheApp/morphe-cli/refs/heads/main/gradle.properties"
39+ val url = when (resolvedChannel) {
40+ // dev branch tracks main after every stable release, so probing
41+ // dev also catches new stables for users on dev builds.
42+ ReleaseChannel .DEV ->
43+ " https://raw.githubusercontent.com/MorpheApp/morphe-cli/refs/heads/dev/gradle.properties"
44+ ReleaseChannel .STABLE ->
45+ " https://raw.githubusercontent.com/MorpheApp/morphe-cli/refs/heads/main/gradle.properties"
2646 }
2747
2848 val connection = URL (url).openConnection() as HttpURLConnection
@@ -35,28 +55,50 @@ object UpdateChecker {
3555 load(response.byteInputStream())
3656 }.getProperty(" version" ) ? : return null
3757
38- if (latestVersion != currentVersion) {
39- // Warning message for when the user is to about to move from dev -> stable edge case.
40- val trackChangesMessage = if (isDev && ! latestVersion.contains(" dev" )){
41- " \n Notice: The latest CLI is a stable release. Updating to that will stop dev " +
42- " update notifications. To keep receiving dev updates, skip stable update " +
43- " and wait for the next dev release."
44- } else " "
45-
46- val downloadLink = if (isDev) {
47- " https://github.com/MorpheApp/morphe-cli/releases/"
48- } else {
49- " https://github.com/MorpheApp/morphe-cli/releases/latest"
50- }
51-
52- return " Update available: v$latestVersion (current: v$currentVersion )" +
53- " $trackChangesMessage \n Download from $downloadLink "
58+ if (latestVersion == currentVersion) return null
59+
60+ val downloadLink = when (resolvedChannel) {
61+ ReleaseChannel .DEV -> " https://github.com/MorpheApp/morphe-cli/releases/"
62+ ReleaseChannel .STABLE -> " https://github.com/MorpheApp/morphe-cli/releases/latest"
5463 }
55- return null
5664
65+ return UpdateInfo (
66+ currentVersion = currentVersion,
67+ latestVersion = latestVersion,
68+ crossesDevToStable = isDev && ! latestVersion.contains(" dev" ),
69+ downloadLink = downloadLink,
70+ )
5771 } catch (ex: Exception ) {
5872 logger.fine(" Could not check for CLI update: $ex " )
5973 return null
6074 }
6175 }
62- }
76+
77+ /* *
78+ * Read the current build's version from the bundled resource. Returns null
79+ * when the resource is missing (development environments without the
80+ * processed properties file). Used by the GUI to decide a smart default
81+ * for the update channel preference.
82+ */
83+ fun currentVersion (): String? = javaClass.getResourceAsStream(" /app/morphe/cli/version.properties" )
84+ ?.use { stream ->
85+ Properties ().apply { load(stream) }.getProperty(" version" )
86+ }
87+
88+ /* *
89+ * Legacy formatter — returns the same multi-line string the CLI prints.
90+ * Kept byte-identical so [app.morphe.cli.command.PatchCommand]'s logger
91+ * output doesn't change.
92+ */
93+ fun check (logger : Logger ): String? {
94+ val info = checkInfo(logger) ? : return null
95+ val trackChangesMessage = if (info.crossesDevToStable) {
96+ " \n Notice: The latest CLI is a stable release. Updating to that will stop dev " +
97+ " update notifications. To keep receiving dev updates, skip stable update " +
98+ " and wait for the next dev release."
99+ } else " "
100+
101+ return " Update available: v${info.latestVersion} (current: v${info.currentVersion} )" +
102+ " $trackChangesMessage \n Download from ${info.downloadLink} "
103+ }
104+ }
0 commit comments