Files
trycua--cua/.github/workflows/ci-distro-compat-cua-driver.yml
T
wehub-resource-sync 91e75e620b
CI: cua-driver distro-compat matrix / Resolve release version (push) Waiting to run
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / Distro compat summary (push) Blocked by required conditions
CI: Nix Linux Rust source / Nix / compositor build (push) Waiting to run
CI: Nix Linux Rust source / Nix / driver package (push) Waiting to run
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Waiting to run
CI: Rust Linux unit / Rust Linux unit and compile (push) Waiting to run
CI: Rust Windows unit / Rust Windows unit and compile (push) Waiting to run
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Waiting to run
CD: Docs MCP Server / build (linux/amd64) (push) Waiting to run
CD: Docs MCP Server / build (linux/arm64) (push) Waiting to run
CD: Docs MCP Server / merge (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:03:19 +08:00

267 lines
13 KiB
YAML

name: "CI: cua-driver distro-compat matrix"
# Smoke-tests the *released* cua-driver binary inside real distro containers
# to catch bugs that the NixOS test suite cannot: glibc ABI floor issues and
# distro-packaging gaps (e.g. Qt5 AT-SPI bridge absent on Ubuntu).
#
# What this catches (the NixOS suite CANNOT catch):
# 1. glibc ABI floor — the released binary is built in a debian:11
# container (glibc 2.31) so it should run on all of the matrix distros.
# If someone accidentally bumps the build container to a newer distro the
# --version smoke-test below will fail on the older glibc distros.
# 2. Runtime library gaps — `doctor` queries the OS for required capabilities
# (X11, AT-SPI, etc.). If a distro is missing a package the doctor command
# exits non-zero and prints a human-readable error.
#
# Design principles:
# - Run the RELEASED binary (downloaded from GitHub Releases), not a freshly
# built one. This is the only way to catch ABI mismatches because the NixOS
# CI builds its own binary against NixOS's own glibc.
# - Keep this fast and cheap: containers + --version/doctor only. Full GUI
# behavior stays in the canonical Rust X11 and Wayland E2E workflows.
# - Non-blocking by default (continue-on-error: true) — the released binary
# may not exist yet on a fresh branch; the job is informational until the
# first linux release tag exists.
#
# Trigger: any PR that touches the Rust cua-driver or this workflow.
# Also runs on push to main and on workflow_dispatch so it works as a
# post-release regression guard.
#
# Companion to the Rust desktop E2E workflows; it validates released binaries,
# not source-built harness behavior.
on:
pull_request:
paths:
- "libs/cua-driver/rust/**"
- ".github/workflows/ci-distro-compat-cua-driver.yml"
push:
# Run on main (path-filtered) AND on release tags so a newly-published
# binary is immediately validated against the distro matrix.
branches: [main]
tags:
- "cua-driver-rs-v*"
paths:
- "libs/cua-driver/rust/**"
- ".github/workflows/ci-distro-compat-cua-driver.yml"
workflow_dispatch:
inputs:
version:
description: "cua-driver-rs version to test (without leading v). Leave blank to auto-detect latest."
required: false
default: ""
permissions:
contents: read
jobs:
# ── Resolve the binary version to test ───────────────────────────────────────
# Fetch the latest published cua-driver-rs release tag so individual matrix
# jobs don't each hit the GitHub API. If workflow_dispatch supplied a version
# we use that instead.
resolve-version:
name: Resolve release version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.pick.outputs.version }}
binary_url: ${{ steps.pick.outputs.binary_url }}
steps:
- name: Pick version
id: pick
env:
GH_TOKEN: ${{ github.token }}
INPUT_VERSION: ${{ inputs.version }}
run: |
if [[ -n "$INPUT_VERSION" ]]; then
VERSION="$INPUT_VERSION"
elif [[ "$GITHUB_REF" == refs/tags/cua-driver-rs-v* ]]; then
VERSION="${GITHUB_REF#refs/tags/cua-driver-rs-v}"
else
# Fetch the latest release that matches the cua-driver-rs-v* pattern.
# The releases are marked prerelease=true so we use /releases instead
# of /releases/latest (which skips pre-releases).
VERSION=$(gh api repos/trycua/cua/releases \
--jq '[.[] | select(.tag_name | startswith("cua-driver-rs-v"))] | first | .tag_name | ltrimstr("cua-driver-rs-v")' \
2>/dev/null || echo "")
fi
if [[ -z "$VERSION" ]]; then
echo "No cua-driver-rs release found yet — this is expected on a fresh branch."
echo "version=none" >> "$GITHUB_OUTPUT"
echo "binary_url=none" >> "$GITHUB_OUTPUT"
else
BINARY_URL="https://github.com/trycua/cua/releases/download/cua-driver-rs-v${VERSION}/cua-driver-rs-${VERSION}-linux-x86_64-binary.tar.gz"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "binary_url=$BINARY_URL" >> "$GITHUB_OUTPUT"
echo "Will test version: $VERSION"
echo "Binary URL: $BINARY_URL"
fi
# ── Per-distro smoke-test matrix ─────────────────────────────────────────────
# Each job runs in a real distro container, downloads the released binary,
# and asserts that:
# 1. The binary executes at all (catches glibc ABI floor regressions).
# 2. `--version` prints a version string.
# 3. `doctor` exits 0 (or exits non-zero with a parseable diagnostic —
# the doctor command reports capabilities, some may be absent in a
# headless container; we treat a clean exit or a known-missing-display
# exit as success for the ABI test).
#
# Why these distros?
# debian:12 — glibc 2.36, representative of Debian stable users
# ubuntu:22.04 — glibc 2.35, Ubuntu LTS most widely deployed
# ubuntu:24.04 — glibc 2.39, also tests Qt5 AT-SPI bridge gap (see CUA-599)
# rockylinux:9 — glibc 2.34, RHEL/Rocky/AlmaLinux users
# fedora:41 — glibc 2.40, leading-edge RPM users
distro-smoke:
name: "${{ matrix.distro }} (glibc ${{ matrix.glibc_version }})"
needs: resolve-version
# Don't block the PR if no release binary exists yet.
continue-on-error: true
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
strategy:
fail-fast: false
matrix:
include:
# Debian family
# X11 runtime libs (libx11-6 libxi6 libxtst6 libxext6) and the
# Wayland client lib (libwayland-client0) are required because
# cua-driver is dynamically linked against the X11 input stack and
# the native Wayland backend (added in #1910). They are the runtime
# counterparts of the build-time deps
# (libx11-dev libxi-dev libxtst-dev libxext-dev libwayland-dev) used
# in the CD workflow. Without them the dynamic linker fails before
# main() and --version exits 127, which the smoke-test correctly
# treats as an ABI error. Installing only curl+ca-certificates is not
# enough.
- distro: "debian:12"
image: "debian:12"
glibc_version: "2.36"
pkg_install: "apt-get update -qq && apt-get install -y --no-install-recommends curl ca-certificates libx11-6 libxi6 libxtst6 libxext6 libwayland-client0"
- distro: "ubuntu:22.04"
image: "ubuntu:22.04"
glibc_version: "2.35"
pkg_install: "apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl ca-certificates libx11-6 libxi6 libxtst6 libxext6 libwayland-client0"
- distro: "ubuntu:24.04"
image: "ubuntu:24.04"
glibc_version: "2.39"
pkg_install: "apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl ca-certificates libx11-6 libxi6 libxtst6 libxext6 libwayland-client0"
# RPM family
# Rocky Linux 9 ships curl-minimal in the base image which conflicts
# with the full curl package. Use --allowerasing to let dnf replace
# curl-minimal with curl, or skip curl and use curl-minimal (already
# present). We use --allowerasing so the install is explicit and
# consistent with what a user would do on a fresh Rocky install.
- distro: "rockylinux:9"
image: "rockylinux:9"
glibc_version: "2.34"
pkg_install: "dnf install -y --setopt=install_weak_deps=False --allowerasing curl ca-certificates libX11 libXi libXtst libXext libwayland-client"
- distro: "fedora:41"
image: "fedora:41"
glibc_version: "2.40"
pkg_install: "dnf install -y --setopt=install_weak_deps=False curl ca-certificates libX11 libXi libXtst libXext libwayland-client"
steps:
- name: Skip if no release binary
if: needs.resolve-version.outputs.version == 'none'
run: |
echo "No cua-driver-rs release binary exists yet. Skipping distro smoke-test."
echo "This is expected on branches before the first release tag."
exit 0
- name: Install runtime deps (curl, ca-certificates, X11 libs)
if: needs.resolve-version.outputs.version != 'none'
run: ${{ matrix.pkg_install }}
- name: Download released binary
if: needs.resolve-version.outputs.version != 'none'
env:
BINARY_URL: ${{ needs.resolve-version.outputs.binary_url }}
run: |
echo "Downloading: $BINARY_URL"
curl -fsSL "$BINARY_URL" -o cua-driver.tar.gz
tar -xzf cua-driver.tar.gz
chmod +x cua-driver
ls -lh cua-driver
- name: Verify glibc floor (ldd)
if: needs.resolve-version.outputs.version != 'none'
run: |
# Print glibc version on this host and the minimum version the binary
# requires. This makes CI logs self-explanatory if the binary fails.
echo "=== Host glibc ==="
ldd --version | head -1 || true
echo "=== Binary glibc requirements ==="
# objdump / readelf may not be installed in minimal containers;
# strings is more universally available.
strings cua-driver | grep -E "^GLIBC_[0-9]" | sort -V | tail -5 || true
echo "=== Binary shared-library dependencies (ldd) ==="
# ldd shows all dynamic deps and flags any missing ones. This is
# purely informational — failures here are diagnosed at smoke-test time.
ldd ./cua-driver 2>&1 || true
- name: Smoke-test --version
if: needs.resolve-version.outputs.version != 'none'
run: |
echo "=== cua-driver --version ==="
# This is the primary ABI-floor gate: if the binary can't even print
# its version the glibc requirement is too high for this distro.
VERSION_OUT=$(./cua-driver --version)
echo "Output: $VERSION_OUT"
# Sanity-check that the output contains a version number.
if ! echo "$VERSION_OUT" | grep -qE "[0-9]+\.[0-9]+\.[0-9]+"; then
echo "ERROR: --version output does not contain a semver string"
exit 1
fi
echo "PASS: --version"
- name: Smoke-test doctor
if: needs.resolve-version.outputs.version != 'none'
run: |
echo "=== cua-driver doctor ==="
# doctor checks for runtime capabilities (display, AT-SPI, etc.).
# In a headless container many capabilities will be absent — that's
# expected and NOT a failure. What we test here is that:
# a. The binary loads and runs the doctor subcommand at all.
# b. It exits with a parseable status (not a SIGILL / glibc symbol
# error which would manifest as exit code 127 or similar).
set +e
./cua-driver doctor 2>&1
EXIT_CODE=$?
set -e
echo "doctor exit code: $EXIT_CODE"
# Exit codes that indicate glibc/ABI failure (command not found / bad ELF):
if [[ $EXIT_CODE -eq 127 || $EXIT_CODE -eq 126 ]]; then
echo "ERROR: cua-driver failed to execute (exit $EXIT_CODE) — likely glibc ABI mismatch"
exit 1
fi
# Treat 0 (all capabilities present) or 1 (capabilities missing but
# doctor ran) as success — both mean the binary loaded correctly.
echo "PASS: doctor ran without ABI error"
# ── Summary job ──────────────────────────────────────────────────────────────
# A single job that other status checks can require. Marks green when all
# distro-smoke jobs pass (or when the binary doesn't exist yet and all
# continue-on-error jobs skipped).
distro-compat-summary:
name: "Distro compat summary"
needs: [resolve-version, distro-smoke]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check results
run: |
echo "resolve-version result: ${{ needs.resolve-version.result }}"
echo "distro-smoke result: ${{ needs.distro-smoke.result }}"
# If resolve-version failed (API error etc.) that's a real failure.
if [[ "${{ needs.resolve-version.result }}" == "failure" ]]; then
echo "ERROR: resolve-version job failed"
exit 1
fi
# distro-smoke is continue-on-error so its result is always
# 'success' even when individual jobs fail. The individual job
# logs are the source of truth; this summary job just gates
# the overall workflow status.
echo "All distro compat checks completed."