22
33import os
44import platform
5+ import re
56import shutil
7+ import subprocess # nosec: B404
68import tempfile
79from functools import lru_cache
810from pathlib import Path
1315from loguru import logger
1416from rich .console import Console
1517
18+ from .version import get_version
19+
1620
1721class RogueTuiInstaller :
1822 def __init__ (self , repo : str = "qualifire-dev/rogue" ):
@@ -42,15 +46,18 @@ def _os(self) -> str:
4246 """Get the operating system name."""
4347 return platform .system ().lower ()
4448
45- def _get_latest_github_release (self ) -> Optional [dict ]:
46- """Get the latest release information from GitHub."""
49+ def _get_release_from_github (
50+ self ,
51+ version : str ,
52+ ) -> Optional [dict ]:
53+ """Get the release information from GitHub."""
4754 console = Console ()
4855
4956 try :
50- url = f"https://api.github.com/repos/{ self ._repo } /releases/latest "
57+ url = f"https://api.github.com/repos/{ self ._repo } /releases/{ version } "
5158
5259 with console .status (
53- "[bold blue]Fetching latest release information..." ,
60+ f "[bold blue]Fetching { version } release information..." ,
5461 spinner = "dots" ,
5562 ):
5663 response = requests .get (
@@ -61,7 +68,7 @@ def _get_latest_github_release(self) -> Optional[dict]:
6168 response .raise_for_status ()
6269 return response .json ()
6370 except Exception :
64- logger .exception ("Error fetching latest release" )
71+ logger .exception (f "Error fetching { version } release" )
6572 return None
6673
6774 def _find_asset_for_platform (
@@ -83,13 +90,19 @@ def _find_asset_for_platform(
8390
8491 return None
8592
86- def _download_rogue_tui_to_temp (self ) -> str :
93+ def _download_rogue_tui_to_temp (
94+ self ,
95+ latest_version_override : bool = False ,
96+ ) -> str :
8797 console = Console ()
8898
89- # Get latest release
90- release_data = self ._get_latest_github_release ()
99+ version = "latest" if latest_version_override else f"v{ get_version ()} "
100+
101+ # Get github release
102+ console .print (f"[bold blue]Fetching { version } release information..." )
103+ release_data = self ._get_release_from_github (version )
91104 if not release_data :
92- raise Exception ("Failed to fetch latest release information." )
105+ raise Exception (f "Failed to fetch rogue-tui { version } release information." )
93106
94107 # Find appropriate asset
95108 download_url = self ._find_asset_for_platform (release_data )
@@ -165,27 +178,78 @@ def _is_rogue_tui_installed(self) -> bool:
165178 else :
166179 return False
167180
168- def install_rogue_tui (
169- self ,
170- upgrade : bool = False ,
171- ) -> bool :
172- """Install rogue-tui from GitHub releases if not already installed."""
173- console = Console ()
174- # Check if rogue-tui is already available
175- if self ._is_rogue_tui_installed () and not upgrade :
176- console .print ("[green]✅ rogue-tui is already installed.[/green]" )
181+ @lru_cache (1 )
182+ def _get_installed_tui_version (self ) -> Optional [str ]:
183+ """Get the version of the installed rogue-tui binary."""
184+ try :
185+ result = subprocess .run ( # nosec: B603 B607
186+ ["rogue-tui" , "--version" ],
187+ capture_output = True ,
188+ text = True ,
189+ timeout = 5 ,
190+ check = True ,
191+ )
192+ # Parse output like "rogue-tui v0.2.2"
193+ match = re .search (r"v?(\d+\.\d+\.\d+)" , result .stdout )
194+ if match :
195+ return match .group (1 )
196+ return None
197+ except Exception :
198+ logger .debug ("Failed to get rogue-tui version" )
199+ return None
200+
201+ @lru_cache (1 )
202+ def _should_reinstall_tui (self ) -> bool :
203+ """Check if rogue-tui should be reinstalled due to version mismatch."""
204+ installed_version = self ._get_installed_tui_version ()
205+ if not installed_version :
177206 return True
178207
179- console .print (
180- "[yellow]📦 Installing rogue-tui from GitHub releases...[/yellow]" ,
181- )
208+ current_version = get_version ("rogue-ai" )
209+ return installed_version != current_version
210+
211+ def install_rogue_tui (self ) -> bool :
212+ """Install rogue-tui from GitHub releases if not installed or needs update."""
213+ console = Console ()
214+
215+ # Check if rogue-tui is already available
216+ if self ._is_rogue_tui_installed ():
217+ # Check if version matches
218+ if not self ._should_reinstall_tui ():
219+ console .print (
220+ "[green]✅ rogue-tui is already installed and up to date.[/green]" ,
221+ )
222+ return True
223+ else :
224+ installed_version = self ._get_installed_tui_version ()
225+ current_version = get_version ("rogue-ai" )
226+ console .print (
227+ f"[yellow]📦 Updating rogue-tui from "
228+ f"v{ installed_version } to v{ current_version } ...[/yellow]" ,
229+ )
230+ else :
231+ console .print (
232+ "[yellow]📦 Installing rogue-tui from GitHub releases...[/yellow]" ,
233+ )
182234
183235 try :
184236 tmp_path = self ._download_rogue_tui_to_temp ()
185237 except Exception :
186- console .print ("[red]❌ Failed to download rogue-tui.[/red]" )
187- logger .exception ("Failed to download rogue-tui." )
188- return False
238+ logger .exception (f"Failed to download rogue-tui for { get_version ()} ." )
239+ console .print (
240+ f"[red]❌ Failed to download rogue-tui for { get_version ()} .[/red]" ,
241+ )
242+ console .print ("[yellow]Trying latest version[/yellow]" )
243+ try :
244+ tmp_path = self ._download_rogue_tui_to_temp (
245+ latest_version_override = True ,
246+ )
247+ except Exception :
248+ logger .exception ("Failed to download rogue-tui for latest version." )
249+ console .print (
250+ "[red]❌ Failed to download rogue-tui for latest version.[/red]" ,
251+ )
252+ return False
189253
190254 try :
191255 # Move to final location
0 commit comments