diff --git a/.woodpecker/windows-release.yml b/.woodpecker/windows-release.yml index 5b0775c..019e262 100644 --- a/.woodpecker/windows-release.yml +++ b/.woodpecker/windows-release.yml @@ -18,86 +18,286 @@ depends_on: concurrency: 1 steps: - - name: linux-x64 + - name: build-linux-x64-agent image: powershell - environment: &build-env + environment: &build-environment GITEA_BASE: http://localhost:3100 GITEA_TOKEN: from_secret: gitea_token NUGET_PACKAGES: 'C:\Users\JustMe-NAS\.nuget\packages' - commands: + RID: linux-x64 + COMPONENT: agent + commands: &build-commands - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-x64 + - ./ci/release-cross-platform.ps1 -RuntimeIdentifier $env:RID -Component $env:COMPONENT depends_on: [] - - name: linux-arm64 + - name: build-linux-x64-probe image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-arm64 + environment: &rid-linux-x64-probe + <<: *build-environment + COMPONENT: probe + commands: *build-commands depends_on: [] - - name: linux-arm + - name: build-linux-x64-enroll image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-arm + environment: &rid-linux-x64-enroll + <<: *build-environment + COMPONENT: fleet-enroll + commands: *build-commands depends_on: [] - - name: linux-musl-x64 + - name: build-linux-arm64-agent image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-musl-x64 + environment: &rid-linux-arm64-agent + <<: *build-environment + RID: linux-arm64 + commands: *build-commands depends_on: [] - - name: linux-musl-arm64 + - name: build-linux-arm64-probe image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-musl-arm64 + environment: &rid-linux-arm64-probe + <<: *rid-linux-arm64-agent + COMPONENT: probe + commands: *build-commands depends_on: [] - - name: linux-musl-arm + - name: build-linux-arm64-enroll image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier linux-musl-arm + environment: &rid-linux-arm64-enroll + <<: *rid-linux-arm64-agent + COMPONENT: fleet-enroll + commands: *build-commands depends_on: [] - - name: macos-x64 + - name: build-linux-arm-agent image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier osx-x64 + environment: &rid-linux-arm-agent + <<: *build-environment + RID: linux-arm + commands: *build-commands depends_on: [] - - name: macos-arm64 + - name: build-linux-arm-probe image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier osx-arm64 + environment: &rid-linux-arm-probe + <<: *rid-linux-arm-agent + COMPONENT: probe + commands: *build-commands depends_on: [] - - name: windows-x64 + - name: build-linux-arm-enroll image: powershell - environment: *build-env - commands: - - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier win-x64 + environment: &rid-linux-arm-enroll + <<: *rid-linux-arm-agent + COMPONENT: fleet-enroll + commands: *build-commands depends_on: [] - - name: windows-arm64 + - name: build-linux-musl-x64-agent image: powershell - environment: *build-env + environment: &rid-linux-musl-x64-agent + <<: *build-environment + RID: linux-musl-x64 + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-x64-probe + image: powershell + environment: &rid-linux-musl-x64-probe + <<: *rid-linux-musl-x64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-x64-enroll + image: powershell + environment: &rid-linux-musl-x64-enroll + <<: *rid-linux-musl-x64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm64-agent + image: powershell + environment: &rid-linux-musl-arm64-agent + <<: *build-environment + RID: linux-musl-arm64 + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm64-probe + image: powershell + environment: &rid-linux-musl-arm64-probe + <<: *rid-linux-musl-arm64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm64-enroll + image: powershell + environment: &rid-linux-musl-arm64-enroll + <<: *rid-linux-musl-arm64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm-agent + image: powershell + environment: &rid-linux-musl-arm-agent + <<: *build-environment + RID: linux-musl-arm + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm-probe + image: powershell + environment: &rid-linux-musl-arm-probe + <<: *rid-linux-musl-arm-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-linux-musl-arm-enroll + image: powershell + environment: &rid-linux-musl-arm-enroll + <<: *rid-linux-musl-arm-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-macos-x64-agent + image: powershell + environment: &rid-macos-x64-agent + <<: *build-environment + RID: osx-x64 + commands: *build-commands + depends_on: [] + + - name: build-macos-x64-probe + image: powershell + environment: &rid-macos-x64-probe + <<: *rid-macos-x64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-macos-x64-enroll + image: powershell + environment: &rid-macos-x64-enroll + <<: *rid-macos-x64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-macos-arm64-agent + image: powershell + environment: &rid-macos-arm64-agent + <<: *build-environment + RID: osx-arm64 + commands: *build-commands + depends_on: [] + + - name: build-macos-arm64-probe + image: powershell + environment: &rid-macos-arm64-probe + <<: *rid-macos-arm64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-macos-arm64-enroll + image: powershell + environment: &rid-macos-arm64-enroll + <<: *rid-macos-arm64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-windows-x64-agent + image: powershell + environment: &rid-windows-x64-agent + <<: *build-environment + RID: win-x64 + commands: *build-commands + depends_on: [] + + - name: build-windows-x64-probe + image: powershell + environment: &rid-windows-x64-probe + <<: *rid-windows-x64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-windows-x64-enroll + image: powershell + environment: &rid-windows-x64-enroll + <<: *rid-windows-x64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: build-windows-arm64-agent + image: powershell + environment: &rid-windows-arm64-agent + <<: *build-environment + RID: win-arm64 + commands: *build-commands + depends_on: [] + + - name: build-windows-arm64-probe + image: powershell + environment: &rid-windows-arm64-probe + <<: *rid-windows-arm64-agent + COMPONENT: probe + commands: *build-commands + depends_on: [] + + - name: build-windows-arm64-enroll + image: powershell + environment: &rid-windows-arm64-enroll + <<: *rid-windows-arm64-agent + COMPONENT: fleet-enroll + commands: *build-commands + depends_on: [] + + - name: package-and-upload + image: powershell + environment: *build-environment commands: - Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force - - ./ci/release-cross-platform.ps1 -RuntimeIdentifier win-arm64 - depends_on: [] + - ./ci/package-cross-platform.ps1 + depends_on: + - build-linux-x64-agent + - build-linux-x64-probe + - build-linux-x64-enroll + - build-linux-arm64-agent + - build-linux-arm64-probe + - build-linux-arm64-enroll + - build-linux-arm-agent + - build-linux-arm-probe + - build-linux-arm-enroll + - build-linux-musl-x64-agent + - build-linux-musl-x64-probe + - build-linux-musl-x64-enroll + - build-linux-musl-arm64-agent + - build-linux-musl-arm64-probe + - build-linux-musl-arm64-enroll + - build-linux-musl-arm-agent + - build-linux-musl-arm-probe + - build-linux-musl-arm-enroll + - build-macos-x64-agent + - build-macos-x64-probe + - build-macos-x64-enroll + - build-macos-arm64-agent + - build-macos-arm64-probe + - build-macos-arm64-enroll + - build-windows-x64-agent + - build-windows-x64-probe + - build-windows-x64-enroll + - build-windows-arm64-agent + - build-windows-arm64-probe + - build-windows-arm64-enroll diff --git a/ci/package-cross-platform.ps1 b/ci/package-cross-platform.ps1 new file mode 100644 index 0000000..dc45eea --- /dev/null +++ b/ci/package-cross-platform.ps1 @@ -0,0 +1,145 @@ +$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), '([^<]+)').Groups[1].Value +if ($version -ne $projectVersion) { + throw "Tag version $version does not match project version $projectVersion." +} + +Add-Type -AssemblyName System.Formats.Tar +$giteaBase = if ($env:GITEA_BASE) { $env:GITEA_BASE.TrimEnd('/') } else { 'http://localhost:3100' } +$netrc = Join-Path $env:TEMP ("monitor-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) { + $tarFile = [IO.File]::Create($ArchivePath) + $gzip = [IO.Compression.GZipStream]::new($tarFile, [IO.Compression.CompressionLevel]::Optimal, $true) + $writer = [System.Formats.Tar.TarWriter]::new($gzip, [System.Formats.Tar.TarEntryFormat]::Pax, $true) + $executableMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::UserExecute -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::GroupExecute -bor [System.IO.UnixFileMode]::OtherRead -bor [System.IO.UnixFileMode]::OtherExecute + $regularMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::OtherRead + try { + foreach ($file in Get-ChildItem -LiteralPath $SourceDirectory -File -Recurse) { + $relativePath = [IO.Path]::GetRelativePath($SourceDirectory, $file.FullName).Replace('\', '/') + $entry = [System.Formats.Tar.PaxTarEntry]::new([System.Formats.Tar.TarEntryType]::RegularFile, $relativePath) + $entry.Mode = if ($Executables -contains $relativePath) { $executableMode } else { $regularMode } + $entry.DataStream = [IO.File]::OpenRead($file.FullName) + try { $writer.WriteEntry($entry) } finally { $entry.DataStream.Dispose() } + } + } + finally { + $writer.Dispose() + $gzip.Dispose() + $tarFile.Dispose() + } +} + +function Upload-Package([string]$File) { + $name = Split-Path -Leaf $File + $url = "$giteaBase/api/packages/justme/generic/monitor-node/$version/$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." + } + if ($status -match '^2') { + Write-Output "uploaded Gitea package $name" + } + elseif ($status -eq '409') { + Write-Output "Gitea package $name already exists; reusing the existing release file" + } + else { + throw "Gitea package upload failed for $name (HTTP $status)." + } +} + +try { + $targets = @( + @{ Rid = 'linux-x64'; Os = 'linux'; Arch = 'amd64'; Windows = $false }, + @{ Rid = 'linux-arm64'; Os = 'linux'; Arch = 'arm64'; Windows = $false }, + @{ Rid = 'linux-arm'; Os = 'linux'; Arch = 'arm'; 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 = 'linux-musl-arm'; Os = 'linux'; Arch = 'musl-arm'; 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 + 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") "monitor-$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 = @("monitor-$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/monitor-$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/monitor-probe.init') -Destination $openwrt + Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/monitor.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) { "monitor-$role-v$version-windows-$($target.Arch).zip" } else { "monitor-$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)) + Upload-Package $archive + Upload-Package $checksumPath + } + } +} +finally { + Remove-Item -LiteralPath $netrc -Force -ErrorAction SilentlyContinue +} diff --git a/ci/release-cross-platform.ps1 b/ci/release-cross-platform.ps1 index 662f090..8b2ca53 100644 --- a/ci/release-cross-platform.ps1 +++ b/ci/release-cross-platform.ps1 @@ -5,7 +5,10 @@ param( 'linux-musl-x64', 'linux-musl-arm64', 'linux-musl-arm', 'osx-x64', 'osx-arm64', 'win-x64', 'win-arm64' )] - [string]$RuntimeIdentifier + [string]$RuntimeIdentifier, + [Parameter(Mandatory)] + [ValidateSet('agent', 'probe', 'fleet-enroll')] + [string]$Component ) $ErrorActionPreference = 'Stop' @@ -21,27 +24,8 @@ $projectVersion = [regex]::Match((Get-Content (Join-Path $backend 'Directory.Bui if ($version -ne $projectVersion) { throw "Tag version $version does not match project version $projectVersion." } -if (-not $env:GITEA_TOKEN) { - throw 'GITEA_TOKEN is required.' -} -$isWindows = $RuntimeIdentifier.StartsWith('win-', [StringComparison]::Ordinal) -$isMacOS = $RuntimeIdentifier.StartsWith('osx-', [StringComparison]::Ordinal) -$os = if ($isWindows) { 'windows' } elseif ($isMacOS) { 'macos' } else { 'linux' } -$arch = switch ($RuntimeIdentifier) { - 'linux-x64' { 'amd64' } - 'linux-arm64' { 'arm64' } - 'linux-arm' { 'arm' } - 'linux-musl-x64' { 'musl-amd64' } - 'linux-musl-arm64' { 'musl-arm64' } - 'linux-musl-arm' { 'musl-arm' } - 'osx-x64' { 'x64' } - 'osx-arm64' { 'arm64' } - 'win-x64' { 'amd64' } - 'win-arm64' { 'arm64' } -} - -if ($isWindows) { +if ($RuntimeIdentifier -in @('win-x64', 'win-arm64')) { $vcArchitecture = if ($RuntimeIdentifier -eq 'win-x64') { 'x64' } else { 'arm64' } $vsWhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' $vsPath = & $vsWhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath @@ -58,152 +42,33 @@ if ($isWindows) { } } -$publishRoot = Join-Path $root ".release\$RuntimeIdentifier-$PID" -$buildRoot = Join-Path $root ".release\build\$RuntimeIdentifier-$PID" -$packageRoot = Join-Path $root '.release\packages' -$agentOut = Join-Path $publishRoot 'agent' -$probeOut = Join-Path $publishRoot 'probe' -$enrollOut = Join-Path $publishRoot 'fleet-enroll' -$null = New-Item -ItemType Directory -Force -Path $agentOut, $probeOut, $enrollOut, $packageRoot - -function Publish-Aot([string]$Project, [string]$Output) { - $arguments = @( - 'publish', $Project, - '-m:2', - '-c', 'Release', - '-r', $RuntimeIdentifier, - '--self-contained', 'true', - '-p:PublishAot=true', - '-p:StripSymbols=true', - '-p:DebugType=none', - '--artifacts-path', $buildRoot, - '-o', $Output - ) - Push-Location $backend - try { - & dotnet @arguments - if ($LASTEXITCODE -ne 0) { - throw "NativeAOT publish failed for $Project ($RuntimeIdentifier)." - } - } - finally { - Pop-Location - } -} - -function New-TarGzArchive([string]$SourceDirectory, [string]$ArchivePath, [string[]]$Executables) { - $tarFile = [IO.File]::Create($ArchivePath) - $gzip = [IO.Compression.GZipStream]::new($tarFile, [IO.Compression.CompressionLevel]::Optimal, $true) - $writer = [System.Formats.Tar.TarWriter]::new($gzip, [System.Formats.Tar.TarEntryFormat]::Pax, $true) - $executableMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::UserExecute -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::GroupExecute -bor [System.IO.UnixFileMode]::OtherRead -bor [System.IO.UnixFileMode]::OtherExecute - $regularMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::OtherRead - try { - foreach ($file in Get-ChildItem -LiteralPath $SourceDirectory -File -Recurse) { - $relativePath = [IO.Path]::GetRelativePath($SourceDirectory, $file.FullName).Replace('\', '/') - $entry = [System.Formats.Tar.PaxTarEntry]::new([System.Formats.Tar.TarEntryType]::RegularFile, $relativePath) - $entry.Mode = if ($Executables -contains $relativePath) { $executableMode } else { $regularMode } - $entry.DataStream = [IO.File]::OpenRead($file.FullName) - try { - $writer.WriteEntry($entry) - } - finally { - $entry.DataStream.Dispose() - } - } - } - finally { - $writer.Dispose() - $gzip.Dispose() - $tarFile.Dispose() - } -} - -Publish-Aot 'agent/src/Monitor.Agent' $agentOut -Publish-Aot 'probe/src/Monitor.Probe' $probeOut -Publish-Aot 'fleet/tools/fleet-enroll' $enrollOut - -$giteaBase = if ($env:GITEA_BASE) { $env:GITEA_BASE.TrimEnd('/') } else { 'http://localhost:3100' } -$netrc = Join-Path $env:TEMP ("monitor-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 Publish-Package([string]$Role, [string]$BinaryDirectory) { - $stage = Join-Path $publishRoot "stage-$Role" - $null = New-Item -ItemType Directory -Force -Path $stage - $binaryName = if ($isWindows) { "monitor-$Role.exe" } else { "monitor-$Role" } - Copy-Item -LiteralPath (Join-Path $BinaryDirectory $binaryName) -Destination $stage - Copy-Item -LiteralPath (Join-Path $root 'LICENSE') -Destination $stage - Copy-Item -LiteralPath (Join-Path $root "backend\$Role\README.md") -Destination $stage - - if ($Role -eq 'agent') { - $enrollName = if ($isWindows) { 'fleet-enroll.exe' } else { 'fleet-enroll' } - Copy-Item -LiteralPath (Join-Path $enrollOut $enrollName) -Destination $stage - } - if ($os -eq 'linux') { - Copy-Item -LiteralPath (Join-Path $root 'packaging/install.sh') -Destination $stage - $systemd = Join-Path $stage 'systemd' - $null = New-Item -ItemType Directory -Force -Path $systemd - Copy-Item -LiteralPath (Join-Path $root "packaging/systemd/monitor-$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/monitor-probe.init') -Destination $openwrt - Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/monitor.keep') -Destination $openwrt - } - } - elseif ($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 ($isWindows) { - "monitor-$Role-v$version-windows-$arch.zip" - } - else { - "monitor-$Role-v$version-$os-$arch.tar.gz" - } - $archive = Join-Path $packageRoot $archiveName - if ($isWindows) { - Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $archive -CompressionLevel Optimal -Force - } - else { - $executables = if ($os -eq 'linux') { @("monitor-$Role", 'install.sh') } else { @("monitor-$Role") } - if ($Role -eq 'agent') { $executables += 'fleet-enroll' } - New-TarGzArchive $stage $archive $executables - } - - $hash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant() - $checksumPath = "$archive.sha256" - [IO.File]::WriteAllText($checksumPath, "$hash $archiveName`n", [Text.UTF8Encoding]::new($false)) - - foreach ($file in @($archive, $checksumPath)) { - $name = Split-Path -Leaf $file - $url = "$giteaBase/api/packages/justme/generic/monitor-node/$version/$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." - } - if ($status -match '^2') { - Write-Output "uploaded Gitea package $name" - } - elseif ($status -eq '409') { - Write-Output "Gitea package $name already exists; reusing the existing release file" - } - else { - throw "Gitea package upload failed for $name (HTTP $status)." - } - } +$project = switch ($Component) { + 'agent' { 'agent/src/Monitor.Agent' } + 'probe' { 'probe/src/Monitor.Probe' } + 'fleet-enroll' { 'fleet/tools/fleet-enroll' } } +$output = Join-Path $root ".release\publish\$RuntimeIdentifier\$Component" +$buildRoot = Join-Path $root ".release\build\$RuntimeIdentifier-$Component" +$null = New-Item -ItemType Directory -Force -Path $output +Push-Location $backend try { - Publish-Package 'agent' $agentOut - Publish-Package 'probe' $probeOut + & dotnet publish $project ` + -m:1 ` + -c Release ` + -r $RuntimeIdentifier ` + --self-contained true ` + -p:PublishAot=true ` + -p:StripSymbols=true ` + -p:DebugType=none ` + --artifacts-path $buildRoot ` + -o $output + if ($LASTEXITCODE -ne 0) { + throw "NativeAOT publish failed for $project ($RuntimeIdentifier)." + } } finally { - Remove-Item -LiteralPath $netrc -Force -ErrorAction SilentlyContinue + Pop-Location } + +Write-Output "built $Component for $RuntimeIdentifier"