<# .SYNOPSIS Veroeffentlicht dieses Projekt im Deploymentcenter. .DESCRIPTION Vorlage zum Kopieren nach scripts/release.ps1 des eigenen Projekts. Anzupassen ist nur der Kopf von release.config.json - dieses Skript selbst bleibt unveraendert. Der Ablauf je Zielplattform: dotnet publish -r -> pack-and-deploy --platform pack-and-deploy uebernimmt dabei Pruefsummen, Dateimanifest, das Fortschreiben der latest.json und die Anmeldung beim Deploymentcenter. Das hier nachzubauen waere ein zweiter Ort fuer dieselben Fehler; das Skript orchestriert nur. Fehlt das Werkzeug, wird es geholt und die Pruefsumme geprueft. .EXAMPLE .\scripts\release.ps1 -Version 1.4.3 -Changelog "Behebt den Login-Fehler." .EXAMPLE .\scripts\release.ps1 -Version 1.5.0 -Channel beta -WhatIf #> [CmdletBinding(SupportsShouldProcess = $true)] param( # Ohne Angabe wird die Version aus Directory.Build.props bzw. der csproj gelesen. [string] $Version, [ValidateSet('prod', 'beta', 'dev')] [string] $Channel = 'prod', [string] $Changelog, # Als kritisches Update kennzeichnen (Rollout priorisieren). [switch] $Critical, [string] $ConfigFile = (Join-Path $PSScriptRoot 'release.config.json') ) $ErrorActionPreference = 'Stop' # ---------------------------------------------------------------- Konfiguration if (-not (Test-Path $ConfigFile)) { throw "Konfiguration fehlt: $ConfigFile`nVorlage kopieren: release.config.example.json -> release.config.json" } $config = Get-Content $ConfigFile -Raw | ConvertFrom-Json foreach ($required in @('project', 'csproj', 'runtimes')) { if (-not $config.$required) { throw "In $ConfigFile fehlt der Eintrag '$required'." } } $repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') $csprojRel = $config.csproj $csproj = Join-Path $repoRoot $csprojRel if (-not (Test-Path $csproj)) { throw "Projektdatei nicht gefunden: $csproj" } $baseUrl = if ($config.baseUrl) { $config.baseUrl.TrimEnd('/') } else { 'https://dc.mhdf.de' } $toolDir = Join-Path $repoRoot '.dc-tools' # ---------------------------------------------------------------------- Version function Get-ProjectVersion { # Directory.Build.props zuerst: Steht nur in einem von mehreren # Projekten, laufen die Angaben frueher oder spaeter auseinander - und # pack-and-deploy bricht dann zu Recht mit einem Versionskonflikt ab. foreach ($candidate in @( (Join-Path $repoRoot 'Directory.Build.props'), $csproj )) { if (-not (Test-Path $candidate)) { continue } $match = [regex]::Match((Get-Content $candidate -Raw), '\s*([^<]+?)\s*') if ($match.Success) { return $match.Groups[1].Value.Trim() } } return $null } if (-not $Version) { $Version = Get-ProjectVersion if (-not $Version) { throw "Keine in Directory.Build.props oder $csprojRel gefunden. Bitte -Version angeben." } Write-Host "Version aus dem Projekt gelesen: $Version" -ForegroundColor DarkGray } if (-not $Changelog) { $Changelog = "Release v$Version" } # ------------------------------------------------------------------- Werkzeug function Get-PackAndDeploy { $exe = Join-Path $toolDir 'pack-and-deploy.exe' if (Test-Path $exe) { return $exe } Write-Host "pack-and-deploy wird geholt ..." -ForegroundColor Cyan New-Item -ItemType Directory -Force -Path $toolDir | Out-Null $name = 'pack-and-deploy-win-x64.exe' $temp = Join-Path $toolDir 'download.tmp' Invoke-WebRequest -Uri "$baseUrl/installer/$name" -OutFile $temp -UseBasicParsing $expectedRaw = (Invoke-WebRequest -Uri "$baseUrl/installer/$name.sha256" -UseBasicParsing).Content $expected = if ($expectedRaw -is [byte[]]) { [System.Text.Encoding]::ASCII.GetString($expectedRaw) } else { [string]$expectedRaw } $expected = $expected.Trim().ToLower() $actual = (Get-FileHash $temp -Algorithm SHA256).Hash.ToLower() if ($actual -ne $expected) { Remove-Item $temp -Force throw "Pruefsumme von $name stimmt nicht.`n erwartet: $expected`n erhalten: $actual" } Move-Item $temp $exe -Force try { Unblock-File $exe -ErrorAction SilentlyContinue } catch { } Write-Host " Pruefsumme in Ordnung." -ForegroundColor DarkGray return $exe } $packAndDeploy = Get-PackAndDeploy # --------------------------------------------------------------------- Ablauf Write-Host '' Write-Host "Projekt : $($config.project)" -ForegroundColor White Write-Host "Version : $Version" Write-Host "Kanal : $Channel" Write-Host "Plattform : $($config.runtimes -join ', ')" Write-Host '' $results = @() foreach ($rid in $config.runtimes) { Write-Host "=== $rid ===" -ForegroundColor Cyan $publishDir = Join-Path $repoRoot "artifacts/publish/$rid" if ($PSCmdlet.ShouldProcess("$($config.project) $Version ($rid)", 'dotnet publish')) { # Sauber neu bauen: Reste einer vorherigen Laufzeitkennung wuerden # sonst mit ins Paket wandern. if (Test-Path $publishDir) { Remove-Item $publishDir -Recurse -Force } $publishArgs = @( 'publish', $csproj, '-c', 'Release', '-r', $rid, '-o', $publishDir, '--nologo' ) if ($config.selfContained) { $publishArgs += '--self-contained', 'true' } else { $publishArgs += '--self-contained', 'false' } if ($config.publishSingleFile) { $publishArgs += '-p:PublishSingleFile=true' } & dotnet @publishArgs if ($LASTEXITCODE -ne 0) { throw "dotnet publish fuer $rid ist fehlgeschlagen." } } # setup.json mitliefern, damit der Installer weiss, was einzurichten ist. # Bewusst kein Ternary-Operator: den gibt es erst ab PowerShell 7, und # diese Vorlage soll auch mit dem mitgelieferten 5.1 laufen. $setupRel = if ($config.setupJson) { $config.setupJson } else { 'setup.json' } $setupJson = Join-Path $repoRoot $setupRel if (Test-Path $setupJson) { Copy-Item $setupJson (Join-Path $publishDir 'setup.json') -Force Write-Host " setup.json mitgenommen" -ForegroundColor DarkGray } if ($PSCmdlet.ShouldProcess("$($config.project) $Version ($rid)", 'pack-and-deploy')) { $packArgs = @( '--project', $config.project, '--version', $Version, '--channel', $Channel, '--platform', $rid, '--publish-dir', $publishDir, '--changelog', $Changelog ) if ($Critical) { $packArgs += '--critical' } if ($config.mainAssembly) { $packArgs += '--main-assembly', $config.mainAssembly } & $packAndDeploy @packArgs $code = $LASTEXITCODE $results += [pscustomobject]@{ Runtime = $rid; ExitCode = $code } # 1 = Konfigurationsfehler oder Versionskonflikt: dann stimmt etwas # Grundsaetzliches, und die weiteren Plattformen wuerden genauso # scheitern. if ($code -eq 1) { throw "pack-and-deploy meldet einen Konfigurations- oder Versionsfehler." } } Write-Host '' } # ------------------------------------------------------------------ Ergebnis Write-Host '=== Ergebnis ===' -ForegroundColor White foreach ($r in $results) { $text = switch ($r.ExitCode) { 0 { 'vollstaendig veroeffentlicht' } 2 { 'TEILWEISE - Upload oder Registrierung fehlgeschlagen' } default { "unerwarteter Rueckgabewert $($r.ExitCode)" } } $color = if ($r.ExitCode -eq 0) { 'Green' } else { 'Yellow' } Write-Host (" {0,-16} {1}" -f $r.Runtime, $text) -ForegroundColor $color } $failed = @($results | Where-Object { $_.ExitCode -ne 0 }) if ($failed.Count -gt 0) { Write-Host '' Write-Host 'Nicht alle Plattformen sind durchgelaufen. Vor einem erneuten Versuch pruefen,' -ForegroundColor Yellow Write-Host 'ob die bereits hochgeladenen Dateien konsistent sind.' -ForegroundColor Yellow exit 2 } Write-Host '' Write-Host "Fertig. $($config.project) $Version ist im Kanal $Channel verfuegbar." -ForegroundColor Green