Skip to content

Commit 788c588

Browse files
committed
feat(install): support Windows (PowerShell) + durcissement Linux
L'installeur couvre désormais les trois plateformes : - Windows : nouveau scripts/install.ps1 — installe Docker Desktop (DL officiel silencieux + WSL2, redémarrage guidé si besoin), démarre, génère les secrets (.env UTF-8 sans BOM), lance la stack, ouvre /setup. Dépose un update.ps1. irm .../install.ps1 | iex - Linux : install.sh bascule sur « sudo docker » quand le groupe docker n'est pas encore actif dans la session (cas juste après install), gère systemctl OU service pour démarrer le daemon, et avertit pour usermod -aG docker. - install.sh : si lancé sous un shell Windows (MINGW/MSYS/Cygwin), redirige vers la commande PowerShell au lieu d'échouer. - update.sh : même fallback sudo. - README + doc : les 3 commandes (macOS/Linux/Windows), note WSL2/redémarrage. Note : les scripts sont servis depuis main (URL raw), pas dans l'image — le merge suffit, pas de nouvelle image. Chemin macOS vérifié de bout en bout ; syntaxe PowerShell + logique secrets/.env validées sur pwsh ; chemins Docker-absent (Win/Linux) logiques mais non exécutés sur ces OS.
1 parent 169b195 commit 788c588

4 files changed

Lines changed: 254 additions & 15 deletions

File tree

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,17 +249,26 @@ et [`docs/architecture/data-model.md`](./docs/architecture/data-model.md).
249249
250250
### Installation en une commande (recommandée)
251251

252+
**macOS / Linux :**
253+
252254
```bash
253255
curl -fsSL https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.sh | bash
254256
```
255257

258+
**Windows (PowerShell) :**
259+
260+
```powershell
261+
irm https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.ps1 | iex
262+
```
263+
256264
Images pré-buildées (GHCR), secrets générés automatiquement, schéma appliqué
257265
au démarrage — puis l'**assistant de premier lancement** (`/setup`) guide la
258266
création du compte admin et la première clé IA dans le navigateur. Détails et
259267
mise à jour : [docs/installation/one-command.md](./docs/installation/one-command.md).
260268

261269
> Aucun prérequis à installer soi-même : le script installe [Docker](https://docs.docker.com/get-docker/)
262-
> s'il manque (Docker Desktop sur macOS, Docker Engine sur Linux).
270+
> s'il manque (Docker Desktop sur macOS/Windows, Docker Engine sur Linux).
271+
> Sur Windows neuf, WSL2 peut exiger un redémarrage — relancez alors la commande.
263272
> Node.js n'est nécessaire que pour l'installation depuis les sources ci-dessous.
264273
265274
### Installation depuis les sources (développement)

docs/installation/one-command.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,30 @@ La façon la plus simple d'installer Louis sur une machine (poste de cabinet,
44
serveur, VPS). **Aucun prérequis à installer soi-même** : si Docker manque, le
55
script l'installe.
66

7+
### macOS / Linux
8+
79
```bash
810
curl -fsSL https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.sh | bash
911
```
1012

11-
Le script :
13+
### Windows (PowerShell)
14+
15+
Ouvrez **PowerShell** et collez :
16+
17+
```powershell
18+
irm https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.ps1 | iex
19+
```
1220

