src/Services/SetupService.ps1
|
# SetupService — Inspección del entorno y estado de instalación. # # Lectura pura: no escribe nada, no muta state. Usa System.IO directo (no # cmdlets PS) para evitar parameter set quirks. La parte mutadora (install / # uninstall del alias) la maneja Install.ps1 — esta clase solo reporta. class SetupService { # Path del $PROFILE current. Si la sesión PS no tiene $PROFILE válido (ej: # corriendo en un host minimalista), devuelve string vacío. [string] GetProfilePath() { if ($global:PROFILE) { return [string]$global:PROFILE } return '' } # Estado del file de profile. # Devuelve hashtable @{ Path; Exists; Size }. [hashtable] GetProfileInfo() { $path = $this.GetProfilePath() if (-not $path) { return @{ Path = ''; Exists = $false; Size = 0 } } if (-not [System.IO.File]::Exists($path)) { return @{ Path = $path; Exists = $false; Size = 0 } } $info = [System.IO.FileInfo]::new($path) return @{ Path = $path Exists = $true Size = $info.Length } } # Parsea el $PROFILE buscando `function NAME` (top-level). Devuelve array # de hashtables @{ Name; Source = 'managed'|'user'; Line }. # 'managed' = generada por Install.ps1 (entre marcadores). # 'user' = otra function que el usuario tiene. [object[]] GetInstalledFunctions() { $info = $this.GetProfileInfo() if (-not $info.Exists) { return @() } $content = [System.IO.File]::ReadAllText($info.Path, [System.Text.UTF8Encoding]::new($false)) $lines = $content -split "`r?`n" $beginMarker = '# >>> repo-nav v3 — managed block (do not edit)' $endMarker = '# <<< repo-nav v3 — managed block end' $results = @() $insideManaged = $false $lineNum = 0 foreach ($line in $lines) { $lineNum++ if ($line -eq $beginMarker) { $insideManaged = $true; continue } if ($line -eq $endMarker) { $insideManaged = $false; continue } # Match top-level `function NAME` ignorando indentación dentro de # cuerpos de funciones nested (PS no permite real nesting top-level # de profile pero por las dudas matcheamos solo line-start). if ($line -match '^\s*function\s+([\w\-]+)\s*\{?') { $results += @{ Name = $Matches[1] Source = if ($insideManaged) { 'managed' } else { 'user' } Line = $lineNum } } } return $results } # ¿Está instalado el alias bajo el bloque managed? Default: 'rnav'. # Mantenemos param para facilitar tests y para el futuro feature de # "agregar otro alias" (ConfigScreen) sin acoplar el método a un nombre. [bool] IsAliasInstalled() { return $this.IsAliasInstalled('rnav') } [bool] IsAliasInstalled([string]$aliasName) { if (-not $aliasName) { return $false } $functions = $this.GetInstalledFunctions() return @($functions | Where-Object { $_.Name -eq $aliasName -and $_.Source -eq 'managed' }).Count -gt 0 } # Lista todos los aliases registrados en el bloque managed. Permite mostrar # en ConfigScreen "tenés rnav y nav instalados" cuando el user agregó varios. [string[]] GetInstalledAliases() { $functions = $this.GetInstalledFunctions() return @($functions | Where-Object Source -eq 'managed' | ForEach-Object Name) } # Path del settings JSON. [string] GetSettingsPath() { $homeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE } return Join-Path $homeDir '.repo-nav/preferences.json' } # Estado del settings file. # Devuelve hashtable @{ Path; Exists; Valid; Size }. [hashtable] GetSettingsInfo() { $path = $this.GetSettingsPath() if (-not [System.IO.File]::Exists($path)) { return @{ Path = $path; Exists = $false; Valid = $false; Size = 0 } } $info = [System.IO.FileInfo]::new($path) $valid = $true try { $raw = [System.IO.File]::ReadAllText($path, [System.Text.UTF8Encoding]::new($false)) if ($raw) { $null = $raw | ConvertFrom-Json -ErrorAction Stop } } catch { $valid = $false } return @{ Path = $path Exists = $true Valid = $valid Size = $info.Length } } # Versión de pwsh actual. [string] GetPsVersion() { if ($null -eq $global:PSVersionTable) { return 'unknown' } return [string]$global:PSVersionTable.PSVersion } # ¿Hay git en el PATH? Devuelve string version o '' si no. [string] GetGitVersion() { try { $gitCmd = Get-Command git -ErrorAction SilentlyContinue if (-not $gitCmd) { return '' } $out = & git --version 2>$null if ($LASTEXITCODE -ne 0) { return '' } return ($out -replace '^git version ', '').Trim() } catch { return '' } } # ¿Hay node en el PATH? Opcional — no se usa hoy pero los flows futuros # (npm install, package detection) lo van a necesitar. [string] GetNodeVersion() { try { $nodeCmd = Get-Command node -ErrorAction SilentlyContinue if (-not $nodeCmd) { return '' } $out = & node --version 2>$null if ($LASTEXITCODE -ne 0) { return '' } return ([string]$out).Trim().TrimStart('v') } catch { return '' } } # ¿Hay gh (GitHub CLI) en el PATH? Opcional — mejora la detección de PR # URLs del IntegrateScreen, no es crítico para el flujo principal. [string] GetGhVersion() { try { $ghCmd = Get-Command gh -ErrorAction SilentlyContinue if (-not $ghCmd) { return '' } $out = & gh --version 2>$null if ($LASTEXITCODE -ne 0) { return '' } # Output típico: "gh version 2.40.1 (2024-01-04)" $first = ([string[]]$out)[0] if ($first -match 'gh version (\S+)') { return $Matches[1] } return '' } catch { return '' } } # Snapshot del estado de dependencias. Devuelve hashtable con una entry por # dep, cada una con @{ Available, Version, Required, Notes }. La consume # ConfigScreen (toggle visual) y `repo-nav doctor` (output plano). # # Required marca si la dep es necesaria para que la app arranque vs # opcional para features puntuales (gh = PR URLs, node = futuro). [hashtable] CheckDependencies() { $psVer = $this.GetPsVersion() $psMajor = 0 if ($psVer -match '^(\d+)') { $psMajor = [int]$Matches[1] } return @{ PowerShell = @{ Available = ($psMajor -ge 7) Version = $psVer Required = $true Notes = if ($psMajor -lt 7) { 'repo-nav v3 necesita pwsh 7+ (no Windows PowerShell 5.1).' } else { '' } } Git = @{ Available = [bool]$this.GetGitVersion() Version = $this.GetGitVersion() Required = $true Notes = if (-not $this.GetGitVersion()) { 'Instalá git desde https://git-scm.com.' } else { '' } } Node = @{ Available = [bool]$this.GetNodeVersion() Version = $this.GetNodeVersion() Required = $false Notes = if (-not $this.GetNodeVersion()) { 'Opcional — para flows npm futuros.' } else { '' } } Gh = @{ Available = [bool]$this.GetGhVersion() Version = $this.GetGhVersion() Required = $false Notes = if (-not $this.GetGhVersion()) { 'Opcional — mejora la detección de PR URLs.' } else { '' } } } } # Path del repo donde vive este servicio (resuelve dinámicamente desde el # archivo del SetupService → src/Services/ → repo root). [string] GetRepoRoot() { # Si está cargado vía dot-source, $PSScriptRoot apunta al folder del # archivo. Subimos 2 niveles: Services → src → repo root. if (-not $script:RNRoot) { # Fallback: usar Get-Location, no ideal pero defensa. return (Get-Location).Path } return $script:RNRoot } } |