Namespaces, assemblies, projects and directories, the solution, binaries, images, systemd units, config paths, environment variables, protocol method names, the media type and the raw chunk magic all move to MoShiro/moshiro. Existing data and deployments are not compatible and must be recreated. Also fix the architecture tests after the solution layout change and make the update tests independent of their order.
173 lines
8.3 KiB
PowerShell
173 lines
8.3 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'
|
|
$projectVersion = [regex]::Match((Get-Content (Join-Path $backend 'Directory.Build.props') -Raw), '<Version>([^<]+)</Version>').Groups[1].Value
|
|
if ($version -ne $projectVersion) {
|
|
throw "Tag version $version does not match project version $projectVersion."
|
|
}
|
|
|
|
$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"
|
|
$probeDirectory = Join-Path $root ".release\publish\$($target.Rid)\probe"
|
|
$enrollDirectory = Join-Path $root ".release\publish\$($target.Rid)\fleet-enroll"
|
|
foreach ($path in @($agentDirectory, $probeDirectory, $enrollDirectory)) {
|
|
if (-not (Test-Path -LiteralPath $path -PathType Container)) {
|
|
throw "Build output is missing: $path"
|
|
}
|
|
}
|
|
|
|
foreach ($role in @('agent', 'probe')) {
|
|
$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
|
|
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")
|
|
if ($role -eq 'agent') {
|
|
$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 'probe') {
|
|
$openwrt = Join-Path $stage 'openwrt'
|
|
$null = New-Item -ItemType Directory -Force -Path $openwrt
|
|
Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/moshiro-probe.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
|
|
}
|