@@ -60,6 +60,7 @@ enum Commands {
60
60
cmd : OverrideCommands ,
61
61
} ,
62
62
/// Update RAZ to the latest version
63
+ #[ command( name = "self-update" ) ]
63
64
SelfUpdate {
64
65
/// Force update even if already on latest version
65
66
#[ arg( long) ]
@@ -919,27 +920,79 @@ async fn handle_self_update(force: bool) -> anyhow::Result<()> {
919
920
920
921
/// Get latest version from GitHub releases
921
922
async 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" ;
923
925
924
926
// Use a simple HTTPS request
925
927
let output = process:: Command :: new ( "curl" )
926
928
. args ( & [ "-s" , "-H" , "Accept: application/vnd.github.v3+json" , url] )
927
929
. output ( ) ?;
928
930
929
931
if !output. status . success ( ) {
930
- anyhow:: bail!( "Failed to fetch latest version from GitHub" ) ;
932
+ anyhow:: bail!( "Failed to fetch versions from GitHub" ) ;
931
933
}
932
934
933
935
let response = String :: from_utf8 ( output. stdout ) ?;
934
936
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) ?;
937
939
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
+ }
941
970
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
+ }
943
996
}
944
997
945
998
/// Initialize configuration for the current project
0 commit comments