13-
1. **vérifie Docker** — l'installe s'il manque (Docker Desktop sur macOS,
14-
Docker Engine sur Linux) et le démarre s'il est arrêté. Au premier
15-
lancement de Docker Desktop sur Mac, une fenêtre demande d'accepter les
21+
> Sur une machine Windows neuve, Docker Desktop s'appuie sur **WSL2** : le
22+
> script l'installe, mais Windows peut demander **un redémarrage** pour le
23+
> finaliser. Dans ce cas, redémarrez puis relancez la même commande — le
24+
> script reprend là où il en était.
25+
26+
Le script (les trois plateformes) :
27+
28+
1. **vérifie Docker** — l'installe s'il manque (Docker Desktop sur macOS et
29+
Windows, Docker Engine sur Linux) et le démarre s'il est arrêté. Au premier
30+
lancement de Docker Desktop, une fenêtre demande d'accepter les
1631
conditions : un clic, et le script reprend tout seul ;
1732
2. crée un dossier `./louis` contenant le `docker-compose.prod.yml` et un
1833
fichier `.env` avec des **secrets générés aléatoirement** (jamais écrasés
@@ -53,7 +68,10 @@ curl -fsSL https://raw.githubusercontent.com/Association-DataRing/Louis/main/scr
5368
Ou, depuis le dossier d'installation, le script déposé à l'install :
5469

5570
```bash
56-
cd louis && ./update.sh
71+
cd louis && ./update.sh # macOS / Linux
72+
```
73+
```powershell
74+
cd louis; .\update.ps1 # Windows
5775
```
5876

5977
Dans les deux cas, le service `migrate` ré-applique le schéma (idempotent)

scripts/install.ps1

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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

scripts/install.sh

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#!/usr/bin/env bash
22
# ─────────────────────────────────────────────────────────────────────────────
3-
# Louis — installeur une commande
3+
# Louis — installeur une commande (macOS + Linux)
44
#
55
# curl -fsSL https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.sh | bash
66
#
7+
# Windows : utilisez plutôt scripts/install.ps1 (PowerShell) :
8+
# irm https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.ps1 | iex
9+
#
710
# Ce script :
8-
# 1. vérifie que Docker (+ plugin compose) est installé et démarré ;
11+
# 1. installe Docker s'il manque (Docker Desktop sur macOS, Docker Engine
12+
# sur Linux), le démarre s'il est arrêté ;
913
# 2. crée un dossier ./louis avec le docker-compose de production ;
1014
# 3. génère les secrets (.env) — jamais écrasés s'ils existent déjà ;
1115
# 4. télécharge les images et démarre la stack (app, Postgres+pgvector,
@@ -26,6 +30,9 @@ LOUIS_DIR="${LOUIS_DIR:-louis}"
2630
LOUIS_PORT="${LOUIS_PORT:-3000}"
2731
LOUIS_REPO_RAW="${LOUIS_REPO_RAW:-https://raw.githubusercontent.com/Association-DataRing/Louis/main}"
2832
COMPOSE_FILE="docker-compose.prod.yml"
33+
# Préfixe des commandes docker : passe à « sudo docker » sur Linux quand le
34+
# groupe « docker » n'est pas encore actif dans la session (cf. docker_ready).
35+
DOCKER="docker"
2936

3037
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
3138
log() { printf ' %s\n' "$*"; }
@@ -41,24 +48,39 @@ rand_secret() {
4148
fi
4249
}
4350

51+
# Vrai si le daemon Docker répond, en fixant $DOCKER (« docker » ou « sudo
52+
# docker »). Sur Linux, juste après l'install, l'utilisateur n'est pas encore
53+
# dans le groupe « docker » (effectif au prochain login) → on bascule sur sudo
54+
# pour cette session. Les identifiants sudo sont en général encore en cache
55+
# (l'install via get.docker.com vient de les demander).
56+
docker_ready() {
57+
if docker info >/dev/null 2>&1; then DOCKER="docker"; return 0; fi
58+
if command -v sudo >/dev/null 2>&1 && sudo -n docker info >/dev/null 2>&1; then
59+
DOCKER="sudo docker"; return 0
60+
fi
61+
return 1
62+
}
63+
4464
# Attend que le daemon Docker réponde (jusqu'à ~3 min), en le démarrant au
4565
# besoin. Docker Desktop (Mac) demande au premier lancement d'accepter les
4666
# conditions dans une fenêtre — d'où le message, puis on attend que le
4767
# daemon soit prêt et on poursuit automatiquement.
4868
wait_for_docker() {
49-
if docker info >/dev/null 2>&1; then return 0; fi
69+
if docker_ready; then return 0; fi
5070
case "$(uname -s)" in
5171
Darwin)
5272
open -a Docker >/dev/null 2>&1 || open -a "Docker Desktop" >/dev/null 2>&1 || true
5373
log "Docker démarre — si une fenêtre s'ouvre, cliquez « Accepter » pour finaliser." ;;
5474
Linux)
5575
if command -v systemctl >/dev/null 2>&1; then
5676
sudo systemctl start docker >/dev/null 2>&1 || true
77+
elif command -v service >/dev/null 2>&1; then
78+
sudo service docker start >/dev/null 2>&1 || true
5779
fi ;;
5880
esac
5981
printf ' … attente du démarrage de Docker'
6082
for _ in $(seq 1 90); do
61-
if docker info >/dev/null 2>&1; then printf '\n'; return 0; fi
83+
if docker_ready; then printf '\n'; return 0; fi
6284
printf '.'; sleep 2
6385
done
6486
printf '\n'
@@ -110,14 +132,20 @@ ensure_docker() {
110132
case "$(uname -s)" in
111133
Darwin) install_docker_mac ;;
112134
Linux) install_docker_linux ;;
135+
MINGW*|MSYS*|CYGWIN*|Windows_NT)
136+
fail "Vous êtes sous Windows. Utilisez la commande PowerShell à la place :
137+
irm https://raw.githubusercontent.com/Association-DataRing/Louis/main/scripts/install.ps1 | iex" ;;
113138
*) fail "OS non reconnu. Installez Docker manuellement : https://docs.docker.com/get-docker/" ;;
114139
esac
115140
fi
116141
wait_for_docker \
117142
|| fail "Docker ne répond pas. Lancez Docker Desktop, attendez qu'il soit prêt (icône fixe), puis relancez ce script."
118-
docker compose version >/dev/null 2>&1 \
143+
$DOCKER compose version >/dev/null 2>&1 \
119144
|| fail "Docker Compose v2 absent. Mettez Docker Desktop à jour (il l'inclut)."
120145
ok "Docker opérationnel"
146+
if [ "$DOCKER" = "sudo docker" ]; then
147+
warn "Docker tourne via sudo (groupe « docker » pas encore actif). Pour l'éviter ensuite : sudo usermod -aG docker \"\$USER\" puis reconnectez-vous."
148+
fi
121149
}
122150

