Compare commits

..
2 Commits
3 changed files with 224 additions and 2 deletions

No files matched your search

-2
View File
@@ -1,7 +1,5 @@
*.bak
*.exe
scripts/*
!scripts/update-manifests.py
.vscode/*
__pycache__/
*.py[cod]
File renamed without changes.
+224
View File
@@ -0,0 +1,224 @@
#requires -Version 5.1
<#
.SYNOPSIS
Stellt bestehende Scoop-Installationen von elexis/streichtech auf medelexis um.
.DESCRIPTION
Ohne -Apply nur ein Probelauf. Keine Sicherungen, Neuinstallationen oder Git-Pushes.
Bearbeitet alle Versionsverzeichnisse der benutzerspezifischen Scoop-Installation.
Ein passender bestehender Bucket wird umbenannt. Danach werden elexis und streichtech entfernt.
Andere Buckets wie main und extras bleiben bestehen.
Alte Paketnamen mit Unterstrich werden auf passende Manifestnamen mit Bindestrich umgestellt.
.EXAMPLE
.\scripts\update-bucket.ps1
.EXAMPLE
.\scripts\update-bucket.ps1 -Apply
#>
[CmdletBinding()]
param(
[switch] $Apply,
[string] $ScoopRoot = $(if ($env:SCOOP) { $env:SCOOP } else { Join-Path $env:USERPROFILE 'scoop' })
)
$ErrorActionPreference = 'Stop'
$repository = 'https://gitea.medelexis.ch/public/scoop'
$targetName = 'medelexis'
$target = Join-Path $ScoopRoot "buckets\$targetName"
$apps = Join-Path $ScoopRoot 'apps'
if (-not (Test-Path -LiteralPath $apps -PathType Container)) {
throw "Kein Scoop-Apps-Verzeichnis gefunden: $apps"
}
function Test-MedelexisRepository([string] $Path) {
if (-not (Test-Path -LiteralPath $Path -PathType Container)) { return $false }
$remote = & git -C $Path remote get-url origin 2>$null
if ($LASTEXITCODE -ne 0) { return $false }
try { $uri = [Uri] ([string] $remote).Trim() } catch { return $false }
return ($uri.Scheme -eq 'https' -and $uri.Host -eq 'gitea.medelexis.ch' -and
$uri.AbsolutePath.TrimEnd('/').Replace('.git', '') -eq '/public/scoop')
}
function Get-PackageRenames([string] $BucketPath) {
if (-not $BucketPath -or -not (Test-Path -LiteralPath $BucketPath)) { return }
foreach ($app in Get-ChildItem -LiteralPath $apps -Directory) {
if (-not $app.Name.Contains('_')) { continue }
$newName = $app.Name.Replace('_', '-')
$newManifest = Join-Path $BucketPath "bucket\$newName.json"
if (-not (Test-Path -LiteralPath $newManifest)) {
$newManifest = Join-Path $BucketPath "$newName.json"
}
if (-not (Test-Path -LiteralPath $newManifest)) { continue }
$current = Join-Path $app.FullName 'current'
$installFile = Join-Path $current 'install.json'
if (-not (Test-Path -LiteralPath $installFile)) { continue }
$install = [IO.File]::ReadAllText($installFile) | ConvertFrom-Json
if ($install.bucket -notin @('elexis', 'streichtech', 'medelexis')) { continue }
$destination = Join-Path $apps $newName
if (Test-Path -LiteralPath $destination) {
throw "'$($app.Name)' und '$newName' sind beide installiert. Keine automatische Zusammenführung."
}
$link = Get-Item -LiteralPath $current -Force
if ($link.LinkType -ne 'Junction') {
throw "Für '$($app.Name)' wird eine Scoop-current-Junction erwartet. Keine automatische Umbenennung."
}
$versionPath = [IO.Path]::GetFullPath([string] @($link.Target)[0])
if ((Split-Path $versionPath -Parent) -ne $app.FullName) {
throw "Die current-Verknüpfung von '$($app.Name)' zeigt ausserhalb des Paketordners."
}
$persistSource = Join-Path $ScoopRoot "persist\$($app.Name)"
$persistTarget = Join-Path $ScoopRoot "persist\$newName"
if ((Test-Path -LiteralPath $persistSource) -and (Test-Path -LiteralPath $persistTarget)) {
throw "Beide Persistenzordner für '$($app.Name)' und '$newName' existieren. Keine automatische Zusammenführung."
}
Write-Host "Paket umbenennen: $($app.Name) -> $newName"
[pscustomobject]@{
OldName = $app.Name; NewName = $newName; Source = $app.FullName
Destination = $destination; Version = (Split-Path $versionPath -Leaf)
PersistSource = $persistSource; PersistTarget = $persistTarget
}
}
}
$renameSource = $null
if (Test-Path -LiteralPath $target) {
if (-not (Test-MedelexisRepository $target)) {
throw 'Der vorhandene Bucket medelexis zeigt auf ein anderes Repository. Keine Änderung.'
}
Write-Host 'Bucket medelexis ist bereits korrekt eingerichtet.'
} else {
foreach ($oldName in @('elexis', 'streichtech')) {
$candidate = Join-Path $ScoopRoot "buckets\$oldName"
if (Test-MedelexisRepository $candidate) {
$renameSource = $candidate
Write-Host "Bucket umbenennen: $oldName -> $targetName"
break
}
}
if (-not $renameSource) { Write-Host "Bucket hinzufügen: $targetName -> $repository" }
}
$changes = @(
foreach ($app in Get-ChildItem -LiteralPath $apps -Directory) {
foreach ($version in Get-ChildItem -LiteralPath $app.FullName -Directory) {
# current und andere Verknüpfungen nicht nochmals bearbeiten.
if ($version.Attributes -band [IO.FileAttributes]::ReparsePoint) { continue }
$file = Join-Path $version.FullName 'install.json'
if (-not (Test-Path -LiteralPath $file -PathType Leaf)) { continue }
$original = [IO.File]::ReadAllText($file)
$data = $original | ConvertFrom-Json
if ($data.bucket -notin @('elexis', 'streichtech')) { continue }
$pattern = '(?i)("bucket"\s*:\s*)"(?:elexis|streichtech)"'
$updated = [regex]::Replace($original, $pattern, '${1}"medelexis"')
if (($updated | ConvertFrom-Json).bucket -ne $targetName) {
throw "Bucket-Zuordnung konnte nicht angepasst werden: $file"
}
Write-Host "$($app.Name) / $($version.Name): $($data.bucket) -> $targetName"
[pscustomobject]@{ File = $file; Original = $original; Updated = $updated; App = $app.Name }
}
}
)
$manifestRoot = $(if (Test-Path -LiteralPath $target) { $target } else { $renameSource })
$renames = @(Get-PackageRenames $manifestRoot)
if (-not $Apply) {
foreach ($oldName in @('elexis', 'streichtech')) {
if (Test-Path -LiteralPath (Join-Path $ScoopRoot "buckets\$oldName")) {
Write-Host "Alter Bucket wird nach der Umstellung entfernt oder umbenannt: $oldName"
}
}
Write-Host "Probelauf: $($changes.Count) install.json-Dateien würden geändert."
Write-Host "Probelauf: $($renames.Count) Paketnamen würden umgestellt."
Write-Host 'Mit -Apply anwenden. Es werden keine Sicherungen erstellt.'
return
}
if (-not (Test-Path -LiteralPath $target)) {
if ($renameSource) {
$bucketRoot = (Resolve-Path -LiteralPath (Join-Path $ScoopRoot 'buckets')).Path
$sourcePath = (Resolve-Path -LiteralPath $renameSource).Path
$targetPath = [IO.Path]::GetFullPath($target)
if ((Split-Path $sourcePath -Parent) -ne $bucketRoot -or
(Split-Path $targetPath -Parent) -ne $bucketRoot) {
throw 'Bucket-Pfade liegen ausserhalb des erwarteten Verzeichnisses. Keine Umbenennung.'
}
[IO.Directory]::Move($sourcePath, $targetPath)
} else {
# scoop verwendet dieselbe Root-Variable für benutzerdefinierte Installationen.
if ([IO.Path]::GetFullPath($ScoopRoot).TrimEnd('\') -ne
[IO.Path]::GetFullPath($(if ($env:SCOOP) { $env:SCOOP } else { Join-Path $env:USERPROFILE 'scoop' })).TrimEnd('\')) {
throw 'ScoopRoot weicht von der aktiven Scoop-Installation ab; Bucket bitte dort zuerst einrichten.'
}
& scoop bucket add $targetName $repository
if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $target)) {
throw 'Bucket konnte nicht hinzugefügt werden. Installationsmetadaten bleiben unverändert.'
}
}
}
# Falls der Ziel-Bucket erst hinzugefügt wurde, stehen seine Manifestnamen jetzt bereit.
if (-not $manifestRoot) { $renames = @(Get-PackageRenames $target) }
# Vor dem ersten Schreiben prüfen, ob während des Probelaufs Dateien geändert wurden.
foreach ($change in $changes) {
if ([IO.File]::ReadAllText($change.File) -cne $change.Original) {
throw 'Installationsmetadaten wurden zwischenzeitlich geändert. Skript bitte erneut starten.'
}
}
foreach ($change in $changes) {
[IO.File]::WriteAllText($change.File, $change.Updated, [Text.UTF8Encoding]::new($false))
}
foreach ($rename in $renames) {
$activeRoot = $(if ($env:SCOOP) { $env:SCOOP } else { Join-Path $env:USERPROFILE 'scoop' })
if ([IO.Path]::GetFullPath($ScoopRoot).TrimEnd('\') -ne [IO.Path]::GetFullPath($activeRoot).TrimEnd('\')) {
throw 'Für Paketumbenennungen muss ScoopRoot der aktiven Scoop-Installation entsprechen.'
}
$appsRoot = (Resolve-Path -LiteralPath $apps).Path
if ((Split-Path $rename.Source -Parent) -ne $appsRoot -or
(Split-Path $rename.Destination -Parent) -ne $appsRoot) {
throw 'Paketpfade liegen ausserhalb des Apps-Verzeichnisses.'
}
if (Test-Path -LiteralPath $rename.PersistSource) {
$persistRoot = (Resolve-Path -LiteralPath (Join-Path $ScoopRoot 'persist')).Path
if ((Split-Path $rename.PersistSource -Parent) -ne $persistRoot -or
(Split-Path $rename.PersistTarget -Parent) -ne $persistRoot) {
throw 'Persistenzpfade liegen ausserhalb des Persist-Verzeichnisses.'
}
[IO.Directory]::Move($rename.PersistSource, $rename.PersistTarget)
}
$oldCurrent = Join-Path $rename.Source 'current'
& attrib -R /L $oldCurrent
if ($LASTEXITCODE -ne 0) { throw 'current-Verknüpfung konnte nicht freigegeben werden.' }
# Nur die Junction entfernen, ohne rekursiv ihren Inhalt zu löschen.
[IO.Directory]::Delete($oldCurrent)
[IO.Directory]::Move($rename.Source, $rename.Destination)
$newCurrent = Join-Path $rename.Destination 'current'
$newVersion = Join-Path $rename.Destination $rename.Version
New-Item -ItemType Junction -Path $newCurrent -Target $newVersion | Out-Null
Write-Host "Startverknüpfungen für $($rename.NewName) erneuern ..."
& scoop reset "$($rename.NewName)@$($rename.Version)"
if ($LASTEXITCODE -ne 0) { throw "Scoop reset für '$($rename.NewName)' fehlgeschlagen." }
}
foreach ($oldName in @('elexis', 'streichtech')) {
$oldPath = Join-Path $ScoopRoot "buckets\$oldName"
if (-not (Test-Path -LiteralPath $oldPath)) { continue }
$activeRoot = $(if ($env:SCOOP) { $env:SCOOP } else { Join-Path $env:USERPROFILE 'scoop' })
if ([IO.Path]::GetFullPath($ScoopRoot).TrimEnd('\') -ne [IO.Path]::GetFullPath($activeRoot).TrimEnd('\')) {
throw 'Zum Entfernen alter Buckets muss ScoopRoot der aktiven Scoop-Installation entsprechen.'
}
Write-Host "Alten Bucket entfernen: $oldName"
& scoop bucket rm $oldName
if ($LASTEXITCODE -ne 0 -or (Test-Path -LiteralPath $oldPath)) {
throw "Alter Bucket '$oldName' konnte nicht entfernt werden. Die Umstellung kann erneut ausgeführt werden."
}
}
foreach ($name in @((Get-ChildItem -LiteralPath $apps -Directory).Name)) {
$installFile = Join-Path $apps "$name\current\install.json"
if (-not (Test-Path -LiteralPath $installFile)) { continue }
if (([IO.File]::ReadAllText($installFile) | ConvertFrom-Json).bucket -ne $targetName) { continue }
if (-not (Test-Path -LiteralPath (Join-Path $target "bucket\$name.json")) -and
-not (Test-Path -LiteralPath (Join-Path $target "$name.json"))) {
Write-Warning "Für '$name' fehlt weiterhin ein gleichnamiges Manifest."
}
}
Write-Host "Fertig: $($changes.Count) install.json-Dateien auf medelexis umgestellt."
Write-Host "Paketnamen umgestellt: $($renames.Count)."
Write-Host 'Von elexis/streichtech/medelexis bleibt nur medelexis bestehen. Prüfung: scoop status --local'