|
| 1 | +#Requires -Version 5 |
| 2 | +<# |
| 3 | + Louis — installeur une commande (Windows / PowerShell) |
| 4 | +
|
| 5 | + irm https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.ps1 | iex |
| 6 | +
|
| 7 | + Ce script : |
| 8 | + 1. installe Docker Desktop s'il manque (téléchargement officiel, WSL2), |
| 9 | + le démarre s'il est arrêté ; |
| 10 | + 2. crée un dossier .\louis avec le docker-compose de production ; |
| 11 | + 3. génère les secrets (.env) — jamais écrasés s'ils existent déjà ; |
| 12 | + 4. télécharge les images et démarre la stack ; |
| 13 | + 5. attend que l'app réponde puis ouvre l'assistant de premier lancement. |
| 14 | +
|
| 15 | + Variables d'environnement optionnelles (mêmes que la version bash) : |
| 16 | + LOUIS_DIR, LOUIS_PORT, LOUIS_VERSION, LOUIS_REPO_RAW |
| 17 | +
|
| 18 | + Relancer le script est sans danger : il est idempotent et vaut mise à jour. |
| 19 | +#> |
| 20 | +$ErrorActionPreference = "Stop" |
| 21 | + |
| 22 | +$LouisDir = if ($env:LOUIS_DIR) { $env:LOUIS_DIR } else { "louis" } |
| 23 | +$LouisPort = if ($env:LOUIS_PORT) { $env:LOUIS_PORT } else { "3000" } |
| 24 | +$RepoRaw = if ($env:LOUIS_REPO_RAW) { $env:LOUIS_REPO_RAW } else { "https://raw.githubusercontent.com/Association-DataRing/Louis/main" } |
| 25 | +$ComposeFile = "docker-compose.prod.yml" |
| 26 | + |
| 27 | +function Bold($m) { Write-Host $m -ForegroundColor White } |
| 28 | +function Info($m) { Write-Host " $m" } |
| 29 | +function Ok($m) { Write-Host " [OK] $m" -ForegroundColor Green } |
| 30 | +function Warn($m) { Write-Host " [!] $m" -ForegroundColor Yellow } |
| 31 | +function Die($m) { Write-Host " [X] $m" -ForegroundColor Red; exit 1 } |
| 32 | + |
| 33 | +# Secret cryptographique base64 (32 octets). |
| 34 | +function New-Secret { |
| 35 | + $bytes = New-Object 'System.Byte[]' 32 |
| 36 | + [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) |
| 37 | + return [Convert]::ToBase64String($bytes) |
| 38 | +} |
| 39 | +# Variante alphanumérique (sûre dans une URL de connexion Postgres / clé S3). |
| 40 | +function New-AlnumSecret { |
| 41 | + return ((New-Secret) -replace '[/+=]', '').Substring(0, 32) |
| 42 | +} |
| 43 | + |
| 44 | +function Test-Docker { |
| 45 | + if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { return $false } |
| 46 | + & docker info *> $null |
| 47 | + return ($LASTEXITCODE -eq 0) |
| 48 | +} |
| 49 | + |
| 50 | +function Start-DockerDesktop { |
| 51 | + foreach ($p in @( |
| 52 | + (Join-Path $env:ProgramFiles "Docker\Docker\Docker Desktop.exe"), |
| 53 | + (Join-Path ${env:ProgramFiles(x86)} "Docker\Docker\Docker Desktop.exe") |
| 54 | + )) { |
| 55 | + if ($p -and (Test-Path $p)) { Start-Process $p | Out-Null; return } |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +function Wait-Docker { |
| 60 | + Info "Attente du démarrage de Docker (jusqu'à 3 min)..." |
| 61 | + for ($i = 0; $i -lt 90; $i++) { |
| 62 | + if (Test-Docker) { return $true } |
| 63 | + Start-Sleep -Seconds 2 |
| 64 | + } |
| 65 | + return $false |
| 66 | +} |
| 67 | + |
| 68 | +function Install-DockerWindows { |
| 69 | + Bold "Docker n'est pas installé — installation de Docker Desktop (~600 Mo)..." |
| 70 | + $installer = Join-Path $env:TEMP "DockerDesktopInstaller.exe" |
| 71 | + $url = "https://desktop.docker.com/win/main/amd64/Docker Desktop Installer.exe" |
| 72 | + try { |
| 73 | + Invoke-WebRequest -Uri $url -OutFile $installer -UseBasicParsing |
| 74 | + } catch { |
| 75 | + Die "Téléchargement de Docker échoué. Installez-le manuellement : https://docs.docker.com/desktop/install/windows-install/" |
| 76 | + } |
| 77 | + Info "Installation silencieuse (peut demander des droits administrateur)..." |
| 78 | + $proc = Start-Process -FilePath $installer -ArgumentList @("install", "--quiet", "--accept-license") -Wait -PassThru -Verb RunAs |
| 79 | + if ($proc.ExitCode -ne 0) { |
| 80 | + Die ("Installation de Docker Desktop échouée (code {0}). Installez-le manuellement : https://docs.docker.com/desktop/install/windows-install/" -f $proc.ExitCode) |
| 81 | + } |
| 82 | + Ok "Docker Desktop installé" |
| 83 | +} |
| 84 | + |
| 85 | +function Ensure-Docker { |
| 86 | + if (Test-Docker) { Ok "Docker opérationnel"; return } |
| 87 | + |
| 88 | + if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { |
| 89 | + Install-DockerWindows |
| 90 | + } |
| 91 | + Start-DockerDesktop |
| 92 | + Info "Si une fenêtre Docker s'ouvre, cliquez « Accept » pour finaliser." |
| 93 | + if (-not (Wait-Docker)) { |
| 94 | + Die @" |
| 95 | +Docker ne répond pas encore. Sur une machine fraîche, WSL2 nécessite souvent |
| 96 | +un redémarrage de Windows pour se finaliser. |
| 97 | + -> Redémarrez Windows, puis relancez cette commande. |
| 98 | +(Ou lancez « Docker Desktop » manuellement, attendez l'icône fixe, et relancez.) |
| 99 | +"@ |
| 100 | + } |
| 101 | + & docker compose version *> $null |
| 102 | + if ($LASTEXITCODE -ne 0) { Die "Docker Compose v2 absent. Mettez Docker Desktop à jour (il l'inclut)." } |
| 103 | + Ok "Docker opérationnel" |
| 104 | +} |
| 105 | + |
| 106 | +# ───────────────────────────────────────────────────────────────────────────── |
| 107 | +Bold "Louis — installation (Windows)" |
| 108 | + |
| 109 | +# 1. Docker (installé/démarré automatiquement) |
| 110 | +Ensure-Docker |
| 111 | + |
| 112 | +# 2. Dossier + compose (toujours rafraîchi : ré-exécuter = mise à jour) |
| 113 | +New-Item -ItemType Directory -Force -Path $LouisDir | Out-Null |
| 114 | +Set-Location $LouisDir |
| 115 | +try { |
| 116 | + Invoke-WebRequest -Uri "$RepoRaw/$ComposeFile" -OutFile $ComposeFile -UseBasicParsing |
| 117 | +} catch { |
| 118 | + Die "Téléchargement de $ComposeFile impossible depuis $RepoRaw" |
| 119 | +} |
| 120 | +Ok "$ComposeFile à jour" |
| 121 | + |
| 122 | +# Script de mise à jour pratique déposé dans le dossier. |
| 123 | +@' |
| 124 | +$ErrorActionPreference = "Stop" |
| 125 | +Set-Location $PSScriptRoot |
| 126 | +Write-Host "Mise a jour de Louis..." |
| 127 | +docker compose -f docker-compose.prod.yml pull |
| 128 | +docker compose -f docker-compose.prod.yml up -d |
| 129 | +Write-Host "Louis est a jour." |
| 130 | +'@ | Set-Content -Path "update.ps1" -Encoding ascii |
| 131 | + |
| 132 | +# 3. Secrets (.env) — jamais écrasés (ENCRYPTION_KEY est irremplaçable) |
| 133 | +if (-not (Test-Path ".env")) { |
| 134 | + $lines = @( |
| 135 | + "# Secrets Louis - NE PAS PARTAGER. ENCRYPTION_KEY est irremplacable.", |
| 136 | + ("POSTGRES_PASSWORD={0}" -f (New-AlnumSecret)), |
| 137 | + ("S3_SECRET_ACCESS_KEY={0}" -f (New-AlnumSecret)), |
| 138 | + ("AUTH_SECRET={0}" -f (New-Secret)), |
| 139 | + ("ENCRYPTION_KEY={0}" -f (New-Secret)), |
| 140 | + ("LOUIS_PORT={0}" -f $LouisPort) |
| 141 | + ) |
| 142 | + if ($env:LOUIS_VERSION) { $lines += ("LOUIS_VERSION={0}" -f $env:LOUIS_VERSION) } |
| 143 | + # UTF-8 sans BOM pour ne pas polluer la 1re variable lue par docker compose. |
| 144 | + [System.IO.File]::WriteAllText( |
| 145 | + (Join-Path (Get-Location) ".env"), |
| 146 | + (($lines -join "`n") + "`n"), |
| 147 | + (New-Object System.Text.UTF8Encoding($false)) |
| 148 | + ) |
| 149 | + Ok "Secrets générés dans $LouisDir\.env (à sauvegarder précieusement)" |
| 150 | +} else { |
| 151 | + Ok ".env déjà présent (conservé)" |
| 152 | +} |
| 153 | + |
| 154 | +# 4. Démarrage |
| 155 | +Bold "Téléchargement des images (premier lancement : quelques minutes)..." |
| 156 | +& docker compose -f $ComposeFile pull |
| 157 | +& docker compose -f $ComposeFile up -d |
| 158 | +Ok "Stack démarrée" |
| 159 | + |
| 160 | +# 5. Attente de l'app |
| 161 | +Info "Démarrage de Louis..." |
| 162 | +$healthy = $false |
| 163 | +for ($i = 0; $i -lt 90; $i++) { |
| 164 | + try { |
| 165 | + $r = Invoke-WebRequest -Uri ("http://localhost:{0}/api/health" -f $LouisPort) -UseBasicParsing -TimeoutSec 3 |
| 166 | + if ($r.StatusCode -eq 200) { $healthy = $true; break } |
| 167 | + } catch {} |
| 168 | + Start-Sleep -Seconds 2 |
| 169 | +} |
| 170 | + |
| 171 | +if (-not $healthy) { |
| 172 | + Die ("Louis ne répond pas après ~3 min. Diagnostic : cd {0}; docker compose -f {1} logs app migrate" -f $LouisDir, $ComposeFile) |
| 173 | +} |
| 174 | + |
| 175 | +Write-Host "" |
| 176 | +Bold "Installation terminée." |
| 177 | +Write-Host (" Ouvrez http://localhost:{0} — l'assistant de premier lancement vous guide." -f $LouisPort) |
| 178 | +Write-Host (" (compte administrateur, clé IA, et c'est prêt)") |
| 179 | +Write-Host "" |
| 180 | +Write-Host (" Mettre à jour : relancez cette commande, ou exécutez {0}\update.ps1" -f $LouisDir) |
| 181 | +Write-Host (" Arrêt : cd {0}; docker compose -f {1} down" -f $LouisDir, $ComposeFile) |
| 182 | +Start-Process ("http://localhost:{0}" -f $LouisPort) | Out-Null |
0 commit comments