ModernUO mandated -dev packages on production servers for one reason:
DllImport never asks for a versioned SONAME, so libdeflate.so.0 and
libargon2.so.1 sitting in /usr/lib went unfound and the -dev package's
unversioned symlink was the only thing making resolution work. That is
fixed in the binding packages, so pick them up and stop asking for
build tooling on machines that compile nothing.
LibDeflate.Bindings 1.0.3 -> 1.0.4
Argon2.Bindings 1.17.0 -> 1.19.0
Also drops zstd, on every platform including macOS. ZstdNet bundles
libzstd for linux-x64, linux-arm64, osx-x64, osx-arm64 and win, and
nothing shells out to the CLI. Verified: the 15 ManagedArchive
round-trip tests pass in a container with no zstd package installed.
NativeLibraryChecker now asks whether the loader can find each library
rather than whether a named package is installed. Package-name checks
are what forced -dev in the first place, and no hardcoded name works
for ICU anyway: its apt package is release-specific (libicu74 on 24.04,
libicu76 on Alpine, libicu77 on Fedora). ldconfig -p is version
agnostic and is the loader's own cache, so one code path replaces the
apt/dnf/generic split.
ldconfig is treated as a positive signal only. musl's exits 0 while
producing no usable cache, so trusting a negative reported every
library missing on Alpine when all were installed; anything it does not
vouch for is now dlopen'd before being called missing. That fallback
bounds the .so.N probe per library, because a single small bound
reports ICU missing when it is present.
CI installs runtime packages only, deliberately: installing -dev there
would restore the unversioned symlink and mask the very resolution this
depends on, so a regression would sail through.
Adds --check-prereqs. The interactive flow was the only path that ran
these checks, so there was no way to verify a deployment target from a
script or a container.
Audited the rest of the codebase for the same hazard. ModernUO's only
other native imports are ws2_32.dll, which is always present on
Windows, and libc in SocketHelper. libc is not affected even though
libc.so is a libc6-dev linker script: DllImport("libc") was verified to
resolve and call successfully on both glibc and musl with no -dev
package installed. Nothing else P/Invokes, and no code here registers a
DllImportResolver.
Verified with 1.0.4 and 1.19.0: build plus 810 Server.Tests and 642
UOContent.Tests, and --check-prereqs reporting correctly on Debian,
Ubuntu, Fedora and Alpine with only the runtime packages installed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
160 lines
5.7 KiB
YAML
160 lines
5.7 KiB
YAML
name: Build
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- '.config/dotnet-tools.json'
|
|
- 'Projects/**'
|
|
- 'Directory.Build.props'
|
|
- 'global.json'
|
|
- 'version.json'
|
|
- '*.slnx'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- '.config/dotnet-tools.json'
|
|
- 'Projects/**'
|
|
- 'Directory.Build.props'
|
|
- 'global.json'
|
|
- 'version.json'
|
|
- '*.slnx'
|
|
|
|
jobs:
|
|
build-macos:
|
|
runs-on: ${{ matrix.os }}
|
|
# A hung run otherwise bills the full 360-minute default before GitHub kills it.
|
|
timeout-minutes: 30
|
|
name: Build (${{ matrix.name }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: macos-15
|
|
name: MacOS 15
|
|
- os: macos-26
|
|
name: MacOS 26
|
|
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
|
|
- name: Install .NET
|
|
uses: actions/setup-dotnet@v6
|
|
with:
|
|
global-json-file: global.json
|
|
- name: Install Prerequisites
|
|
run: |
|
|
brew update
|
|
brew install icu4c libdeflate argon2
|
|
- name: Set Library Path
|
|
run: echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
|
|
- name: Build
|
|
run: dotnet run --project Projects/BuildTool -- --config Release --skip-prereqs
|
|
- name: Migration Changes
|
|
run: git diff --exit-code ./**/Migrations/*.v*.json
|
|
- name: Test
|
|
# blame-hang kills a stuck test host after 10 minutes and reports the in-flight
|
|
# tests plus a process dump instead of hanging until the job timeout.
|
|
run: |
|
|
dotnet test --logger trx --results-directory ./TestResults --blame-hang --blame-hang-timeout 10m --blame-hang-dump-type full
|
|
if [ -z "$(find ./TestResults -name '*.trx' 2>/dev/null)" ]; then
|
|
echo "::error::No test result files were produced - no test projects ran. Failing to avoid masking failures."
|
|
exit 1
|
|
fi
|
|
- name: Upload test results on failure
|
|
if: failure()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: TestResults-${{ matrix.name }}
|
|
path: ./TestResults
|
|
if-no-files-found: ignore
|
|
|
|
build-linux:
|
|
runs-on: ubuntu-latest
|
|
# A hung run otherwise bills the full 360-minute default before GitHub kills it.
|
|
timeout-minutes: 30
|
|
container:
|
|
image: ${{ matrix.container }}
|
|
options: --security-opt seccomp=unconfined
|
|
name: Build (${{ matrix.name }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- container: ubuntu:26.04
|
|
name: Ubuntu 26
|
|
packageManager: apt
|
|
- container: ubuntu:noble
|
|
name: Ubuntu 24
|
|
packageManager: apt
|
|
- container: ubuntu:jammy
|
|
name: Ubuntu 22
|
|
packageManager: apt
|
|
- container: debian:trixie
|
|
name: Debian 13
|
|
packageManager: apt
|
|
- container: debian:bookworm
|
|
name: Debian 12
|
|
packageManager: apt
|
|
- container: fedora:44
|
|
name: Fedora 44
|
|
packageManager: dnf
|
|
- container: quay.io/centos/centos:stream9
|
|
name: CentOS 9 Stream
|
|
packageManager: dnf
|
|
epel: true
|
|
- container: quay.io/centos/centos:stream10
|
|
name: CentOS 10 Stream
|
|
packageManager: dnf
|
|
epel: true
|
|
- container: almalinux:10
|
|
name: AlmaLinux 10
|
|
packageManager: dnf
|
|
epel: true
|
|
|
|
steps:
|
|
# Enable CRB before EPEL, per the EPEL quickstart. epel-next is not installed:
|
|
# none of the prerequisites need it, EPEL 10 does not have it, and it is one more
|
|
# mirrorlist to fetch.
|
|
- name: Enable EPEL and CRB
|
|
run: |
|
|
dnf upgrade --refresh -y
|
|
dnf install -y dnf-plugins-core
|
|
dnf config-manager --set-enabled crb
|
|
dnf install -y epel-release
|
|
if: ${{ matrix.epel }}
|
|
# Runtime packages only, deliberately. Installing the -dev packages here would add the
|
|
# unversioned .so symlink and mask the very thing the binding packages now probe for, so a
|
|
# regression in versioned-SONAME resolution would sail through CI.
|
|
- name: Install Prerequisites using dnf
|
|
run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate libargon2
|
|
if: ${{ matrix.packageManager == 'dnf' }}
|
|
- name: Install Prerequisites using apt
|
|
run: apt-get update -y && apt-get install -y curl libicu-dev libdeflate0 libargon2-1 tzdata
|
|
if: ${{ matrix.packageManager == 'apt' }}
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
|
|
- name: Install .NET
|
|
uses: actions/setup-dotnet@v6
|
|
with:
|
|
global-json-file: global.json
|
|
- name: Build
|
|
run: dotnet run --project Projects/BuildTool -- --config Release --skip-prereqs
|
|
- name: Test
|
|
# blame-hang kills a stuck test host after 10 minutes and reports the in-flight
|
|
# tests plus a process dump instead of hanging until the job timeout.
|
|
run: |
|
|
dotnet test --logger trx --results-directory ./TestResults --blame-hang --blame-hang-timeout 10m --blame-hang-dump-type full
|
|
if [ -z "$(find ./TestResults -name '*.trx' 2>/dev/null)" ]; then
|
|
echo "::error::No test result files were produced - no test projects ran. Failing to avoid masking failures."
|
|
exit 1
|
|
fi
|
|
- name: Upload test results on failure
|
|
if: failure()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: TestResults-${{ matrix.name }}
|
|
path: ./TestResults
|
|
if-no-files-found: ignore
|