Fix : widows giea 2
This commit is contained in:
@@ -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
|
||||
|
||||
"@
|
||||
|
||||
Reference in New Issue
Block a user