mbproxy: cross-platform support — Linux/systemd alongside Windows

Make the service build, run, and install on Linux as a first-class
target while keeping the Windows Service + Event Log behaviour intact.

- Build: drop the hardcoded win-x64 RID — single-file publish now works
  for any RID. publish.ps1 gains -Rid; new publish.sh for Linux hosts.
- Diagnostics: DiagnosticSinkSelector picks the Error+ sink per host —
  Windows Event Log under the SCM, local syslog under systemd
  (Serilog.Sinks.SyslogMessages), none for interactive runs. The
  EventLog truncation helper is extracted so it is testable cross-OS.
- Host: Program.cs registers AddSystemd() alongside AddWindowsService().
- Config: a RID-conditioned appsettings template ships Windows or Unix
  paths; both templates are schema-validated by a test.
- Install: systemd unit (Type=exec) plus install.sh / uninstall.sh.
  Also fixes two cross-platform bugs found while testing: install.ps1
  and uninstall.ps1 used New-EventLog / Remove-EventLog (absent in
  PowerShell 7), and the E2E sim launcher hardcoded Windows venv paths.
- Docs updated across README, CLAUDE.md, and docs/ for dual-platform.

413 tests pass on Windows; 374 (all non-simulator) on Linux.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-15 09:41:59 -04:00
parent 0868613890
commit b330faff03
29 changed files with 1805 additions and 106 deletions
+34 -21
View File
@@ -1,19 +1,27 @@
<#
.SYNOPSIS
Publishes Mbproxy.exe in two flavours: self-contained and framework-dependent.
Publishes the Mbproxy binary in two flavours: self-contained and framework-dependent.
.DESCRIPTION
Produces two single-file win-x64 builds under <repo>\publish-out\:
Produces two single-file builds for the requested runtime under <repo>\publish-out\:
self-contained\Mbproxy.exe ~100 MB — bundles the .NET 10 runtime;
no .NET install needed on target.
framework-dependent\Mbproxy.exe ~1.6 MB — requires .NET 10 + ASP.NET Core
runtime preinstalled on target.
self-contained\ ~100 MB — bundles the .NET 10 + ASP.NET Core runtime;
no .NET install needed on the target.
framework-dependent\ ~1.6 MB — requires the .NET 10 + ASP.NET Core runtime
preinstalled on the target.
Both builds use the Release configuration and inherit the publish settings
declared in src\Mbproxy\Mbproxy.csproj (PublishSingleFile=true,
IncludeNativeLibrariesForSelfExtract=true). The framework-dependent build
overrides SelfContained=false on the command line.
The runtime is selected with -Rid (default win-x64). The binary is Mbproxy.exe on
Windows RIDs and Mbproxy on Linux/macOS RIDs.
Both builds use the Release configuration and inherit the publish settings declared
in src\Mbproxy\Mbproxy.csproj (PublishSingleFile=true, SelfContained=true,
IncludeNativeLibrariesForSelfExtract=true; those settings are gated on an explicit
RID, which is supplied here). The framework-dependent build overrides
SelfContained=false on the command line.
.PARAMETER Rid
.NET runtime identifier to publish for. Examples: win-x64, linux-x64.
Default: win-x64
.PARAMETER OutputDir
Root output directory. Two subfolders are created beneath it.
@@ -24,10 +32,12 @@
.EXAMPLE
.\publish.ps1
.\publish.ps1 -Clean
.\publish.ps1 -Rid linux-x64
.\publish.ps1 -Rid win-x64 -Clean
#>
[CmdletBinding()]
param(
[string]$Rid = 'win-x64',
[string]$OutputDir = (Join-Path (Split-Path -Parent $PSScriptRoot) 'publish-out'),
[switch]$Clean
)
@@ -46,15 +56,18 @@ if ($Clean -and (Test-Path $OutputDir)) {
Remove-Item -Recurse -Force $OutputDir
}
# Binary name: Windows RIDs produce an .exe, every other RID produces an extensionless ELF/Mach-O.
$exeName = if ($Rid -like 'win-*') { 'Mbproxy.exe' } else { 'Mbproxy' }
$selfContainedOut = Join-Path $OutputDir 'self-contained'
$frameworkDependentOut = Join-Path $OutputDir 'framework-dependent'
Write-Host "`n=== Publishing self-contained (~100 MB) ===" -ForegroundColor Cyan
& dotnet publish $csproj -c Release -r win-x64 -o $selfContainedOut --nologo
Write-Host "`n=== Publishing self-contained ($Rid, ~100 MB) ===" -ForegroundColor Cyan
& dotnet publish $csproj -c Release -r $Rid -o $selfContainedOut --nologo
if ($LASTEXITCODE -ne 0) { throw "self-contained publish failed (exit $LASTEXITCODE)" }
Write-Host "`n=== Publishing framework-dependent (~1.6 MB) ===" -ForegroundColor Cyan
& dotnet publish $csproj -c Release -r win-x64 -p:SelfContained=false -p:PublishSingleFile=true -o $frameworkDependentOut --nologo
Write-Host "`n=== Publishing framework-dependent ($Rid, ~1.6 MB) ===" -ForegroundColor Cyan
& dotnet publish $csproj -c Release -r $Rid -p:SelfContained=false -p:PublishSingleFile=true -o $frameworkDependentOut --nologo
if ($LASTEXITCODE -ne 0) { throw "framework-dependent publish failed (exit $LASTEXITCODE)" }
function Format-Size {
@@ -63,14 +76,14 @@ function Format-Size {
else { '{0:N1} KB' -f ($Bytes / 1KB) }
}
Write-Host "`n=== Result ===" -ForegroundColor Green
Write-Host "`n=== Result ($Rid) ===" -ForegroundColor Green
foreach ($flavour in 'self-contained','framework-dependent') {
$exe = Join-Path $OutputDir "$flavour\Mbproxy.exe"
if (Test-Path $exe) {
$size = (Get-Item $exe).Length
Write-Host (" {0,-22} {1,10} {2}" -f $flavour, (Format-Size $size), $exe)
$bin = Join-Path $OutputDir "$flavour\$exeName"
if (Test-Path $bin) {
$size = (Get-Item $bin).Length
Write-Host (" {0,-22} {1,10} {2}" -f $flavour, (Format-Size $size), $bin)
} else {
Write-Warning "Missing: $exe"
Write-Warning "Missing: $bin"
}
}
Write-Host ""