123151
bold "Louis — installation"
@@ -143,9 +171,11 @@ cat > update.sh <<'UPD'
143171
# (base, documents) et vos secrets (.env) sont conservés.
144172
set -euo pipefail
145173
cd "$(dirname "$0")"
174+
DOCKER="docker"
175+
docker info >/dev/null 2>&1 || DOCKER="sudo docker"
146176
echo "Mise à jour de Louis…"
147-
docker compose -f docker-compose.prod.yml pull
148-
docker compose -f docker-compose.prod.yml up -d
177+
$DOCKER compose -f docker-compose.prod.yml pull
178+
$DOCKER compose -f docker-compose.prod.yml up -d
149179
echo "Louis est à jour."
150180
UPD
151181
chmod +x update.sh
@@ -173,8 +203,8 @@ fi
173203

174204
# 4. Démarrage ────────────────────────────────────────────────────────────────
175205
bold "Téléchargement des images (premier lancement : quelques minutes)…"
176-
docker compose -f "$COMPOSE_FILE" pull --quiet
177-
docker compose -f "$COMPOSE_FILE" up -d
206+
$DOCKER compose -f "$COMPOSE_FILE" pull --quiet
207+
$DOCKER compose -f "$COMPOSE_FILE" up -d
178208
ok "Stack démarrée"
179209

180210
# 5. Attente de l'app ─────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)