build(quic): bundle Linux runtime libraries [CI]
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/dev-image unknown status

This commit is contained in:
justme committed 2026-10-10 04:01:24 +08:00
1 parent e2e5e437cb
commit 29d998bc94
18 files changed
+227 -15

No files matched your search

+141
View File
@@ -0,0 +1,141 @@
param(
[Parameter(Mandatory)]
[ValidateSet('linux-x64', 'linux-arm64', 'linux-musl-x64', 'linux-musl-arm64')]
[string]$RuntimeIdentifier,
[Parameter(Mandatory)]
[string]$Destination
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$arch = if ($RuntimeIdentifier.EndsWith('arm64')) { 'arm64' } else { 'amd64' }
$root = Split-Path -Parent $PSScriptRoot
$cacheRoot = if ($env:MOSHIRO_QUIC_CACHE) {
$env:MOSHIRO_QUIC_CACHE
}
elseif ($env:NUGET_PACKAGES) {
Join-Path (Split-Path -Parent $env:NUGET_PACKAGES) 'moshiro-quic-runtime'
}
else {
Join-Path $root '.release\quic-package-cache'
}
$cache = Join-Path $cacheRoot $RuntimeIdentifier
$null = New-Item -ItemType Directory -Force -Path $cache, $Destination
Get-ChildItem -LiteralPath $Destination -Filter '*.so.*' -File -ErrorAction SilentlyContinue | Remove-Item -Force
function Invoke-Tar([string[]]$Arguments) {
$tar = if (Get-Command tar.exe -ErrorAction SilentlyContinue) { 'tar.exe' } else { 'tar' }
& $tar @Arguments
if ($LASTEXITCODE -ne 0) { throw "tar.exe failed ($LASTEXITCODE): $($Arguments -join ' ')" }
}
function Copy-LibrariesFromDataTar([string]$DataTar, [string]$ExtractRoot, [string]$ExcludePattern = '') {
$tar = if (Get-Command tar.exe -ErrorAction SilentlyContinue) { 'tar.exe' } else { 'tar' }
$entries = & $tar -tf $DataTar
if ($LASTEXITCODE -ne 0) { throw "Could not list $DataTar" }
foreach ($entry in $entries) {
$normalized = $entry -replace '^\./', ''
if ($normalized -notmatch '^(?:usr/)?lib/.+\.so\.\d+(?:\.\d+)+$') { continue }
$leaf = [IO.Path]::GetFileName($normalized)
if ($ExcludePattern -and $leaf -match $ExcludePattern) { continue }
$soname = [regex]::Match($leaf, '^(.+\.so\.\d+)')
if (-not $soname.Success) { continue }
Invoke-Tar @('-xf', $DataTar, '-C', $ExtractRoot, $entry)
$source = Join-Path $ExtractRoot ($normalized -replace '/', [IO.Path]::DirectorySeparatorChar)
Copy-Item -LiteralPath $source -Destination (Join-Path $Destination $soname.Groups[1].Value) -Force
}
}
function Copy-LibrariesFromTree([string]$ExtractRoot) {
foreach ($source in Get-ChildItem -LiteralPath $ExtractRoot -Recurse -File) {
if ($source.DirectoryName -notmatch '[\\/]usr[\\/]lib([\\/]|$)') { continue }
$soname = [regex]::Match($source.Name, '^(.+\.so\.\d+)\.\d+')
if ($soname.Success) {
Copy-Item -LiteralPath $source.FullName -Destination (Join-Path $Destination $soname.Groups[1].Value) -Force
}
}
}
function Get-DebLibrary([string]$IndexUrl, [string]$PackagePattern, [string]$Name, [string]$ExcludePattern = '') {
$index = (Invoke-WebRequest -Uri $IndexUrl -UseBasicParsing).Content
$links = [regex]::Matches($index, 'href="([^"]+\.deb)"') | ForEach-Object { $_.Groups[1].Value -replace '^\./', '' }
$file = $links | Where-Object { $_ -match $PackagePattern } | Sort-Object -Descending | Select-Object -First 1
if (-not $file) { throw "Could not find $Name ($arch) in $IndexUrl" }
$packagePath = Join-Path $cache $file
if (-not (Test-Path -LiteralPath $packagePath)) {
Invoke-WebRequest -Uri "$IndexUrl$file" -OutFile $packagePath -UseBasicParsing
}
$unpack = Join-Path $cache ([IO.Path]::GetFileNameWithoutExtension($file))
$null = New-Item -ItemType Directory -Force -Path $unpack
if (Get-Command ar -ErrorAction SilentlyContinue) {
Push-Location $unpack
try {
& ar x $packagePath
if ($LASTEXITCODE -ne 0) { throw "Could not unpack Debian package $file" }
}
finally { Pop-Location }
}
else {
Invoke-Tar @('-xf', $packagePath, '-C', $unpack)
}
$dataTar = Get-ChildItem -LiteralPath $unpack -Filter 'data.tar.*' | Select-Object -First 1
if (-not $dataTar) { throw "Debian package $file has no data archive" }
Copy-LibrariesFromDataTar $dataTar.FullName $unpack $ExcludePattern
}
function Get-ApkLibrary([string]$Repository, [string]$PackageName) {
$apkArch = if ($arch -eq 'amd64') { 'x86_64' } else { 'aarch64' }
$base = "https://dl-cdn.alpinelinux.org/alpine/v3.22/$Repository/$apkArch"
$indexPath = Join-Path $cache "$Repository-APKINDEX.tar.gz"
if (-not (Test-Path -LiteralPath $indexPath) -or (Get-Item -LiteralPath $indexPath).LastWriteTimeUtc -lt [DateTime]::UtcNow.AddHours(-24)) {
Invoke-WebRequest -Uri "$base/APKINDEX.tar.gz" -OutFile $indexPath -UseBasicParsing
}
$indexDirectory = Join-Path $cache "$Repository-index"
$null = New-Item -ItemType Directory -Force -Path $indexDirectory
Invoke-Tar @('-xzf', $indexPath, '-C', $indexDirectory)
$indexFile = Get-ChildItem -LiteralPath $indexDirectory -File | Where-Object Name -Like 'APKINDEX*' | Select-Object -First 1 -ExpandProperty FullName
if (-not $indexFile) { throw "Alpine index archive has no APKINDEX: $indexPath" }
$records = (Get-Content -LiteralPath $indexFile -Raw) -split "`n`n"
$record = $records | Where-Object { $_ -match "(?m)^P:$([regex]::Escape($PackageName))\r?$" } | Select-Object -First 1
if (-not $record) { throw "Could not find Alpine package $PackageName in $Repository/$apkArch" }
$version = [regex]::Match($record, '(?m)^V:(.+)$').Groups[1].Value.Trim()
$packageFile = "$PackageName-$version.apk"
if (-not $version -or -not $packageFile) { throw "Incomplete APKINDEX record for $PackageName" }
$apkPath = Join-Path $cache $packageFile
if (-not (Test-Path -LiteralPath $apkPath)) { Invoke-WebRequest -Uri "$base/$packageFile" -OutFile $apkPath -UseBasicParsing }
$unpack = Join-Path $cache "$PackageName-$version"
$null = New-Item -ItemType Directory -Force -Path $unpack
Invoke-Tar @('-xzf', $apkPath, '-C', $unpack)
Copy-LibrariesFromTree $unpack
}
if ($RuntimeIdentifier.StartsWith('linux-musl-')) {
Get-ApkLibrary 'community' 'libmsquic'
Get-ApkLibrary 'main' 'numactl'
Get-ApkLibrary 'main' 'lttng-ust'
Get-ApkLibrary 'main' 'zlib'
}
else {
# Keep these Debian 12 builds: selecting the newest package in the shared
# Debian pool can silently raise the minimum glibc above Bookworm's 2.36.
Get-DebLibrary 'https://deb.debian.org/debian/pool/main/n/numactl/' "^libnuma1_2\.0\.16-1_$arch\.deb$" 'libnuma'
Get-DebLibrary 'https://deb.debian.org/debian/pool/main/libz/libzstd/' "^libzstd1_1\.5\.4\+dfsg2-5_$arch\.deb$" 'libzstd'
Get-DebLibrary 'https://deb.debian.org/debian/pool/main/z/zlib/' "^zlib1g_1\.2\.13\.dfsg-1_$arch\.deb$" 'zlib'
# MsQuic's LTTng plugin is optional and is not loaded by the node runtime.
Get-DebLibrary 'https://packages.microsoft.com/debian/12/prod/pool/main/libm/libmsquic/' "^libmsquic_\d+(?:\.\d+){1,3}_$arch\.deb$" 'MsQuic' '^libmsquic\.lttng\.so\.'
}
if (-not (Test-Path -LiteralPath (Join-Path $Destination 'libmsquic.so.2'))) {
throw "Bundled runtime is missing libmsquic.so.2 for $RuntimeIdentifier"
}
if (-not (Test-Path -LiteralPath (Join-Path $Destination 'libnuma.so.1'))) {
throw "Bundled runtime is missing libnuma.so.1 for $RuntimeIdentifier"
}
if (-not (Test-Path -LiteralPath (Join-Path $Destination 'libz.so.1'))) {
throw "Bundled runtime is missing libz.so.1 for $RuntimeIdentifier"
}
Get-ChildItem -LiteralPath $Destination -Filter '*.so.*' | ForEach-Object { Write-Output "bundled $($_.Name) for $RuntimeIdentifier" }
+10
View File
@@ -57,11 +57,21 @@ try {
}
}
$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', '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
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
+2 -1
View File
@@ -64,9 +64,10 @@ build_arch_image() {
curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \
"$gitea_base/api/packages/justme/generic/moshiro-node/$version/$filename" -o "$archive"
mkdir -p "$context/app" "$context/config"
tar -xzf "$archive" -C "$work" "moshiro-$component" fleet-enroll
tar -xzf "$archive" -C "$work"
install -m 0755 "$work/fleet-enroll" "$context/app/fleet-enroll"
install -m 0755 "$work/moshiro-$component" "$context/app/moshiro-$component"
install -m 0644 "$work"/lib*.so.* "$context/app/"
: > "$context/config/.keep"
install -m 0755 "$root/packaging/container/entrypoint.sh" "$context/entrypoint.sh"