@@ -60,6 +60,7 @@ enum Commands {
6060 cmd : OverrideCommands ,
6161 } ,
6262 /// Update RAZ to the latest version
63+ #[ command( name = "self-update" ) ]
6364 SelfUpdate {
6465 /// Force update even if already on latest version
6566 #[ arg( long) ]
@@ -919,27 +920,79 @@ async fn handle_self_update(force: bool) -> anyhow::Result<()> {
919920
920921/// Get latest version from GitHub releases
921922async fn get_latest_version ( ) -> anyhow:: Result < String > {
922- let url = "https://api.github.com/repos/codeitlikemiley/raz/releases/latest" ;
923+ // Get all releases, not just the "latest" (which might be wrong)
924+ let url = "https://api.github.com/repos/codeitlikemiley/raz/releases?per_page=20" ;
923925
924926 // Use a simple HTTPS request
925927 let output = process:: Command :: new ( "curl" )
926928 . args ( & [ "-s" , "-H" , "Accept: application/vnd.github.v3+json" , url] )
927929 . output ( ) ?;
928930
929931 if !output. status . success ( ) {
930- anyhow:: bail!( "Failed to fetch latest version from GitHub" ) ;
932+ anyhow:: bail!( "Failed to fetch versions from GitHub" ) ;
931933 }
932934
933935 let response = String :: from_utf8 ( output. stdout ) ?;
934936
935- // Parse JSON to get tag_name
936- let json : serde_json:: Value = serde_json:: from_str ( & response) ?;
937+ // Parse JSON to get all releases
938+ let releases : Vec < serde_json:: Value > = serde_json:: from_str ( & response) ?;
937939
938- let tag_name = json[ "tag_name" ]
939- . as_str ( )
940- . ok_or_else ( || anyhow:: anyhow!( "Failed to parse version from GitHub response" ) ) ?;
940+ if releases. is_empty ( ) {
941+ anyhow:: bail!( "No releases found" ) ;
942+ }
943+
944+ // Find the highest version that looks like a CLI release (not vscode-specific)
945+ let mut highest_version = String :: new ( ) ;
946+ let mut highest_semver = ( 0 , 0 , 0 ) ;
947+
948+ for release in releases {
949+ if let Some ( tag_name) = release[ "tag_name" ] . as_str ( ) {
950+ // Skip pre-releases unless there are no stable releases
951+ if release[ "prerelease" ] . as_bool ( ) . unwrap_or ( false ) {
952+ continue ;
953+ }
954+
955+ // Skip VS Code specific releases
956+ if tag_name. contains ( "vscode" ) {
957+ continue ;
958+ }
959+
960+ // Parse semantic version (handle v prefix)
961+ let version_str = tag_name. trim_start_matches ( 'v' ) ;
962+ if let Some ( ( major, minor, patch) ) = parse_semver ( version_str) {
963+ if ( major, minor, patch) > highest_semver {
964+ highest_semver = ( major, minor, patch) ;
965+ highest_version = tag_name. to_string ( ) ;
966+ }
967+ }
968+ }
969+ }
941970
942- Ok ( tag_name. to_string ( ) )
971+ if highest_version. is_empty ( ) {
972+ anyhow:: bail!( "No valid CLI releases found" ) ;
973+ }
974+
975+ Ok ( highest_version)
976+ }
977+
978+ /// Parse semantic version string into (major, minor, patch)
979+ fn parse_semver ( version : & str ) -> Option < ( u32 , u32 , u32 ) > {
980+ let parts: Vec < & str > = version. split ( '.' ) . collect ( ) ;
981+ if parts. len ( ) >= 3 {
982+ let major = parts[ 0 ] . parse ( ) . ok ( ) ?;
983+ let minor = parts[ 1 ] . parse ( ) . ok ( ) ?;
984+ // Handle patch versions that might have extra info (e.g., "0-alpha")
985+ let patch_str = parts[ 2 ] . split ( '-' ) . next ( ) ?;
986+ let patch = patch_str. parse ( ) . ok ( ) ?;
987+ Some ( ( major, minor, patch) )
988+ } else if parts. len ( ) == 2 {
989+ // Handle versions like "0.1" as "0.1.0"
990+ let major = parts[ 0 ] . parse ( ) . ok ( ) ?;
991+ let minor = parts[ 1 ] . parse ( ) . ok ( ) ?;
992+ Some ( ( major, minor, 0 ) )
993+ } else {
994+ None
995+ }
943996}
944997
945998/// Initialize configuration for the current project
0 commit comments