Files
MoShiro/ci/package-cross-platform.ps1
T
justme 4515d7b075
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/dev-image Pipeline failed
scripts: publish unified Agent artifacts and restore CI coverage [CI]
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.
2026-10-21 11:56:06 +08:00

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
}