From ff037fe1fd40b973d2429a864448d932bfa3eb1c Mon Sep 17 00:00:00 2001 From: zignd Date: Sat, 24 Oct 2026 18:40:11 -0300 Subject: [PATCH 1/3] ci: always run the Windows legs on windows-latest The two Windows legs were a placeholder under TRACE_CI_RUNNERS=self-hosted, so nothing exercised the WindowsDX catalog smoke, the FNA video smoke or the Windows render parity while the fleet handled the rest. No Windows machine is part of the local fleet, and this repository is public, where standard GitHub-hosted runners (Windows included) do not consume billed minutes, so the legs can run for real there. The matrix now always carries windows-latest; the flag still selects the self-hosted labels for the Linux and macOS legs, and windows-latest maps to itself either way. Job names are unchanged, so the required checks still resolve. --- .github/workflows/ci.yml | 36 +++++++++++++++------------------- docs/continuous-integration.md | 2 +- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 516c7c8d..43fcdc5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,33 +41,29 @@ jobs: - name: Verify deterministic default theme icons run: dotnet run --project tools/Forma.IconPipeline/Forma.IconPipeline.csproj -- verify - # The Windows legs cannot run on the local fleet (no Windows runner exists here), but the branch ruleset requires those two - # contexts to succeed. Under the flag they are reported as successful with this placeholder instead of being left pending. - windows-legs-not-run: - name: ${{ matrix.runtime }} / windows-latest - if: vars.TRACE_CI_RUNNERS == 'self-hosted' - runs-on: ${{ vars.TRACE_CI_RUNNERS == 'self-hosted' && fromJSON('["self-hosted","Linux","X64"]') || 'ubuntu-latest' }} - strategy: - fail-fast: false - matrix: - runtime: [MonoGame, FNA] - steps: - - name: Report that the Windows leg did not run - run: echo "No Windows runner exists on the local machine; the ${{ matrix.runtime }} Windows leg is skipped under TRACE_CI_RUNNERS=self-hosted." - runtime: needs: lint name: ${{ matrix.runtime }} / ${{ matrix.os }} strategy: fail-fast: false matrix: - # Windows legs are skipped under TRACE_CI_RUNNERS=self-hosted: no Windows runner exists on the local machine. - os: ${{ fromJSON(vars.TRACE_CI_RUNNERS == 'self-hosted' && '["ubuntu-latest","macos-latest"]' || '["ubuntu-latest","windows-latest","macos-latest"]') }} runtime: [MonoGame, FNA] - # Under TRACE_CI_RUNNERS=self-hosted (docs/self-hosted-runners.md in Trace Arena) Linux legs run on the local x64 runner and - # macOS legs on the local macOS runner. The Linux steps that render through the Vulkan software renderer are skipped there: it - # crashes under x64 emulation ("rosetta error: mmap_anonymous_rw mmap failed"), and the native arm64 runner lacks the freetype native library. - runs-on: ${{ vars.TRACE_CI_RUNNERS == 'self-hosted' && (matrix.os == 'macos-latest' && fromJSON('["self-hosted","macOS","ARM64"]') || fromJSON('["self-hosted","Linux","X64","yiazmat-linux-x64-native"]')) || matrix.os }} + # The Windows legs always run on GitHub-hosted windows-latest: no Windows machine is part of the local fleet, and Forma + # is a public repository, where standard runners (Windows included) do not consume billed minutes. + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + selfHosted: '["self-hosted","Linux","X64","yiazmat-linux-x64-native"]' + - os: macos-latest + selfHosted: '["self-hosted","macOS","ARM64"]' + - os: windows-latest + selfHosted: '["windows-latest"]' + # Under TRACE_CI_RUNNERS=self-hosted (docs/self-hosted-runners.md in Trace Arena) the Linux legs run on the local native x64 + # runner and the macOS legs on the local macOS runner. The Windows legs stay on windows-latest either way (see the matrix). + # The Linux steps that render through the Vulkan software renderer are skipped on the self-hosted Linux runner: it crashes + # under x64 emulation ("rosetta error: mmap_anonymous_rw mmap failed"), and the native arm64 runner lacks the freetype native + # library. + runs-on: ${{ vars.TRACE_CI_RUNNERS == 'self-hosted' && fromJSON(matrix.selfHosted) || matrix.os }} env: FORMA_RUNTIME: ${{ matrix.runtime }} steps: diff --git a/docs/continuous-integration.md b/docs/continuous-integration.md index 182e4bda..d3de91d4 100644 --- a/docs/continuous-integration.md +++ b/docs/continuous-integration.md @@ -13,7 +13,7 @@ supported configuration for forks. | `parity` | `self-hosted, Linux, X64, yiazmat-linux-x64-emulated` | Sequential runtime builds; the step is wrapped in `scripts/retry.sh` because a transient `GenerateDepsFile` file lock can survive one build boundary. | | `runtime` (`ubuntu-latest` legs) | `self-hosted, Linux, X64, yiazmat-linux-x64-native` | Runs on the native x64 Linux server, where the Vulkan software renderer works. | | `runtime` (`macos-latest` legs) | `self-hosted, macOS, ARM64` | Native arm64 macOS. | -| `runtime` (`windows-latest` legs) | placeholder job | No Windows machine is part of the fleet; the job reports the leg as not run so the required checks still resolve. | +| `runtime` (`windows-latest` legs) | `windows-latest` | Always GitHub-hosted: no Windows machine is part of the fleet, and the repository is public, so these minutes are not billed. | | `thorvg-linux` | `self-hosted, Linux, X64, yiazmat-linux-x64-native` | Builds the ThorVG adapter and runs its tests inside the slot's own Docker daemon. | | `thorvg-macos`, `nativeaot` | `self-hosted, macOS, ARM64` | Native arm64 macOS. | | `package-consumers` | `self-hosted, Linux, X64, yiazmat-linux-x64-native` | Builds and inspects the shipped packages. | From 14527ef2818ac640ea4d20f7e34c5e98cbf19f60 Mon Sep 17 00:00:00 2001 From: zignd Date: Sat, 24 Oct 2026 19:32:14 -0300 Subject: [PATCH 2/3] fix(ci): wait for the X server before running an X program The Linux legs started failing with XCB failed to connect to the X server due to error:1 ERROR at ./vulkaninfo/./vulkaninfo.h:737: AppCreateXcbSurface failed to establish connection on the very first X program of the job. xvfb-run -a hands the display number to the payload as soon as Xvfb creates its socket, and Xvfb 2:21.1.12-1ubuntu1.9 still needs a moment before it listens, so an immediate client gets ECONNREFUSED. A fresh container on the server reproduces it: 4 of 5 plain xvfb-run runs failed, 0 of 5 with a readiness wait. scripts/xvfb-run.sh wraps xvfb-run with that wait (xdpyinfo, from x11-utils) and every call site now uses it. This is an environment change (the xvfb package is installed at job time), not a code one: it would have hit main on the next Linux job regardless of this branch. --- .github/workflows/ci.yml | 32 ++++++++++----------- .github/workflows/release.yml | 4 +-- docs/continuous-integration.md | 7 +++++ scripts/check-runtime-svg-linux.sh | 8 +++--- scripts/xvfb-run.sh | 46 ++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 22 deletions(-) create mode 100755 scripts/xvfb-run.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43fcdc5f..462dec5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 mesa-vulkan-drivers vulkan-tools xvfb + sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 mesa-vulkan-drivers vulkan-tools xvfb x11-utils software_icd=$(find /opt/google/chrome -name 'vk_swiftshader_icd.json' -print -quit 2>/dev/null || true) software_icd=${software_icd:-$(find /usr/share/vulkan/icd.d -name 'lvp_icd*.json' -print -quit)} test -n "$software_icd" @@ -121,24 +121,24 @@ jobs: FNA3D_OPENGL_WINDOW_DEPTHSTENCILFORMAT: None run: | if [[ "$FORMA_RUNTIME" == "FNA" ]]; then - env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES SvgBackend=Skia FormaRuntime=FNA xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh - env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-quick-start.sh - env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' dotnet test tests/Forma.RenderTests/Forma.RenderTests.csproj --configuration Release -p:FormaRuntime=FNA --no-build --nologo - env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=FNA -p:SvgBackend=Skia + env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES SvgBackend=Skia FormaRuntime=FNA bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh + env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-quick-start.sh + env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' dotnet test tests/Forma.RenderTests/Forma.RenderTests.csproj --configuration Release -p:FormaRuntime=FNA --no-build --nologo + env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=FNA -p:SvgBackend=Skia else - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' vulkaninfo --summary - SvgBackend=Skia FormaRuntime=MonoGame xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-quick-start.sh - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' dotnet test tests/Forma.RenderTests/Forma.RenderTests.csproj --configuration Release -p:FormaRuntime=MonoGame --no-build --nologo - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=MonoGame -p:SvgBackend=Skia + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' vulkaninfo --summary + SvgBackend=Skia FormaRuntime=MonoGame bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-quick-start.sh + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' dotnet test tests/Forma.RenderTests/Forma.RenderTests.csproj --configuration Release -p:FormaRuntime=MonoGame --no-build --nologo + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=MonoGame -p:SvgBackend=Skia # MonoGame's DesktopVK backend aborts on llvmpipe over Xvfb on the self-hosted fleet, in both the catalog smoke and the # swapchain smoke: "Fatal : VkResult is c4653214 in vulkan/MGG_Vulkan.cpp:2226" followed by SIGSEGV (the backend prints # an uninitialized VkResult). A 1 GB /dev/shm did not change it, so both Vulkan steps stay on hosted CI. Everything # else of this leg (DesktopGL catalog smoke, quick start, render tests, SVG smoke) runs on the fleet, and the Vulkan # coverage there is `thorvg-linux`, which builds the ThorVG adapter and runs its tests on the native x64 server. if [[ -z "${FORMA_SKIP_VULKAN_SMOKE:-}" ]]; then - SvgBackend=Skia MonoGamePlatform=DesktopVK FormaNativeRuntime=true CatalogBackend=Vulkan xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh - MonoGamePlatform=DesktopVK FormaNativeRuntime=true xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=MonoGame -p:MonoGamePlatform=DesktopVK -p:FormaNativeRuntime=true -p:SvgBackend=Skia + SvgBackend=Skia MonoGamePlatform=DesktopVK FormaNativeRuntime=true CatalogBackend=Vulkan bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-smoke.sh + MonoGamePlatform=DesktopVK FormaNativeRuntime=true bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj --configuration Release -p:FormaRuntime=MonoGame -p:MonoGamePlatform=DesktopVK -p:FormaNativeRuntime=true -p:SvgBackend=Skia else echo "Skipped on the self-hosted fleet: MonoGame DesktopVK crashes in vulkan/MGG_Vulkan.cpp:2226 on llvmpipe over Xvfb." fi @@ -152,7 +152,7 @@ jobs: SDL_AUDIODRIVER: dummy FNA3D_FORCE_DRIVER: OpenGL FNA3D_OPENGL_WINDOW_DEPTHSTENCILFORMAT: None - run: env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-fna-video-smoke.sh + run: env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-fna-video-smoke.sh - name: Compare Linux peer rendering if: runner.os == 'Linux' && matrix.runtime == 'MonoGame' shell: bash @@ -161,7 +161,7 @@ jobs: SDL_VIDEODRIVER: x11 FormaCatalogViewportWidth: 800 FormaCatalogViewportHeight: 480 - run: SvgBackend=Skia bash scripts/retry.sh xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-render-parity.sh + run: SvgBackend=Skia bash scripts/retry.sh bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/check-catalog-render-parity.sh - name: Validate Windows selected runtime if: runner.os == 'Windows' shell: bash @@ -300,7 +300,7 @@ jobs: - name: Install Linux graphics dependencies run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 mesa-vulkan-drivers xvfb + sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 mesa-vulkan-drivers xvfb x11-utils software_icd=$(find /opt/google/chrome -name 'vk_swiftshader_icd.json' -print -quit 2>/dev/null || true) software_icd=${software_icd:-$(find /usr/share/vulkan/icd.d -name 'lvp_icd*.json' -print -quit)} test -n "$software_icd" @@ -313,7 +313,7 @@ jobs: # The committed font bytes belong to the GitHub-hosted image's font stack (on native x64 Linux they already differ # at the size field), so the fleet skips that one comparison and hosted CI keeps it. FORMA_SKIP_FONT_XNB_CMP: ${{ vars.TRACE_CI_RUNNERS == 'self-hosted' && '1' || '' }} - run: bash scripts/retry.sh env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/test-package-consumer.sh + run: bash scripts/retry.sh env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/test-package-consumer.sh - uses: actions/upload-artifact@v7 with: name: forma-peer-packages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5afa9b5d..19a945cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: - name: Install Linux graphics dependencies run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 xvfb + sudo apt-get install -y --no-install-recommends libegl1 libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 xvfb x11-utils - name: Validate release tag if: startsWith(github.ref, 'refs/tags/') shell: bash @@ -89,7 +89,7 @@ jobs: env: LIBGL_ALWAYS_SOFTWARE: 1 SDL_VIDEODRIVER: x11 - run: xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' bash scripts/test-package-consumer.sh + run: bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' bash scripts/test-package-consumer.sh - name: Validate SVG package consumers run: bash scripts/test-svg-package-consumers.sh - name: Pack approved release manifest diff --git a/docs/continuous-integration.md b/docs/continuous-integration.md index d3de91d4..bf943f77 100644 --- a/docs/continuous-integration.md +++ b/docs/continuous-integration.md @@ -40,6 +40,13 @@ These are deliberate, documented limits — not silent skips: The emulated x64 pool runs at most two jobs at a time: the native x64 server carries the heavier Linux legs, and two emulated containers keep the host's Docker VM comfortable. +Every X program runs through `scripts/xvfb-run.sh`, not `xvfb-run` directly: `xvfb-run -a` hands the +display number to the payload as soon as Xvfb creates its socket, and Xvfb `2:21.1.12-1ubuntu1.9` +still needs a moment before it listens, so a client that connects immediately dies with +`XCB failed to connect to the X server due to error:1`. The wrapper waits for `xdpyinfo` to succeed +before starting the payload (4 of 5 plain `xvfb-run` runs failed in a fresh container on the server; +0 of 5 with the wrapper). + Every job exports `MSBUILDDISABLENODEREUSE=1`, `DOTNET_CLI_DO_NOT_USE_MSBUILD_SERVER=1` and `DOTNET_PROCESSOR_COUNT=2`: reused MSBuild nodes have held `*.deps.json` files across builds and broken the next `GenerateDepsFile`. Runs are serialized per working directory by the ephemeral diff --git a/scripts/check-runtime-svg-linux.sh b/scripts/check-runtime-svg-linux.sh index 0fd7eb34..17d5abd2 100755 --- a/scripts/check-runtime-svg-linux.sh +++ b/scripts/check-runtime-svg-linux.sh @@ -9,7 +9,7 @@ cd "$repository_root" if [[ "${FORMA_SKIP_APT:-0}" != "1" ]] && command -v apt-get >/dev/null 2>&1 && [[ "$(id -u)" == "0" ]]; then apt-get update - apt-get install -y --no-install-recommends libegl1 libfreetype6-dev libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 libharfbuzz-dev mesa-vulkan-drivers vulkan-tools xvfb xauth + apt-get install -y --no-install-recommends libegl1 libfreetype6-dev libgl1 libgl1-mesa-dri libglx0 libglx-mesa0 libharfbuzz-dev mesa-vulkan-drivers vulkan-tools xvfb x11-utils xauth fi software_icd="$(find /usr/share/vulkan/icd.d -name 'lvp_icd*.json' -print -quit)" @@ -22,19 +22,19 @@ for runtime_name in MonoGame FNA; do -p:FormaRuntime="$runtime_name" --filter 'FullyQualifiedName~Svg' --nologo done -SDL_VIDEODRIVER=x11 xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' \ +SDL_VIDEODRIVER=x11 bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' \ dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj \ --configuration Release -p:FormaRuntime=MonoGame env -u VK_ICD_FILENAMES -u VK_DRIVER_FILES SDL_VIDEODRIVER=offscreen FNA3D_FORCE_DRIVER=OpenGL \ - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' \ + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' \ dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj \ --configuration Release -p:FormaRuntime=FNA VK_ICD_FILENAMES="$software_icd" VK_DRIVER_FILES="$software_icd" vulkaninfo --summary VK_ICD_FILENAMES="$software_icd" VK_DRIVER_FILES="$software_icd" SDL_VIDEODRIVER=x11 \ MonoGamePlatform=DesktopVK FormaNativeRuntime=true \ - xvfb-run -a -s '-screen 0 1440x900x24 +extension GLX' \ + bash scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' \ dotnet run --project tests/Forma.SvgRenderSmoke/Forma.SvgRenderSmoke.csproj \ --configuration Release -p:FormaRuntime=MonoGame -p:MonoGamePlatform=DesktopVK -p:FormaNativeRuntime=true diff --git a/scripts/xvfb-run.sh b/scripts/xvfb-run.sh new file mode 100755 index 00000000..cb43e710 --- /dev/null +++ b/scripts/xvfb-run.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Runs a command under a fresh X server, and returns only once the server accepts connections. +# +# `xvfb-run -a` hands the display number to the payload as soon as Xvfb creates its socket; Xvfb 2:21.1.12-1ubuntu1.9 then still needs a +# moment before it listens, so a client that connects immediately dies with +# XCB failed to connect to the X server due to error:1 +# (vulkaninfo, and SDL/GL programs in general). Waiting for a successful X query before starting the payload removes the race. +# Usage mirrors the part of xvfb-run this repository uses: +# +# scripts/xvfb-run.sh -s '-screen 0 1440x900x24 +extension GLX' COMMAND [ARGS...] +set -euo pipefail + +server_args="" +if [ "${1:-}" = "-s" ]; then + server_args=${2:?xvfb-run.sh: -s needs a value} + shift 2 +fi +if [ $# -eq 0 ]; then + echo "usage: xvfb-run.sh [-s SERVER_ARGS] COMMAND [ARGS...]" >&2 + exit 2 +fi +if ! command -v xdpyinfo >/dev/null 2>&1; then + echo "xvfb-run.sh: xdpyinfo is required (package x11-utils)" >&2 + exit 1 +fi + +# Xvfb starts in milliseconds on an idle machine and in a second or two under load; 15 s is the "something is actually wrong" limit. +wait_seconds=${XVFB_WAIT_SECONDS:-15} +args=(xvfb-run -a) +if [ -n "$server_args" ]; then + args+=(-s "$server_args") +fi + +exec "${args[@]}" bash -c ' + wait=$1 + shift + deadline=$((SECONDS + wait)) + while ! xdpyinfo >/dev/null 2>&1; do + if [ "$SECONDS" -ge "$deadline" ]; then + echo "xvfb-run.sh: the X server on ${DISPLAY:-?} did not accept connections within ${wait}s" >&2 + exit 1 + fi + sleep 0.1 + done + exec "$@" +' _ "$wait_seconds" "$@" From a1a8c864db10248daffe84a59e95433ecc046852 Mon Sep 17 00:00:00 2001 From: zignd Date: Sat, 24 Oct 2026 20:21:41 -0300 Subject: [PATCH 3/3] docs: add xdpyinfo to the project dictionary The Xvfb readiness wrapper documents the probe it uses, and cspell flags the name. --- config/cspell-project-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/config/cspell-project-words.txt b/config/cspell-project-words.txt index 71c26ce2..4914f204 100644 --- a/config/cspell-project-words.txt +++ b/config/cspell-project-words.txt @@ -115,3 +115,4 @@ premake SIGSEGV swapchain yiazmat +xdpyinfo