Fix : widows giea 2
All checks were successful
release / build (push) Successful in 16s
release / verify-windows (push) Successful in 1m13s

This commit is contained in:
jeanotx32
2026-08-11 22:48:15 +02:00
parent c9ab3dafb4
commit 6a45b3063f
2 changed files with 79 additions and 54 deletions

View File

@@ -1,16 +1,27 @@
<#
ATTENTION : ce fichier doit rester en ASCII pur, sans accent ni typographie.
Il est publie sans BOM et execute par Windows PowerShell 5.1, qui relit alors
un .ps1 dans la page de codes ANSI. Un caractere non-ASCII y devient illisible,
et certains cassent carrement le parseur : le tiret cadratin se relit en CP1252
comme trois caracteres dont le dernier, U+201D, est un delimiteur de chaine
valide en PowerShell. La chaine se ferme en plein milieu de la ligne, et
l'erreur remontee est une accolade manquante ailleurs dans le fichier.
La CI verifie cette regle avant le controle de syntaxe.
.SYNOPSIS
Installe (ou met à jour) l'agent Stream Control sur une VM Windows.
Installe (ou met a jour) l'agent Stream Control sur une VM Windows.
.DESCRIPTION
L'agent est enregistré comme tâche planifiée « à l'ouverture de session », et
L'agent est enregistre comme tache planifiee 'a l'ouverture de session', et
non comme service Windows : un service tourne en session 0 et ne verrait ni la
fenêtre du navigateur, ni OBS.
fenetre du navigateur, ni OBS.
Réexécuter le script met à jour le binaire et applique les options fournies.
L'identité de l'agent (jeton permanent, agentId) est préservée : sans cela il
réapparaîtrait en double dans le dashboard. Utiliser -ResetIdentity pour forcer
un réenrôlement.
Reexecuter le script met a jour le binaire et applique les options fournies.
L'identite de l'agent (jeton permanent, agentId) est preservee : sans cela il
reapparaitrait en double dans le dashboard. Utiliser -ResetIdentity pour forcer
un reenrolement.
.EXAMPLE
& ([scriptblock]::Create((irm 'https://git.jeanbonapp.com/api/packages/jeanbon/generic/stream-control-agent/latest/install-agent.ps1'))) `
@@ -34,28 +45,28 @@ param(
[string] $InstallDir = 'C:\stream-control-agent',
[string] $RunAsUser = "$env:USERDOMAIN\$env:USERNAME",
[string] $TaskName = 'StreamControlAgent',
# Réenrôle avec le -Token fourni : efface l'identité existante, ce qui crée un
# nouvel agent dans le dashboard. Sans ce commutateur, -Token est ignoré sur
# une mise à jour.
# Reenrole avec le -Token fourni : efface l'identite existante, ce qui cree un
# nouvel agent dans le dashboard. Sans ce commutateur, -Token est ignore sur
# une mise a jour.
[switch] $ResetIdentity
)
$ErrorActionPreference = 'Stop'
$Registry = $Registry.TrimEnd('/')
function Info($msg) { Write-Host "· $msg" }
function Die($msg) { Write-Host " $msg" -ForegroundColor Red; exit 1 }
function Info($msg) { Write-Host "- $msg" }
function Die($msg) { Write-Host "[ECHEC] $msg" -ForegroundColor Red; exit 1 }
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) { Die 'À exécuter dans un PowerShell administrateur.' }
if (-not $isAdmin) { Die 'A executer dans un PowerShell administrateur.' }
$configFile = Join-Path $InstallDir 'agent.config.json'
$isUpgrade = Test-Path $configFile
if (-not $isUpgrade) {
if (-not $Server) { Die '-Server est obligatoire pour une première installation.' }
if (-not $Token) { Die '-Token est obligatoire pour une première installation.' }
if (-not $Server) { Die '-Server est obligatoire pour une premiere installation.' }
if (-not $Token) { Die '-Token est obligatoire pour une premiere installation.' }
}
Write-Host ''
@@ -69,65 +80,65 @@ Write-Host ''
$nodeMajor = 0
$nodeCmd = Get-Command node -ErrorAction SilentlyContinue
if ($nodeCmd) {
# On lit `node --version` plutôt que d'évaluer une expression JavaScript.
# PowerShell 5.1 perd les guillemets doubles imbriqués quand il passe un
# argument à un exécutable natif : `node -p 'x.split(".")[0]'` arrive côté
# Node comme `x.split(.)[0]`, qui lève une SyntaxError. Le majeur valait
# alors 0 et le script réinstallait Node sur une machine déjà équipée.
# On lit `node --version` plutot que d'evaluer une expression JavaScript.
# PowerShell 5.1 perd les guillemets doubles imbriques quand il passe un
# argument a un executable natif : `node -p 'x.split(".")[0]'` arrive cote
# Node comme `x.split(.)[0]`, qui leve une SyntaxError. Le majeur valait
# alors 0 et le script reinstallait Node sur une machine deja equipee.
$nodeRaw = (& node --version 2>$null)
if ($nodeRaw -match '^v(\d+)\.') { $nodeMajor = [int]$Matches[1] }
}
if ($nodeMajor -lt 22) {
Info "Node.js 22+ requis (trouvé : $nodeMajor) installation via winget"
Info "Node.js 22+ requis (trouve : $nodeMajor) - installation via winget"
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
Die 'winget introuvable. Installe Node.js 22+ manuellement puis relance : https://nodejs.org/'
}
& winget install --id OpenJS.NodeJS.LTS --silent --accept-source-agreements --accept-package-agreements
# winget ne rafraîchit pas le PATH de la session courante.
# winget ne rafraichit pas le PATH de la session courante.
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
[Environment]::GetEnvironmentVariable('Path', 'User')
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Die 'Node.js installé mais absent du PATH rouvre PowerShell et relance le script.'
Die 'Node.js installe mais absent du PATH - rouvre PowerShell et relance le script.'
}
} else {
Info "Node.js $(& node -v) présent"
Info "Node.js $(& node -v) present"
}
# --- Téléchargement du bundle -----------------------------------------------
# --- Telechargement du bundle -----------------------------------------------
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
$url = "$Registry/api/packages/$Owner/generic/stream-control-agent/$Version/agent.cjs"
Info "Téléchargement depuis $url"
Info "Telechargement depuis $url"
$headers = @{}
if ($PackageToken) { $headers['Authorization'] = "token $PackageToken" }
# Le fichier temporaire doit garder l'extension .cjs : `node --check` déduit le
# format du module de l'extension et rejette tout ce qu'il ne reconnaît pas
# (« Unknown file extension .tmp »), quel que soit le contenu.
# Le fichier temporaire doit garder l'extension .cjs : `node --check` deduit le
# format du module de l'extension et rejette tout ce qu'il ne reconnait pas
# ('Unknown file extension .tmp'), quel que soit le contenu.
$tmp = Join-Path $InstallDir 'agent-new.cjs'
try {
Invoke-WebRequest -Uri $url -Headers $headers -OutFile $tmp -UseBasicParsing
} catch {
Die "Téléchargement impossible ($($_.Exception.Message)). Vérifie -Registry, -Version, et -PackageToken si le paquet est privé."
Die "Telechargement impossible ($($_.Exception.Message)). Verifie -Registry, -Version, et -PackageToken si le paquet est prive."
}
$checkErr = (& node --check $tmp 2>&1 | Out-String)
if ($LASTEXITCODE -ne 0) {
Write-Host $checkErr
Write-Host " (fichier conservé pour inspection : $tmp)"
Die 'Le fichier téléchargé n''est pas un script Node valide voir l''erreur ci-dessus.'
Write-Host " (fichier conserve pour inspection : $tmp)"
Die 'Le fichier telecharge n''est pas un script Node valide - voir l''erreur ci-dessus.'
}
Move-Item -Force $tmp (Join-Path $InstallDir 'agent.cjs')
# --- Configuration ----------------------------------------------------------
if ($isUpgrade) {
# Fusion, et non conservation en bloc : l'identité (jeton, agentId) est
# préservée l'écraser ferait réapparaître l'agent en double dans le
# dashboard mais toute option explicitement passée doit s'appliquer.
# $PSBoundParameters distingue « fourni » de « laissé au défaut ».
# Fusion, et non conservation en bloc : l'identite (jeton, agentId) est
# preservee - l'ecraser ferait reapparaitre l'agent en double dans le
# dashboard - mais toute option explicitement passee doit s'appliquer.
# $PSBoundParameters distingue 'fourni' de 'laisse au defaut'.
$raw = (Get-Content $configFile -Raw) -replace "^\uFEFF", ''
$current = $raw | ConvertFrom-Json
if (-not $current.obs) {
@@ -135,8 +146,8 @@ if ($isUpgrade) {
}
$applied = @()
# Toujours rafraîchie : ce n'est pas de l'identité, c'est de l'infrastructure.
# Sans elle, l'agent ne saurait pas où chercher sa propre mise à jour.
# Toujours rafraichie : ce n'est pas de l'identite, c'est de l'infrastructure.
# Sans elle, l'agent ne saurait pas ou chercher sa propre mise a jour.
if ($current.PSObject.Properties.Name -notcontains 'packageUrl') {
$current | Add-Member -NotePropertyName packageUrl -NotePropertyValue $url -Force
$applied += 'packageUrl'
@@ -154,17 +165,17 @@ if ($isUpgrade) {
if ($ResetIdentity -and $Token) {
$current.token = $Token
$current.PSObject.Properties.Remove('agentId')
$applied += 'identité réinitialisée'
$applied += 'identite reinitialisee'
} elseif ($Token -and $Token -ne $current.token) {
Write-Host ' (-Token ignoré : identité conservée. Utilise -ResetIdentity pour forcer.)'
Write-Host ' (-Token ignore : identite conservee. Utilise -ResetIdentity pour forcer.)'
}
$json = $current | ConvertTo-Json -Depth 5
[System.IO.File]::WriteAllText($configFile, $json, (New-Object System.Text.UTF8Encoding($false)))
Write-Host $(if ($applied.Count) { " champs mis à jour : $($applied -join ', ')" }
else { ' aucun changement demandé' })
Info 'Configuration mise à jour, identité de l''agent préservée'
Write-Host $(if ($applied.Count) { " champs mis a jour : $($applied -join ', ')" }
else { ' aucun changement demande' })
Info 'Configuration mise a jour, identite de l''agent preservee'
} else {
$config = [ordered]@{
serverUrl = $Server
@@ -173,14 +184,14 @@ if ($isUpgrade) {
packageUrl = $url
obs = [ordered]@{ host = $ObsHost; port = $ObsPort; password = $ObsPassword }
}
# Surtout pas Set-Content -Encoding UTF8 : sous PowerShell 5.1 il préfixe un
# BOM, et JSON.parse côté Node refuse le fichier.
# Surtout pas Set-Content -Encoding UTF8 : sous PowerShell 5.1 il prefixe un
# BOM, et JSON.parse cote Node refuse le fichier.
$json = $config | ConvertTo-Json -Depth 5
[System.IO.File]::WriteAllText($configFile, $json, (New-Object System.Text.UTF8Encoding($false)))
Info "Configuration écrite dans $configFile"
Info "Configuration ecrite dans $configFile"
}
# Le fichier porte un jeton : lecture réservée au compte de l'agent et aux admins.
# Le fichier porte un jeton : lecture reservee au compte de l'agent et aux admins.
$acl = Get-Acl $configFile
$acl.SetAccessRuleProtection($true, $false)
foreach ($identity in @($RunAsUser, 'BUILTIN\Administrators', 'NT AUTHORITY\SYSTEM')) {
@@ -188,12 +199,12 @@ foreach ($identity in @($RunAsUser, 'BUILTIN\Administrators', 'NT AUTHORITY\SYST
$acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
$identity, 'FullControl', 'Allow')))
} catch {
Write-Host "! Règle d'accès ignorée pour $identity"
Write-Host "! Regle d'acces ignoree pour $identity"
}
}
Set-Acl -Path $configFile -AclObject $acl
# --- Tâche planifiée --------------------------------------------------------
# --- Tache planifiee --------------------------------------------------------
$nodePath = (Get-Command node).Source
$entry = Join-Path $InstallDir 'agent.cjs'
@@ -211,7 +222,7 @@ Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger `
Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
Start-ScheduledTask -TaskName $TaskName
# --- Vérification -----------------------------------------------------------
# --- Verification -----------------------------------------------------------
Write-Host ''
$env:AGENT_CONFIG = $configFile
& node $entry --check
@@ -220,15 +231,15 @@ Write-Host ''
Start-Sleep -Seconds 2
$state = (Get-ScheduledTask -TaskName $TaskName).State
if ($state -eq 'Running') {
Write-Host ' Agent installé et démarré.' -ForegroundColor Green
Write-Host '[OK] Agent installe et demarre.' -ForegroundColor Green
} else {
Write-Host "! Tâche dans l'état « $state ». Détails :" -ForegroundColor Yellow
Write-Host "! Tache dans l'etat '$state'. Details :" -ForegroundColor Yellow
Get-ScheduledTaskInfo -TaskName $TaskName | Format-List TaskName, LastRunTime, LastTaskResult
}
Write-Host @"
état : Get-ScheduledTask -TaskName $TaskName
etat : Get-ScheduledTask -TaskName $TaskName
test : node $entry --check
"@