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
267 lines
13 KiB
YAML
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."
|