@@ -114,10 +114,35 @@ func runUpgrade(cmd *cobra.Command, args []string) error {
114114 fmt .Printf ("Current version: %s\n " , currentVersion )
115115 fmt .Printf ("Latest version: %s\n " , latestVersion )
116116
117- // Check if upgrade is needed
118- if ! force && currentVersion == latestVersion {
119- fmt .Println ("✅ Already running the latest version" )
120- return nil
117+ // Check if upgrade is needed using semantic version comparison
118+ if ! force {
119+ comparison , err := compareVersions (currentVersion , latestVersion )
120+ if err != nil {
121+ return fmt .Errorf ("failed to compare versions: %w" , err )
122+ }
123+
124+ // Debug: show comparison result
125+ if os .Getenv ("DEBUG" ) == "1" {
126+ fmt .Printf ("🔍 Version comparison: %d (current vs latest)\n " , comparison )
127+ }
128+
129+ if comparison > 0 {
130+ // Current version is newer than latest
131+ if strings .Contains (currentVersion , "-" ) {
132+ fmt .Printf ("✅ Already running newer version %s (dev/pre-release, latest stable is %s)\n " , currentVersion , latestVersion )
133+ } else {
134+ fmt .Printf ("✅ Already running newer version %s (latest is %s)\n " , currentVersion , latestVersion )
135+ }
136+ return nil
137+ }
138+
139+ if comparison == 0 {
140+ // Current version equals latest
141+ fmt .Println ("✅ Already running the latest version" )
142+ return nil
143+ }
144+
145+ // comparison < 0: upgrade needed, continue below
121146 }
122147
123148 if checkOnly {
@@ -185,6 +210,68 @@ func getLatestVersion(includeDev bool) (string, error) {
185210 }
186211}
187212
213+ // parseVersion extracts the base version and pre-release suffix from a version string
214+ // Examples:
215+ //
216+ // "v0.11.1-dev" -> ("0.11.1", "-dev")
217+ // "0.11.2" -> ("0.11.2", "")
218+ // "v1.2.3-rc.1" -> ("1.2.3", "-rc.1")
219+ func parseVersion (version string ) (baseVersion string , suffix string ) {
220+ // Strip leading 'v' if present
221+ version = strings .TrimPrefix (version , "v" )
222+
223+ // Split on first hyphen to separate base version from suffix
224+ if idx := strings .Index (version , "-" ); idx != - 1 {
225+ return version [:idx ], version [idx :]
226+ }
227+
228+ return version , ""
229+ }
230+
231+ // compareVersions compares two semantic versions
232+ // Returns:
233+ //
234+ // -1 if v1 < v2
235+ // 0 if v1 == v2 (comparing base versions)
236+ // 1 if v1 > v2
237+ func compareVersions (v1 , v2 string ) (int , error ) {
238+ base1 , _ := parseVersion (v1 )
239+ base2 , _ := parseVersion (v2 )
240+
241+ // Parse version components
242+ parts1 := strings .Split (base1 , "." )
243+ parts2 := strings .Split (base2 , "." )
244+
245+ // Ensure we have at least 3 parts (major.minor.patch)
246+ for len (parts1 ) < 3 {
247+ parts1 = append (parts1 , "0" )
248+ }
249+ for len (parts2 ) < 3 {
250+ parts2 = append (parts2 , "0" )
251+ }
252+
253+ // Compare each component
254+ for i := 0 ; i < 3 ; i ++ {
255+ var num1 , num2 int
256+ if _ , err := fmt .Sscanf (parts1 [i ], "%d" , & num1 ); err != nil {
257+ return 0 , fmt .Errorf ("invalid version component in %s: %s" , v1 , parts1 [i ])
258+ }
259+ if _ , err := fmt .Sscanf (parts2 [i ], "%d" , & num2 ); err != nil {
260+ return 0 , fmt .Errorf ("invalid version component in %s: %s" , v2 , parts2 [i ])
261+ }
262+
263+ if num1 < num2 {
264+ return - 1 , nil
265+ }
266+ if num1 > num2 {
267+ return 1 , nil
268+ }
269+ }
270+
271+ // Base versions are equal
272+ return 0 , nil
273+ }
274+
188275func confirmUpgrade (currentVersion , latestVersion string ) bool {
189276 // Check if we're in a TTY (interactive terminal)
190277 if ! term .IsTerminal (int (os .Stdin .Fd ())) {
0 commit comments