mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 15:44:25 +08:00
* 激进的更新 * 试试 * fix.可爱的我一直在修CI( * fix.启动器一定要能够启动 * feat.尝试弄了AOT的启动器。 * fix.修CI,好像是因为Linux那边有个问题,反正修就对了。 * fix.ci难修,为什么liunx跑不起来呢? * Update build.yml * Update LanMountainDesktop.csproj * changed.调整了启动逻辑,优化了更新页面。 * changed.优化了更新体验 * feat.依旧试增量更新这一块,看看velopack * fix.我们试验性地修复了启动器无法正常启动的问题,原因可能是这个画面没有启动,就GUI没显示。然后还把编译问题修了一下。 * fix.继续修ci,ci怎么天天炸 * changed.velopack,试试rust * fix.修ci,修融合桌面,修启动器 * fix.GitHub Action工作流怎么天天出问题 * feat.引入velopack,不好,是rust(至少内存很安全了。 * chore: migrate release pipeline to signed filemap and wire rainyun s3 * fix: make optional s3 upload step workflow-parse safe * fix: make delta pack generation robust for empty diffs and linux paths * chore: rotate launcher update public key for pdc signing * fix: restore stable launcher update public key * fix: sync launcher public key with update signing secret * fix: normalize PEM line endings in signing key validation * fix: rotate launcher public key to match ci signing secret * fix: compare signing keys by SPKI instead of PEM text * refactor update backend to host-managed PDC pipeline * fix release workflow env key collisions * relax publish-pdc precheck to require S3 only * set GH_TOKEN for PDCC installer step * ci: add local pdc mock fallback for release publish * ci: fix pdc mock process log redirection * ci: fallback pdcc signing key to update private key * ci: ensure pdcc signing passphrase env is always set * ci: create pdcc publish root before invoking client * ci: set pdcc version variable from release version * ci: decouple pdcc installer version from publish config version * ci: package pdcc subchannels with generated filemap and changelog * ci: make local pdc mock diff return empty for fast fallback * ci: fix pdcc variable mapping and pdc signing prechecks * Update App.axaml.cs * ci: wire aws cli credentials for rainyun s3 * ci: pin pdcc client version separately from app version * ci: harden local pdc mock transport handling * ci: publish pdcc subchannels in one pass * ci: add pdcc publish heartbeat and timeout * ci: fix pdcc publish workdir bootstrap * feat.Penguin Logistics Online Network Distribution System * ci: fix plonds s3 probe and signing fallback * ci: validate signing key and quiet missing baselines * ci: relax aws checksum mode for rainyun s3 * ci: avoid multipart uploads to rainyun s3 * ci: handle empty plonds baselines safely * ci.plonds * Rebuild release pipeline around PLONDS and DDSS * Fix Windows installer script path in release workflow
1045 lines
35 KiB
PowerShell
1045 lines
35 KiB
PowerShell
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Version,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppArtifactsRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$InstallerArtifactsRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutputDir,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$PrivateKeyPath,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Channel = "stable",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$S3Endpoint = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$S3Bucket = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$S3Region = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$IncrementalStrategy = "release-payload",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$PublishIncrementalRelease = "true",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$BaselineRef = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$GitHubRepository = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$GitHubTag = "",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$MirrorInstallersToS3 = "false",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$UploadMetaToS3 = "true"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function ConvertTo-Boolean {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Value,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[bool]$DefaultValue = $false
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
return $DefaultValue
|
|
}
|
|
|
|
return $Value.Trim().ToLowerInvariant() -in @("1", "true", "yes", "y", "on")
|
|
}
|
|
|
|
function Get-GitHubReleaseBaseUrl {
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Repository,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Tag
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Repository) -or [string]::IsNullOrWhiteSpace($Tag)) {
|
|
return $null
|
|
}
|
|
|
|
$normalizedRepository = $Repository.Trim().Trim('/')
|
|
$normalizedTag = $Tag.Trim()
|
|
if ($normalizedTag.StartsWith("refs/tags/", [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$normalizedTag = $normalizedTag.Substring("refs/tags/".Length)
|
|
}
|
|
|
|
return "https://github.com/$normalizedRepository/releases/download/$normalizedTag"
|
|
}
|
|
|
|
function Get-PlatformConfigurations {
|
|
return @(
|
|
@{
|
|
Platform = "windows-x64"
|
|
ArtifactName = "app-payload-windows-x64"
|
|
},
|
|
@{
|
|
Platform = "windows-x86"
|
|
ArtifactName = "app-payload-windows-x86"
|
|
},
|
|
@{
|
|
Platform = "linux-x64"
|
|
ArtifactName = "app-payload-linux-x64"
|
|
}
|
|
)
|
|
}
|
|
|
|
function Resolve-AppDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SearchRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Version
|
|
)
|
|
|
|
$preferred = Get-ChildItem -LiteralPath $SearchRoot -Recurse -Directory -Filter "app-$Version" -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1
|
|
if ($preferred) {
|
|
return $preferred.FullName
|
|
}
|
|
|
|
$fallback = Get-ChildItem -LiteralPath $SearchRoot -Recurse -Directory -Filter "app-*" -ErrorAction SilentlyContinue |
|
|
Sort-Object FullName |
|
|
Select-Object -First 1
|
|
return $fallback?.FullName
|
|
}
|
|
|
|
function Clear-Directory {
|
|
param([Parameter(Mandatory = $true)][string]$Path)
|
|
|
|
if (Test-Path -LiteralPath $Path) {
|
|
Get-ChildItem -LiteralPath $Path -Force -ErrorAction SilentlyContinue | ForEach-Object {
|
|
Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
else {
|
|
New-Item -ItemType Directory -Path $Path -Force | Out-Null
|
|
}
|
|
}
|
|
|
|
function Invoke-AwsCommandIfPossible {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$Arguments,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$IgnoreFailure
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($S3Endpoint) -or [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
return $null
|
|
}
|
|
|
|
$previousRequestChecksumCalculation = $env:AWS_REQUEST_CHECKSUM_CALCULATION
|
|
$previousResponseChecksumValidation = $env:AWS_RESPONSE_CHECKSUM_VALIDATION
|
|
|
|
$env:AWS_REQUEST_CHECKSUM_CALCULATION = "WHEN_REQUIRED"
|
|
$env:AWS_RESPONSE_CHECKSUM_VALIDATION = "WHEN_REQUIRED"
|
|
|
|
try {
|
|
if ($IgnoreFailure) {
|
|
return (& aws @Arguments 2>$null)
|
|
}
|
|
|
|
return (& aws @Arguments)
|
|
}
|
|
finally {
|
|
if ($null -eq $previousRequestChecksumCalculation) {
|
|
Remove-Item Env:AWS_REQUEST_CHECKSUM_CALCULATION -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
$env:AWS_REQUEST_CHECKSUM_CALCULATION = $previousRequestChecksumCalculation
|
|
}
|
|
|
|
if ($null -eq $previousResponseChecksumValidation) {
|
|
Remove-Item Env:AWS_RESPONSE_CHECKSUM_VALIDATION -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
$env:AWS_RESPONSE_CHECKSUM_VALIDATION = $previousResponseChecksumValidation
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-S3Key {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Prefix,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RelativePath
|
|
)
|
|
|
|
$trimmedPrefix = $Prefix.Trim('/').Replace('\', '/')
|
|
$trimmedRelativePath = $RelativePath.TrimStart('\', '/').Replace('\', '/')
|
|
return "$trimmedPrefix/$trimmedRelativePath"
|
|
}
|
|
|
|
function Get-RelativePath {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Root,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path
|
|
)
|
|
|
|
$rootPath = [System.IO.Path]::GetFullPath($Root)
|
|
if (-not $rootPath.EndsWith([System.IO.Path]::DirectorySeparatorChar)) {
|
|
$rootPath += [System.IO.Path]::DirectorySeparatorChar
|
|
}
|
|
|
|
$pathValue = [System.IO.Path]::GetFullPath($Path)
|
|
return [System.IO.Path]::GetRelativePath($rootPath, $pathValue)
|
|
}
|
|
|
|
function Test-S3ObjectExists {
|
|
param([Parameter(Mandatory = $true)][string]$Key)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($S3Endpoint) -or [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
return $false
|
|
}
|
|
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3api", "head-object",
|
|
"--bucket", $S3Bucket,
|
|
"--key", $Key.Trim('/').Replace('\', '/')
|
|
) -IgnoreFailure | Out-Null
|
|
|
|
return $LASTEXITCODE -eq 0
|
|
}
|
|
|
|
function Copy-S3ObjectToLocal {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Key,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationPath
|
|
)
|
|
|
|
New-Item -ItemType Directory -Path ([System.IO.Path]::GetDirectoryName($DestinationPath)) -Force | Out-Null
|
|
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3", "cp",
|
|
"s3://$S3Bucket/$($Key.Trim('/').Replace('\', '/'))",
|
|
$DestinationPath,
|
|
"--only-show-errors"
|
|
) -IgnoreFailure | Out-Null
|
|
|
|
return ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $DestinationPath))
|
|
}
|
|
|
|
function Get-S3JsonDocument {
|
|
param([Parameter(Mandatory = $true)][string]$Key)
|
|
|
|
$tempPath = Join-Path $OutputDir ("_tmp_" + [System.Guid]::NewGuid().ToString("N") + ".json")
|
|
try {
|
|
if (-not (Copy-S3ObjectToLocal -Key $Key -DestinationPath $tempPath)) {
|
|
return $null
|
|
}
|
|
|
|
return Get-Content -LiteralPath $tempPath -Raw | ConvertFrom-Json -AsHashtable
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function New-ZipFromDirectory {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SourceDirectory,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationPath
|
|
)
|
|
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
if (Test-Path -LiteralPath $DestinationPath) {
|
|
Remove-Item -LiteralPath $DestinationPath -Force
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path ([System.IO.Path]::GetDirectoryName($DestinationPath)) -Force | Out-Null
|
|
[System.IO.Compression.ZipFile]::CreateFromDirectory($SourceDirectory, $DestinationPath, [System.IO.Compression.CompressionLevel]::Optimal, $false)
|
|
}
|
|
|
|
function Expand-PayloadSnapshot {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Platform,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$BaselineVersion,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationPath
|
|
)
|
|
|
|
$payloadKey = "lanmountain/update/payloads/$Platform/$BaselineVersion/app-payload.zip"
|
|
if (-not (Test-S3ObjectExists -Key $payloadKey)) {
|
|
return $false
|
|
}
|
|
|
|
$tempZip = Join-Path $OutputDir ("payload-" + $Platform + "-" + $BaselineVersion + ".zip")
|
|
try {
|
|
if (-not (Copy-S3ObjectToLocal -Key $payloadKey -DestinationPath $tempZip)) {
|
|
return $false
|
|
}
|
|
|
|
Clear-Directory -Path $DestinationPath
|
|
Expand-Archive -LiteralPath $tempZip -DestinationPath $DestinationPath -Force
|
|
return $true
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $tempZip -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function Restore-LegacyBaseline {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Platform,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DestinationPath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$VersionFilePath
|
|
)
|
|
|
|
Clear-Directory -Path $DestinationPath
|
|
Remove-Item -LiteralPath $VersionFilePath -Force -ErrorAction SilentlyContinue
|
|
|
|
if ([string]::IsNullOrWhiteSpace($S3Endpoint) -or [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
return
|
|
}
|
|
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3", "sync",
|
|
"s3://$S3Bucket/lanmountain/update/baselines/$Platform/current/",
|
|
$DestinationPath,
|
|
"--only-show-errors"
|
|
) -IgnoreFailure | Out-Null
|
|
|
|
Copy-S3ObjectToLocal -Key "lanmountain/update/baselines/$Platform/version.txt" -DestinationPath $VersionFilePath | Out-Null
|
|
}
|
|
|
|
function ConvertTo-NormalizedVersion {
|
|
param([Parameter(Mandatory = $false)][string]$Value)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
return $null
|
|
}
|
|
|
|
$trimmed = $Value.Trim()
|
|
if ($trimmed.StartsWith("refs/tags/", [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$trimmed = $trimmed.Substring("refs/tags/".Length)
|
|
}
|
|
|
|
if ($trimmed.StartsWith("v", [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$trimmed = $trimmed.Substring(1)
|
|
}
|
|
|
|
if ($trimmed -match '^\d+(\.\d+){1,3}$') {
|
|
return $trimmed
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Resolve-GitTagFromRef {
|
|
param([Parameter(Mandatory = $true)][string]$GitRef)
|
|
|
|
$tag = (& git describe --tags --match "v*" --abbrev=0 $GitRef 2>$null)
|
|
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($tag)) {
|
|
return $null
|
|
}
|
|
|
|
return $tag.Trim()
|
|
}
|
|
|
|
function Get-LatestChannelPointer {
|
|
param([Parameter(Mandatory = $true)][string]$Platform)
|
|
|
|
return Get-S3JsonDocument -Key "lanmountain/update/meta/channels/$Channel/$Platform/latest.json"
|
|
}
|
|
|
|
function Get-CommitRangeInfo {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RangeStart,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RangeEnd
|
|
)
|
|
|
|
$files = (& git diff --name-only "$RangeStart..$RangeEnd" 2>$null)
|
|
if ($LASTEXITCODE -ne 0) {
|
|
return @{
|
|
Start = $RangeStart
|
|
End = $RangeEnd
|
|
ChangeCount = 0
|
|
HasPotentialPayloadImpact = $true
|
|
RequiresComponentExpansion = $true
|
|
SamplePaths = ""
|
|
}
|
|
}
|
|
|
|
$changes = @($files | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
$ignoredPrefixes = @(".github/", ".trae/", "docs/", "PenguinLogisticsOnlineNetworkDistributionSystem/")
|
|
$ignoredExtensions = @(".md", ".txt")
|
|
$expansionPrefixes = @(
|
|
"LanMountainDesktop/",
|
|
"LanMountainDesktop.Launcher/",
|
|
"LanMountainDesktop.Appearance/",
|
|
"LanMountainDesktop.PluginSdk/",
|
|
"LanMountainDesktop.Settings.Core/",
|
|
"LanMountainDesktop.Shared.Contracts/",
|
|
"LanMountainDesktop.Tests/",
|
|
"scripts/"
|
|
)
|
|
$expansionExtensions = @(".csproj", ".props", ".targets", ".sln", ".slnx", ".json", ".axaml", ".resx")
|
|
|
|
$impactfulChanges = [System.Collections.Generic.List[string]]::new()
|
|
$requiresExpansion = $false
|
|
|
|
foreach ($change in $changes) {
|
|
$normalized = $change.Replace('\', '/')
|
|
$extension = [System.IO.Path]::GetExtension($normalized)
|
|
|
|
$isIgnored = $false
|
|
foreach ($ignoredPrefix in $ignoredPrefixes) {
|
|
if ($normalized.StartsWith($ignoredPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$isIgnored = $true
|
|
break
|
|
}
|
|
}
|
|
if (-not $isIgnored -and $ignoredExtensions -contains $extension.ToLowerInvariant()) {
|
|
$isIgnored = $true
|
|
}
|
|
|
|
if ($isIgnored) {
|
|
continue
|
|
}
|
|
|
|
$impactfulChanges.Add($normalized)
|
|
|
|
foreach ($expansionPrefix in $expansionPrefixes) {
|
|
if ($normalized.StartsWith($expansionPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
$requiresExpansion = $true
|
|
break
|
|
}
|
|
}
|
|
|
|
if ($requiresExpansion -or $expansionExtensions -contains $extension.ToLowerInvariant()) {
|
|
$requiresExpansion = $true
|
|
}
|
|
}
|
|
|
|
return @{
|
|
Start = $RangeStart
|
|
End = $RangeEnd
|
|
ChangeCount = $changes.Count
|
|
HasPotentialPayloadImpact = ($impactfulChanges.Count -gt 0)
|
|
RequiresComponentExpansion = $requiresExpansion
|
|
SamplePaths = (($impactfulChanges | Select-Object -First 10) -join "; ")
|
|
}
|
|
}
|
|
|
|
function Update-JsonMetadata {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Metadata
|
|
)
|
|
|
|
$document = Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json -AsHashtable
|
|
if (-not $document.ContainsKey("metadata") -or $null -eq $document.metadata) {
|
|
$document.metadata = @{}
|
|
}
|
|
|
|
foreach ($key in $Metadata.Keys) {
|
|
if ($null -ne $Metadata[$key] -and -not [string]::IsNullOrWhiteSpace([string]$Metadata[$key])) {
|
|
$document.metadata[$key] = [string]$Metadata[$key]
|
|
}
|
|
}
|
|
|
|
$document | ConvertTo-Json -Depth 64 | Set-Content -LiteralPath $Path -Encoding utf8NoBOM
|
|
}
|
|
|
|
function Get-FileMapChangeSummary {
|
|
param([Parameter(Mandatory = $true)][string]$Path)
|
|
|
|
$document = Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json -AsHashtable
|
|
$summary = @{
|
|
Add = 0
|
|
Replace = 0
|
|
Reuse = 0
|
|
Delete = 0
|
|
}
|
|
|
|
foreach ($component in @($document.components)) {
|
|
foreach ($file in @($component.files)) {
|
|
$operation = [string]$file.op
|
|
if ($summary.ContainsKey($operation.Substring(0, 1).ToUpperInvariant() + $operation.Substring(1))) {
|
|
$summary[$operation.Substring(0, 1).ToUpperInvariant() + $operation.Substring(1)]++
|
|
}
|
|
}
|
|
}
|
|
|
|
return $summary
|
|
}
|
|
|
|
function Upload-DirectoryToS3 {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$LocalRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RemotePrefix,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[switch]$SkipExisting
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $LocalRoot)) {
|
|
Write-Host "Skipping missing upload root: $LocalRoot"
|
|
return
|
|
}
|
|
|
|
$files = Get-ChildItem -LiteralPath $LocalRoot -Recurse -File | Sort-Object FullName
|
|
if ($files.Count -eq 0) {
|
|
Write-Host "No files found under $LocalRoot; skipping upload."
|
|
return
|
|
}
|
|
|
|
$index = 0
|
|
foreach ($file in $files) {
|
|
$index++
|
|
$relativePath = Get-RelativePath -Root $LocalRoot -Path $file.FullName
|
|
$key = Get-S3Key -Prefix $RemotePrefix -RelativePath $relativePath
|
|
|
|
if ($SkipExisting -and (Test-S3ObjectExists -Key $key)) {
|
|
if ($index -eq 1 -or $index % 25 -eq 0 -or $index -eq $files.Count) {
|
|
Write-Host "Skipping existing $index/$($files.Count): $key"
|
|
}
|
|
continue
|
|
}
|
|
|
|
if ($index -eq 1 -or $index % 25 -eq 0 -or $index -eq $files.Count) {
|
|
Write-Host "Uploading $index/$($files.Count): $key"
|
|
}
|
|
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3api", "put-object",
|
|
"--bucket", $S3Bucket,
|
|
"--key", $key,
|
|
"--body", $file.FullName
|
|
) | Out-Null
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to upload $key"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Upload-InstallerDirectoryToS3 {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$LocalRoot,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RemotePrefix
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $LocalRoot)) {
|
|
Write-Host "Skipping missing installer upload root: $LocalRoot"
|
|
return
|
|
}
|
|
|
|
$files = Get-ChildItem -LiteralPath $LocalRoot -Recurse -File | Sort-Object FullName
|
|
if ($files.Count -eq 0) {
|
|
Write-Host "No installer files found under $LocalRoot; skipping installer upload."
|
|
return
|
|
}
|
|
|
|
$tempDir = Join-Path $OutputDir ("_aws-installer-config-" + [System.Guid]::NewGuid().ToString("N"))
|
|
$tempConfigPath = Join-Path $tempDir "config"
|
|
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
|
@"
|
|
[default]
|
|
s3 =
|
|
preferred_transfer_client = classic
|
|
addressing_style = path
|
|
max_concurrent_requests = 4
|
|
max_queue_size = 32
|
|
multipart_threshold = 64MB
|
|
multipart_chunksize = 32MB
|
|
payload_signing_enabled = false
|
|
"@ | Set-Content -LiteralPath $tempConfigPath -Encoding ascii
|
|
|
|
$previousConfigFile = $env:AWS_CONFIG_FILE
|
|
$previousRetryMode = $env:AWS_RETRY_MODE
|
|
$previousMaxAttempts = $env:AWS_MAX_ATTEMPTS
|
|
$env:AWS_CONFIG_FILE = $tempConfigPath
|
|
$env:AWS_RETRY_MODE = "adaptive"
|
|
$env:AWS_MAX_ATTEMPTS = "6"
|
|
|
|
try {
|
|
$index = 0
|
|
foreach ($file in $files) {
|
|
$index++
|
|
$relativePath = Get-RelativePath -Root $LocalRoot -Path $file.FullName
|
|
$key = Get-S3Key -Prefix $RemotePrefix -RelativePath $relativePath
|
|
|
|
if (Test-S3ObjectExists -Key $key) {
|
|
if ($index -eq 1 -or $index % 10 -eq 0 -or $index -eq $files.Count) {
|
|
Write-Host "Skipping existing installer $index/$($files.Count): $key"
|
|
}
|
|
continue
|
|
}
|
|
|
|
Write-Host "Uploading installer $index/$($files.Count): $key"
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--cli-connect-timeout", "60",
|
|
"--cli-read-timeout", "0",
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3", "cp",
|
|
$file.FullName,
|
|
"s3://$S3Bucket/$key",
|
|
"--only-show-errors",
|
|
"--no-progress"
|
|
) -IgnoreFailure | Out-Null
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
continue
|
|
}
|
|
|
|
Write-Warning "Multipart installer upload failed for $key, falling back to put-object."
|
|
Invoke-AwsCommandIfPossible -Arguments @(
|
|
"--endpoint-url", $S3Endpoint,
|
|
"--region", $S3Region,
|
|
"s3api", "put-object",
|
|
"--bucket", $S3Bucket,
|
|
"--key", $key,
|
|
"--body", $file.FullName
|
|
) | Out-Null
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to upload installer mirror: $key"
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
if ($null -eq $previousConfigFile) {
|
|
Remove-Item Env:AWS_CONFIG_FILE -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
$env:AWS_CONFIG_FILE = $previousConfigFile
|
|
}
|
|
|
|
if ($null -eq $previousRetryMode) {
|
|
Remove-Item Env:AWS_RETRY_MODE -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
$env:AWS_RETRY_MODE = $previousRetryMode
|
|
}
|
|
|
|
if ($null -eq $previousMaxAttempts) {
|
|
Remove-Item Env:AWS_MAX_ATTEMPTS -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
$env:AWS_MAX_ATTEMPTS = $previousMaxAttempts
|
|
}
|
|
|
|
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $PrivateKeyPath)) {
|
|
throw "Private key file not found: $PrivateKeyPath"
|
|
}
|
|
|
|
$toolProject = Join-Path $PSScriptRoot "..\PenguinLogisticsOnlineNetworkDistributionSystem\src\Plonds.Tool\Plonds.Tool.csproj"
|
|
if (-not (Test-Path -LiteralPath $toolProject)) {
|
|
throw "PLONDS tool project not found: $toolProject"
|
|
}
|
|
|
|
$supportedPlatforms = Get-PlatformConfigurations
|
|
$publishedRoot = Join-Path $OutputDir "published"
|
|
$releaseAssetsRoot = Join-Path $OutputDir "release-assets"
|
|
$baselineRoot = Join-Path $OutputDir "_baselines"
|
|
$legacyRoot = Join-Path $OutputDir "legacy"
|
|
$publishIncremental = ConvertTo-Boolean -Value $PublishIncrementalRelease -DefaultValue $true
|
|
$isFullPayloadRelease = -not $publishIncremental
|
|
$mirrorInstallers = ConvertTo-Boolean -Value $MirrorInstallersToS3 -DefaultValue $false
|
|
$uploadMetaToS3 = ConvertTo-Boolean -Value $UploadMetaToS3 -DefaultValue $true
|
|
$gitHubReleaseBaseUrl = Get-GitHubReleaseBaseUrl -Repository $GitHubRepository -Tag $GitHubTag
|
|
$sourceCommit = (& git rev-parse HEAD 2>$null)
|
|
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($sourceCommit)) {
|
|
$sourceCommit = ""
|
|
}
|
|
else {
|
|
$sourceCommit = $sourceCommit.Trim()
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path $publishedRoot -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $releaseAssetsRoot -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $baselineRoot -Force | Out-Null
|
|
New-Item -ItemType Directory -Path $legacyRoot -Force | Out-Null
|
|
|
|
$repoBaseUrl = if ([string]::IsNullOrWhiteSpace($S3Endpoint) -or [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
$null
|
|
}
|
|
else {
|
|
"$($S3Endpoint.TrimEnd('/'))/$S3Bucket/lanmountain/update/repo/sha256"
|
|
}
|
|
|
|
$installerBaseUrl = if (-not [string]::IsNullOrWhiteSpace($gitHubReleaseBaseUrl)) {
|
|
$gitHubReleaseBaseUrl
|
|
}
|
|
elseif ([string]::IsNullOrWhiteSpace($S3Endpoint) -or [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
$null
|
|
}
|
|
else {
|
|
"$($S3Endpoint.TrimEnd('/'))/$S3Bucket/lanmountain/update/installers"
|
|
}
|
|
$installerMirrorMode = if (-not [string]::IsNullOrWhiteSpace($gitHubReleaseBaseUrl)) {
|
|
"github-release"
|
|
}
|
|
elseif ($mirrorInstallers -and -not [string]::IsNullOrWhiteSpace($installerBaseUrl)) {
|
|
"s3"
|
|
}
|
|
else {
|
|
"none"
|
|
}
|
|
|
|
$resolvedBaselineVersionOverride = ConvertTo-NormalizedVersion -Value $BaselineRef
|
|
$resolvedBaselineRefOverride = if ([string]::IsNullOrWhiteSpace($BaselineRef)) {
|
|
$null
|
|
}
|
|
elseif (-not [string]::IsNullOrWhiteSpace($resolvedBaselineVersionOverride)) {
|
|
"v$resolvedBaselineVersionOverride"
|
|
}
|
|
else {
|
|
$BaselineRef.Trim()
|
|
}
|
|
|
|
$platformStates = @{}
|
|
foreach ($config in $supportedPlatforms) {
|
|
$platform = $config.Platform
|
|
$platformBaselineRoot = Join-Path $baselineRoot $platform
|
|
$baselineCurrentDir = Join-Path $platformBaselineRoot "current"
|
|
$baselineVersionPath = Join-Path $platformBaselineRoot "version.txt"
|
|
$snapshotRoot = Join-Path $platformBaselineRoot "previous-snapshot"
|
|
$emptyRoot = Join-Path $platformBaselineRoot "empty"
|
|
|
|
New-Item -ItemType Directory -Path $platformBaselineRoot -Force | Out-Null
|
|
Clear-Directory -Path $baselineCurrentDir
|
|
Clear-Directory -Path $snapshotRoot
|
|
Clear-Directory -Path $emptyRoot
|
|
|
|
$latestPointer = $null
|
|
$resolvedBaselineVersion = $resolvedBaselineVersionOverride
|
|
$resolvedBaselineRef = $resolvedBaselineRefOverride
|
|
|
|
if (-not $resolvedBaselineVersion) {
|
|
if (-not [string]::IsNullOrWhiteSpace($BaselineRef)) {
|
|
$resolvedBaselineRef = if ($resolvedBaselineRef) { $resolvedBaselineRef } else { $BaselineRef.Trim() }
|
|
$tag = Resolve-GitTagFromRef -GitRef $BaselineRef.Trim()
|
|
if ($tag) {
|
|
$resolvedBaselineVersion = ConvertTo-NormalizedVersion -Value $tag
|
|
if (-not $resolvedBaselineRef) {
|
|
$resolvedBaselineRef = $tag
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$latestPointer = Get-LatestChannelPointer -Platform $platform
|
|
if ($latestPointer) {
|
|
$resolvedBaselineVersion = [string]$latestPointer.version
|
|
$resolvedBaselineRef = if ([string]::IsNullOrWhiteSpace([string]$latestPointer.version)) { $null } else { "v$($latestPointer.version)" }
|
|
}
|
|
}
|
|
}
|
|
|
|
$baselineSource = "none"
|
|
if ($isFullPayloadRelease) {
|
|
"0.0.0" | Set-Content -LiteralPath $baselineVersionPath -Encoding ascii
|
|
$baselineSource = "empty"
|
|
}
|
|
else {
|
|
$restored = $false
|
|
if (-not [string]::IsNullOrWhiteSpace($resolvedBaselineVersion)) {
|
|
$restored = Expand-PayloadSnapshot -Platform $platform -BaselineVersion $resolvedBaselineVersion -DestinationPath $baselineCurrentDir
|
|
if ($restored) {
|
|
$baselineSource = "payload"
|
|
$resolvedBaselineRef = if ($resolvedBaselineRef) { $resolvedBaselineRef } else { "v$resolvedBaselineVersion" }
|
|
}
|
|
}
|
|
|
|
if (-not $restored) {
|
|
Restore-LegacyBaseline -Platform $platform -DestinationPath $baselineCurrentDir -VersionFilePath $baselineVersionPath
|
|
$legacyVersion = if (Test-Path -LiteralPath $baselineVersionPath) {
|
|
(Get-Content -LiteralPath $baselineVersionPath -Raw).Trim()
|
|
}
|
|
else {
|
|
""
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($legacyVersion)) {
|
|
$resolvedBaselineVersion = $legacyVersion
|
|
$resolvedBaselineRef = if ($resolvedBaselineRef) { $resolvedBaselineRef } else { "v$legacyVersion" }
|
|
$baselineSource = "legacy-baseline"
|
|
}
|
|
else {
|
|
"0.0.0" | Set-Content -LiteralPath $baselineVersionPath -Encoding ascii
|
|
$resolvedBaselineVersion = "0.0.0"
|
|
$baselineSource = "empty"
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $baselineVersionPath)) {
|
|
$versionToPersist = if ([string]::IsNullOrWhiteSpace($resolvedBaselineVersion)) { "0.0.0" } else { $resolvedBaselineVersion }
|
|
$versionToPersist | Set-Content -LiteralPath $baselineVersionPath -Encoding ascii
|
|
}
|
|
}
|
|
|
|
$baselineItems = @(Get-ChildItem -LiteralPath $baselineCurrentDir -Force -ErrorAction SilentlyContinue)
|
|
if ($baselineItems.Count -gt 0) {
|
|
foreach ($baselineItem in $baselineItems) {
|
|
Copy-Item -LiteralPath $baselineItem.FullName -Destination $snapshotRoot -Recurse -Force
|
|
}
|
|
$legacyPreviousDir = $snapshotRoot
|
|
}
|
|
else {
|
|
$legacyPreviousDir = $emptyRoot
|
|
}
|
|
|
|
$commitInfo = @{
|
|
Start = $null
|
|
End = $sourceCommit
|
|
ChangeCount = 0
|
|
HasPotentialPayloadImpact = $true
|
|
RequiresComponentExpansion = $true
|
|
SamplePaths = ""
|
|
}
|
|
|
|
if ($IncrementalStrategy -eq "commit-range") {
|
|
$rangeStart = if (-not [string]::IsNullOrWhiteSpace($resolvedBaselineRef)) {
|
|
$resolvedBaselineRef
|
|
}
|
|
elseif (-not [string]::IsNullOrWhiteSpace($resolvedBaselineVersion)) {
|
|
"v$resolvedBaselineVersion"
|
|
}
|
|
else {
|
|
$null
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($rangeStart) -and -not [string]::IsNullOrWhiteSpace($sourceCommit)) {
|
|
$commitInfo = Get-CommitRangeInfo -RangeStart $rangeStart -RangeEnd $sourceCommit
|
|
}
|
|
}
|
|
|
|
$platformStates[$platform] = @{
|
|
Platform = $platform
|
|
ArtifactName = $config.ArtifactName
|
|
BaselineVersion = if ([string]::IsNullOrWhiteSpace($resolvedBaselineVersion)) { "0.0.0" } else { $resolvedBaselineVersion }
|
|
BaselineRef = $resolvedBaselineRef
|
|
BaselineSource = $baselineSource
|
|
LegacyPreviousDir = $legacyPreviousDir
|
|
CommitInfo = $commitInfo
|
|
}
|
|
|
|
Write-Host "Prepared baseline for $platform => version=$($platformStates[$platform].BaselineVersion), source=$baselineSource, strategy=$IncrementalStrategy"
|
|
}
|
|
|
|
$publishArguments = @(
|
|
"run",
|
|
"--project", $toolProject,
|
|
"--",
|
|
"publish",
|
|
"--version", $Version,
|
|
"--app-artifacts-root", $AppArtifactsRoot,
|
|
"--installer-artifacts-root", $InstallerArtifactsRoot,
|
|
"--output-dir", $publishedRoot,
|
|
"--private-key", $PrivateKeyPath,
|
|
"--baseline-root", $baselineRoot,
|
|
"--channel", $Channel,
|
|
"--incremental-strategy", $IncrementalStrategy,
|
|
"--is-full-payload-release", $isFullPayloadRelease.ToString().ToLowerInvariant()
|
|
)
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($repoBaseUrl)) {
|
|
$publishArguments += @("--repo-base-url", $repoBaseUrl)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($installerBaseUrl)) {
|
|
$publishArguments += @("--installer-base-url", $installerBaseUrl)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($sourceCommit)) {
|
|
$publishArguments += @("--source-commit", $sourceCommit)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($resolvedBaselineVersionOverride)) {
|
|
$publishArguments += @("--baseline-version", $resolvedBaselineVersionOverride)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($resolvedBaselineRefOverride)) {
|
|
$publishArguments += @("--baseline-ref", $resolvedBaselineRefOverride)
|
|
}
|
|
|
|
& dotnet @publishArguments
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "PLONDS publish command failed."
|
|
}
|
|
|
|
foreach ($config in $supportedPlatforms) {
|
|
$platform = $config.Platform
|
|
$state = $platformStates[$platform]
|
|
$artifactRoot = Join-Path $AppArtifactsRoot $config.ArtifactName
|
|
if (-not (Test-Path -LiteralPath $artifactRoot)) {
|
|
throw "App payload artifact root not found for ${platform}: $artifactRoot"
|
|
}
|
|
|
|
$currentAppDir = Resolve-AppDirectory -SearchRoot $artifactRoot -Version $Version
|
|
if ([string]::IsNullOrWhiteSpace($currentAppDir)) {
|
|
throw "Unable to locate app payload directory for $platform under $artifactRoot"
|
|
}
|
|
|
|
$distributionId = "plonds-$Version-$platform"
|
|
$manifestPath = Join-Path $publishedRoot "manifests/$distributionId/plonds-filemap.json"
|
|
$manifestSignaturePath = "$manifestPath.sig"
|
|
$distributionPath = Join-Path $publishedRoot "meta/distributions/$distributionId.json"
|
|
$latestPath = Join-Path $publishedRoot "meta/channels/$Channel/$platform/latest.json"
|
|
$payloadSnapshotPath = Join-Path $publishedRoot "payloads/$platform/$Version/app-payload.zip"
|
|
New-ZipFromDirectory -SourceDirectory $currentAppDir -DestinationPath $payloadSnapshotPath
|
|
|
|
$changeSummary = Get-FileMapChangeSummary -Path $manifestPath
|
|
$changeCount = $changeSummary.Add + $changeSummary.Replace + $changeSummary.Delete
|
|
$commitVerificationAdjusted = $false
|
|
if ($IncrementalStrategy -eq "commit-range" -and -not $state.CommitInfo.HasPotentialPayloadImpact -and $changeCount -gt 0) {
|
|
$commitVerificationAdjusted = $true
|
|
Write-Warning "Commit range for $platform predicted no payload impact, but payload diff found $changeCount changes. Keeping payload diff as source of truth."
|
|
}
|
|
|
|
$metadata = @{
|
|
baselineVersion = $state.BaselineVersion
|
|
baselineRef = $state.BaselineRef
|
|
baselineSource = $state.BaselineSource
|
|
sourceCommit = $sourceCommit
|
|
incrementalStrategy = $IncrementalStrategy
|
|
isFullPayloadRelease = $isFullPayloadRelease.ToString().ToLowerInvariant()
|
|
commitRangeStart = $state.CommitInfo.Start
|
|
commitRangeEnd = $state.CommitInfo.End
|
|
commitChangeCount = [string]$state.CommitInfo.ChangeCount
|
|
commitHasPotentialPayloadImpact = [string]$state.CommitInfo.HasPotentialPayloadImpact
|
|
commitRequiresComponentExpansion = [string]$state.CommitInfo.RequiresComponentExpansion
|
|
commitVerificationAdjusted = [string]$commitVerificationAdjusted
|
|
commitSamplePaths = $state.CommitInfo.SamplePaths
|
|
payloadSnapshotPath = "lanmountain/update/payloads/$platform/$Version/app-payload.zip"
|
|
installerMirrorMode = $installerMirrorMode
|
|
installerMirrorBaseUrl = $installerBaseUrl
|
|
}
|
|
|
|
Update-JsonMetadata -Path $manifestPath -Metadata $metadata
|
|
Update-JsonMetadata -Path $distributionPath -Metadata $metadata
|
|
|
|
& (Join-Path $PSScriptRoot "Sign-FileMap.ps1") `
|
|
-FilesJsonPath $manifestPath `
|
|
-PrivateKeyPath $PrivateKeyPath `
|
|
-OutputPath $manifestSignaturePath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to re-sign PLONDS manifest for $platform"
|
|
}
|
|
|
|
$legacyOutputDir = Join-Path $legacyRoot $platform
|
|
New-Item -ItemType Directory -Path $legacyOutputDir -Force | Out-Null
|
|
|
|
& (Join-Path $PSScriptRoot "Generate-DeltaPackage.ps1") `
|
|
-PreviousVersion $state.BaselineVersion `
|
|
-CurrentVersion $Version `
|
|
-PreviousDir $state.LegacyPreviousDir `
|
|
-CurrentDir $currentAppDir `
|
|
-OutputDir $legacyOutputDir
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Generate-DeltaPackage.ps1 failed for $platform"
|
|
}
|
|
|
|
$legacyManifestPath = Join-Path $legacyOutputDir "files.json"
|
|
$legacySignaturePath = Join-Path $legacyOutputDir "files.json.sig"
|
|
& (Join-Path $PSScriptRoot "Sign-FileMap.ps1") `
|
|
-FilesJsonPath $legacyManifestPath `
|
|
-PrivateKeyPath $PrivateKeyPath `
|
|
-OutputPath $legacySignaturePath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Failed to sign legacy manifest for $platform"
|
|
}
|
|
|
|
Copy-Item -LiteralPath $manifestPath -Destination (Join-Path $releaseAssetsRoot "plonds-filemap-$platform.json") -Force
|
|
Copy-Item -LiteralPath $manifestSignaturePath -Destination (Join-Path $releaseAssetsRoot "plonds-filemap-$platform.json.sig") -Force
|
|
Copy-Item -LiteralPath $distributionPath -Destination (Join-Path $releaseAssetsRoot "plonds-distribution-$platform.json") -Force
|
|
Copy-Item -LiteralPath $latestPath -Destination (Join-Path $releaseAssetsRoot "plonds-latest-$platform.json") -Force
|
|
Copy-Item -LiteralPath $payloadSnapshotPath -Destination (Join-Path $releaseAssetsRoot "plonds-payload-$platform.zip") -Force
|
|
|
|
Copy-Item -LiteralPath $legacyManifestPath -Destination (Join-Path $releaseAssetsRoot "files-$platform.json") -Force
|
|
Copy-Item -LiteralPath $legacySignaturePath -Destination (Join-Path $releaseAssetsRoot "files-$platform.json.sig") -Force
|
|
Copy-Item -LiteralPath (Join-Path $legacyOutputDir "update.zip") -Destination (Join-Path $releaseAssetsRoot "update-$platform.zip") -Force
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($S3Endpoint) -and -not [string]::IsNullOrWhiteSpace($S3Bucket)) {
|
|
Upload-DirectoryToS3 -LocalRoot (Join-Path $publishedRoot "payloads") -RemotePrefix "lanmountain/update/payloads" -SkipExisting
|
|
Upload-DirectoryToS3 -LocalRoot (Join-Path $publishedRoot "repo") -RemotePrefix "lanmountain/update/repo" -SkipExisting
|
|
if ($mirrorInstallers) {
|
|
Upload-InstallerDirectoryToS3 -LocalRoot (Join-Path $publishedRoot "installers") -RemotePrefix "lanmountain/update/installers"
|
|
}
|
|
else {
|
|
Write-Host "Skipping blocking S3 installer mirror upload. Installer mirrors will resolve via $installerMirrorMode."
|
|
}
|
|
Upload-DirectoryToS3 -LocalRoot (Join-Path $publishedRoot "manifests") -RemotePrefix "lanmountain/update/manifests"
|
|
if ($uploadMetaToS3) {
|
|
Upload-DirectoryToS3 -LocalRoot (Join-Path $publishedRoot "meta") -RemotePrefix "lanmountain/update/meta"
|
|
}
|
|
else {
|
|
Write-Host "Deferring S3 meta upload until after GitHub Release is published."
|
|
}
|
|
}
|
|
|
|
Write-Host "PLONDS publish staging completed."
|
|
Write-Host "Published root: $publishedRoot"
|
|
Write-Host "Release assets: $releaseAssetsRoot"
|