164 lines
8.5 KiB
PowerShell
164 lines
8.5 KiB
PowerShell
#requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Fragt nach und richtet optional die täglichen Scoop-Auto-Updates ein.
|
|
.DESCRIPTION
|
|
Eigenständig ausführbar, ohne Bucket-Migration. Bei y wird auto-update.ps1
|
|
heruntergeladen und eine Aufgabe für täglich 03:00 Uhr erstellt.
|
|
Die Aufgabe startet das Updateskript verborgen und holt verpasste Termine nach,
|
|
sobald die Benutzersitzung und eine Netzwerkverbindung verfügbar sind.
|
|
Bestehende Aufgaben können nach Rückfrage repariert oder samt Updateskript
|
|
entfernt werden. Werden beide Fragen verneint, bleibt alles unverändert.
|
|
.EXAMPLE
|
|
.\scripts\setup-auto-update.ps1
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $ScoopRoot = $(if ($env:SCOOP) { $env:SCOOP } else { Join-Path $env:USERPROFILE 'scoop' })
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$ScoopRoot = [IO.Path]::GetFullPath($ScoopRoot)
|
|
$repository = 'https://gitea.medelexis.ch/public/scoop'
|
|
|
|
$userId = [Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
$userSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
$taskName = 'Medelexis AG - Scoop AutoUpdate'
|
|
$legacyTaskName = "Medelexis-Scoop-AutoUpdate-$userSid"
|
|
$autoUpdateDirectory = Join-Path $ScoopRoot 'scripts\medelexis'
|
|
$existingTasks = @(
|
|
foreach ($name in @($taskName, $legacyTaskName)) {
|
|
Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue
|
|
}
|
|
)
|
|
$autoUpdateRemoved = $false
|
|
if ($existingTasks.Count -gt 0) {
|
|
do {
|
|
$autoUpdateAnswer = (Read-Host 'Bestehende Scoop-Auto-Update-Aufgabe reparieren (ersetzen)? [y/n]').Trim().ToLowerInvariant()
|
|
} while ($autoUpdateAnswer -notin @('y', 'n'))
|
|
if ($autoUpdateAnswer -eq 'n') {
|
|
do {
|
|
$removeAnswer = (Read-Host 'Scoop-Auto-Update entfernen? [y/n]').Trim().ToLowerInvariant()
|
|
} while ($removeAnswer -notin @('y', 'n'))
|
|
if ($removeAnswer -eq 'y') {
|
|
foreach ($existingTask in $existingTasks) {
|
|
if ($existingTask.State -eq 'Running') {
|
|
Stop-ScheduledTask -TaskName $existingTask.TaskName
|
|
}
|
|
Unregister-ScheduledTask -TaskName $existingTask.TaskName -Confirm:$false
|
|
}
|
|
# Nur die zugehörigen Dateien entfernen, niemals den Ordner rekursiv.
|
|
foreach ($fileName in @('auto-update.ps1', 'auto-update-hidden.exe', 'auto-update.log')) {
|
|
$filePath = Join-Path $autoUpdateDirectory $fileName
|
|
if (Test-Path -LiteralPath $filePath -PathType Leaf) {
|
|
Remove-Item -LiteralPath $filePath -Force
|
|
}
|
|
}
|
|
$autoUpdateRemoved = $true
|
|
Write-Host 'Scoop-Auto-Update entfernt (Aufgabe und zugehörige Dateien).'
|
|
}
|
|
}
|
|
} else {
|
|
do {
|
|
$autoUpdateAnswer = (Read-Host 'Scoop-Auto-Update einschalten? [y/n]').Trim().ToLowerInvariant()
|
|
} while ($autoUpdateAnswer -notin @('y', 'n'))
|
|
}
|
|
|
|
if ($autoUpdateAnswer -eq 'y') {
|
|
$scoopCommand = Join-Path $ScoopRoot 'shims\scoop.ps1'
|
|
if (-not (Test-Path -LiteralPath $scoopCommand -PathType Leaf)) {
|
|
throw "Scoop wurde nicht gefunden: $scoopCommand"
|
|
}
|
|
$autoUpdateDirectory = Join-Path $ScoopRoot 'scripts\medelexis'
|
|
New-Item -ItemType Directory -Path $autoUpdateDirectory -Force | Out-Null
|
|
$autoUpdateScript = Join-Path $autoUpdateDirectory 'auto-update.ps1'
|
|
$downloadPath = Join-Path $autoUpdateDirectory ('auto-update-{0}.download' -f [guid]::NewGuid())
|
|
try {
|
|
$autoUpdateUrl = "$repository/raw/branch/master/scripts/auto-update.ps1"
|
|
Invoke-WebRequest -Uri $autoUpdateUrl -OutFile $downloadPath -UseBasicParsing
|
|
$parseTokens = $null
|
|
$parseErrors = $null
|
|
$downloadedScript = [Management.Automation.Language.Parser]::ParseFile($downloadPath, [ref] $parseTokens, [ref] $parseErrors)
|
|
if ($parseErrors.Count -gt 0 -or -not $downloadedScript.ParamBlock -or
|
|
'ScoopRoot' -notin @($downloadedScript.ParamBlock.Parameters.Name.VariablePath.UserPath)) {
|
|
throw 'Das heruntergeladene Auto-Update-Skript ist kein gültiges Updateskript.'
|
|
}
|
|
Move-Item -LiteralPath $downloadPath -Destination $autoUpdateScript -Force
|
|
# Nur die Aufgabe einrichten; das Updateskript wird durch die Aufgabe gestartet.
|
|
# Bei einer Reparatur zuerst eine laufende Aufgabe anhalten,
|
|
# damit ihr Starter ersetzt werden kann.
|
|
foreach ($existingTask in $existingTasks) {
|
|
if ($existingTask.State -eq 'Running') {
|
|
Stop-ScheduledTask -TaskName $existingTask.TaskName
|
|
}
|
|
}
|
|
# Ein GUI-Programm startet PowerShell ohne Konsole, auch wenn Windows
|
|
# Terminal als Standardterminal eingerichtet ist. Es wartet auf das Ende
|
|
# des Updates und reicht den Exitcode weiter (Fehlerstatus / IgnoreNew).
|
|
$launcherPath = Join-Path $autoUpdateDirectory 'auto-update-hidden.exe'
|
|
$launcherSource = @'
|
|
using System;
|
|
using System.Diagnostics;
|
|
public static class ScoopAutoUpdateLauncher {
|
|
[STAThread]
|
|
public static int Main(string[] args) {
|
|
if (args.Length != 2) return 2;
|
|
try {
|
|
var start = new ProcessStartInfo();
|
|
start.FileName = System.IO.Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"),
|
|
@"System32\WindowsPowerShell\v1.0\powershell.exe");
|
|
start.Arguments = "-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File \"" +
|
|
args[0] + "\" -ScoopRoot \"" + args[1] + "\"";
|
|
start.UseShellExecute = false;
|
|
start.CreateNoWindow = true;
|
|
start.WindowStyle = ProcessWindowStyle.Hidden;
|
|
using (var child = Process.Start(start)) {
|
|
child.WaitForExit();
|
|
return child.ExitCode;
|
|
}
|
|
} catch { return 1; }
|
|
}
|
|
}
|
|
'@
|
|
$compiledLauncher = Join-Path $autoUpdateDirectory ('auto-update-{0}.exe' -f [guid]::NewGuid())
|
|
$launcherSourcePath = [IO.Path]::ChangeExtension($compiledLauncher, '.cs')
|
|
# Der .NET-Framework-Compiler erzeugt eine Windows-Anwendung auch unter
|
|
# PowerShell 7, wo Add-Type -OutputType WindowsApplication nicht existiert.
|
|
$compiler = @(
|
|
(Join-Path $env:SystemRoot 'Microsoft.NET\Framework64\v4.0.30319\csc.exe'),
|
|
(Join-Path $env:SystemRoot 'Microsoft.NET\Framework\v4.0.30319\csc.exe')
|
|
) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
|
|
if (-not $compiler) { throw 'Der Windows-.NET-Framework-Compiler wurde nicht gefunden.' }
|
|
try {
|
|
[IO.File]::WriteAllText($launcherSourcePath, $launcherSource, [Text.Encoding]::UTF8)
|
|
$compilerOutput = & $compiler /nologo /target:winexe "/out:$compiledLauncher" $launcherSourcePath 2>&1
|
|
if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $compiledLauncher -PathType Leaf)) {
|
|
throw "Der fensterlose Starter konnte nicht erstellt werden: $($compilerOutput | Out-String)"
|
|
}
|
|
Move-Item -LiteralPath $compiledLauncher -Destination $launcherPath -Force
|
|
} finally {
|
|
foreach ($temporaryFile in @($compiledLauncher, $launcherSourcePath)) {
|
|
if (Test-Path -LiteralPath $temporaryFile) { Remove-Item -LiteralPath $temporaryFile }
|
|
}
|
|
}
|
|
$action = New-ScheduledTaskAction -Execute $launcherPath -Argument ('"{0}" "{1}"' -f $autoUpdateScript, $ScoopRoot.TrimEnd('\'))
|
|
$trigger = New-ScheduledTaskTrigger -Daily -At '03:00'
|
|
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
|
|
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -RunOnlyIfNetworkAvailable `
|
|
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew `
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 4)
|
|
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger `
|
|
-Principal $principal -Settings $settings -Force `
|
|
-Description 'Tägliche Aktualisierung der installierten Elemente (Elexis ausgeschlossen)' | Out-Null
|
|
# Die frühere Aufgabe dieses Benutzers entfernen, damit nur ein Update läuft.
|
|
if (Get-ScheduledTask -TaskName $legacyTaskName -ErrorAction SilentlyContinue) {
|
|
Unregister-ScheduledTask -TaskName $legacyTaskName -Confirm:$false
|
|
}
|
|
Write-Host "Scoop-Auto-Update eingeschaltet: $taskName"
|
|
} finally {
|
|
if (Test-Path -LiteralPath $downloadPath) { Remove-Item -LiteralPath $downloadPath }
|
|
}
|
|
} elseif (-not $autoUpdateRemoved) {
|
|
Write-Host 'Keine Änderung an Scoop-Auto-Update vorgenommen.'
|
|
}
|