Remove Probe build jobs and release paths. Transfer Gitea publishing credentials to both Agent architecture jobs, publish each platform archive once, and run slow tests after development images are published.
180 lines
8.8 KiB
PowerShell
180 lines
8.8 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version Latest
|
|
|
|
if (-not $env:CI_COMMIT_TAG -or $env:CI_COMMIT_TAG -notmatch '^v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$') {
|
|
throw 'Release must run from a vMAJOR.MINOR.PATCH tag.'
|
|
}
|
|
if (-not $env:GITEA_TOKEN) {
|
|
throw 'GITEA_TOKEN is required.'
|
|
}
|
|
$version = $Matches[1]
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$backend = Join-Path $root 'backend'
|
|
# 版本取自 tag:写进工作区的 Directory.Build.props(仓库里的 <Version> 只是占位,发版不用再提交 bump)
|
|
$propsPath = Join-Path $backend 'Directory.Build.props'
|
|
$props = Get-Content $propsPath -Raw
|
|
Set-Content -LiteralPath $propsPath -Value ([regex]::Replace($props, '<Version>[^<]*</Version>', "<Version>$version</Version>")) -NoNewline
|
|
|
|
$tarProject = Join-Path $PSScriptRoot 'MoShiro.Tar\MoShiro.Tar.csproj'
|
|
$tarTool = Join-Path $PSScriptRoot 'MoShiro.Tar\bin\Release\net10.0\MoShiro.Tar.dll'
|
|
& dotnet build $tarProject -c Release --nologo
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw 'Could not build the cross-platform tar helper.'
|
|
}
|
|
$giteaBase = if ($env:GITEA_BASE) { $env:GITEA_BASE.TrimEnd('/') } else { 'http://localhost:3100' }
|
|
$netrc = Join-Path $env:TEMP ("moshiro-gitea-$([guid]::NewGuid().ToString('N')).netrc")
|
|
$giteaHost = ([Uri]$giteaBase).Host
|
|
Set-Content -LiteralPath $netrc -Value "machine $giteaHost login justme password $($env:GITEA_TOKEN)" -Encoding ascii -NoNewline
|
|
|
|
function New-TarGzArchive([string]$SourceDirectory, [string]$ArchivePath, [string[]]$Executables) {
|
|
& dotnet $tarTool $SourceDirectory $ArchivePath @Executables
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Could not create tar archive $ArchivePath."
|
|
}
|
|
}
|
|
|
|
try {
|
|
$targets = @(
|
|
@{ Rid = 'linux-x64'; Os = 'linux'; Arch = 'amd64'; Windows = $false },
|
|
@{ Rid = 'linux-arm64'; Os = 'linux'; Arch = 'arm64'; Windows = $false },
|
|
@{ Rid = 'linux-musl-x64'; Os = 'linux'; Arch = 'musl-amd64'; Windows = $false },
|
|
@{ Rid = 'linux-musl-arm64'; Os = 'linux'; Arch = 'musl-arm64'; Windows = $false },
|
|
@{ Rid = 'osx-x64'; Os = 'macos'; Arch = 'x64'; Windows = $false },
|
|
@{ Rid = 'osx-arm64'; Os = 'macos'; Arch = 'arm64'; Windows = $false },
|
|
@{ Rid = 'win-x64'; Os = 'windows'; Arch = 'amd64'; Windows = $true },
|
|
@{ Rid = 'win-arm64'; Os = 'windows'; Arch = 'arm64'; Windows = $true }
|
|
)
|
|
$packageRoot = Join-Path $root '.release\packages'
|
|
$null = New-Item -ItemType Directory -Force -Path $packageRoot
|
|
$uploadFiles = [System.Collections.Generic.List[string]]::new()
|
|
foreach ($target in $targets) {
|
|
$agentDirectory = Join-Path $root ".release\publish\$($target.Rid)\agent"
|
|
$enrollDirectory = Join-Path $root ".release\publish\$($target.Rid)\fleet-enroll"
|
|
foreach ($path in @($agentDirectory, $enrollDirectory)) {
|
|
if (-not (Test-Path -LiteralPath $path -PathType Container)) {
|
|
throw "Build output is missing: $path"
|
|
}
|
|
}
|
|
|
|
$quicRuntimeDirectory = $null
|
|
if ($target.Os -eq 'linux') {
|
|
$quicRuntimeDirectory = Join-Path $root ".release\quic-runtime\$($target.Rid)"
|
|
& (Join-Path $PSScriptRoot 'Bundle-QuicRuntime.ps1') -RuntimeIdentifier $target.Rid -Destination $quicRuntimeDirectory
|
|
if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) { throw "Could not bundle QUIC runtime for $($target.Rid)." }
|
|
}
|
|
|
|
foreach ($role in @('agent')) {
|
|
$stage = Join-Path $root ".release\stage\$($target.Rid)\$role"
|
|
$null = New-Item -ItemType Directory -Force -Path $stage
|
|
$extension = if ($target.Windows) { '.exe' } else { '' }
|
|
Copy-Item -LiteralPath (Join-Path (Join-Path $root ".release\publish\$($target.Rid)\$role") "moshiro-$role$extension") -Destination $stage
|
|
if ($quicRuntimeDirectory) {
|
|
Copy-Item -Path (Join-Path $quicRuntimeDirectory '*.so.*') -Destination $stage -ErrorAction Stop
|
|
}
|
|
Copy-Item -LiteralPath (Join-Path $root 'LICENSE') -Destination $stage
|
|
Copy-Item -LiteralPath (Join-Path $root "backend\$role\README.md") -Destination $stage
|
|
|
|
$executables = @("moshiro-$role$extension")
|
|
$enrollName = if ($target.Windows) { 'fleet-enroll.exe' } else { 'fleet-enroll' }
|
|
Copy-Item -LiteralPath (Join-Path $enrollDirectory $enrollName) -Destination $stage
|
|
$executables += $enrollName
|
|
if ($target.Os -eq 'linux') {
|
|
Copy-Item -LiteralPath (Join-Path $root 'packaging/install.sh') -Destination $stage
|
|
$executables += 'install.sh'
|
|
$systemd = Join-Path $stage 'systemd'
|
|
$null = New-Item -ItemType Directory -Force -Path $systemd
|
|
Copy-Item -LiteralPath (Join-Path $root "packaging/systemd/moshiro-$role.service") -Destination $systemd
|
|
if ($role -eq 'agent') {
|
|
$openwrt = Join-Path $stage 'openwrt'
|
|
$null = New-Item -ItemType Directory -Force -Path $openwrt
|
|
Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/moshiro-agent.init') -Destination $openwrt
|
|
Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/moshiro.keep') -Destination $openwrt
|
|
}
|
|
}
|
|
elseif ($target.Os -eq 'macos') {
|
|
@'
|
|
This macOS NativeAOT binary is not signed with a Developer ID certificate and
|
|
has not been notarized by Apple. Apple Silicon builds receive an ad-hoc code
|
|
signature; Intel builds are unsigned. Gatekeeper may block browser-downloaded
|
|
files. A Developer ID signature and notarization are required for a seamless
|
|
consumer install.
|
|
'@ | Set-Content -LiteralPath (Join-Path $stage 'MACOS-SECURITY.txt') -Encoding utf8
|
|
}
|
|
|
|
$archiveName = if ($target.Windows) { "moshiro-$role-v$version-windows-$($target.Arch).zip" } else { "moshiro-$role-v$version-$($target.Os)-$($target.Arch).tar.gz" }
|
|
$archive = Join-Path $packageRoot $archiveName
|
|
if ($target.Windows) {
|
|
Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $archive -CompressionLevel Optimal -Force
|
|
}
|
|
else {
|
|
New-TarGzArchive $stage $archive $executables
|
|
}
|
|
$hash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
$checksum = "$hash $archiveName`n"
|
|
$checksumPath = "$archive.sha256"
|
|
[IO.File]::WriteAllText($checksumPath, $checksum, [Text.UTF8Encoding]::new($false))
|
|
[void]$uploadFiles.Add($archive)
|
|
[void]$uploadFiles.Add($checksumPath)
|
|
}
|
|
}
|
|
|
|
$uploadScript = @'
|
|
param([string]$File, [string]$Base, [string]$PackageVersion, [string]$Netrc)
|
|
$ErrorActionPreference = 'Stop'
|
|
$name = [IO.Path]::GetFileName($File)
|
|
$url = "$Base/api/packages/justme/generic/moshiro-node/$PackageVersion/$name"
|
|
$status = & curl.exe --silent --show-error --retry 3 --netrc-file $Netrc --upload-file $File --output NUL --write-out '%{http_code}' $url
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Gitea package upload failed for $name (curl exit $LASTEXITCODE)."
|
|
}
|
|
if ($status -match '^2') {
|
|
"uploaded Gitea package $name"
|
|
}
|
|
elseif ($status -eq '409') {
|
|
"Gitea package $name already exists; reusing the existing release file"
|
|
}
|
|
else {
|
|
throw "Gitea package upload failed for $name (HTTP $status)."
|
|
}
|
|
'@
|
|
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 8)
|
|
$runspacePool.Open()
|
|
$pendingUploads = [System.Collections.Generic.List[object]]::new()
|
|
try {
|
|
foreach ($file in $uploadFiles) {
|
|
$powerShell = [PowerShell]::Create()
|
|
$powerShell.RunspacePool = $runspacePool
|
|
[void]$powerShell.AddScript($uploadScript).AddArgument($file).AddArgument($giteaBase).AddArgument($version).AddArgument($netrc)
|
|
$pendingUploads.Add([pscustomobject]@{ PowerShell = $powerShell; Handle = $powerShell.BeginInvoke() })
|
|
}
|
|
|
|
$uploadErrors = [System.Collections.Generic.List[string]]::new()
|
|
foreach ($pending in $pendingUploads) {
|
|
try {
|
|
foreach ($message in $pending.PowerShell.EndInvoke($pending.Handle)) {
|
|
Write-Output $message
|
|
}
|
|
foreach ($errorRecord in $pending.PowerShell.Streams.Error) {
|
|
$uploadErrors.Add($errorRecord.ToString())
|
|
}
|
|
}
|
|
catch {
|
|
$uploadErrors.Add($_.Exception.Message)
|
|
}
|
|
}
|
|
if ($uploadErrors.Count -gt 0) {
|
|
throw "One or more Gitea package uploads failed:`n$($uploadErrors -join "`n")"
|
|
}
|
|
}
|
|
finally {
|
|
foreach ($pending in $pendingUploads) {
|
|
$pending.PowerShell.Dispose()
|
|
}
|
|
$runspacePool.Close()
|
|
$runspacePool.Dispose()
|
|
}
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $netrc -Force -ErrorAction SilentlyContinue
|
|
}
|