Skip to content

docs: Discord discards typed text a second after it lands #73

docs: Discord discards typed text a second after it lands

docs: Discord discards typed text a second after it lands #73

Workflow file for this run

# PR / push CI: correctness, compliance, MSRV, headless, and a cheap build of
# every shipped target so cross-platform breakage is caught per-PR instead of
# at release time.
#
# Design decisions (full rationale in docs/build-and-release.md#ci):
# - Scripts, not inline run-blocks, for anything longer than two lines:
# contributors run scripts/ci-*.sh locally and get the identical result.
# - Swatinem/rust-cache over manual actions/cache: it keys on the lockfile
# AND toolchain AND target list, and prunes stale artifacts, which manual
# cache keys always eventually get wrong.
# - Every cargo invocation is --locked: CI must build what the lockfile
# says, or the lockfile is fiction.
name: ci
on:
push:
branches: [main]
pull_request:
# A superseded push to the same PR cancels the older run; saves the finite
# macOS/Windows runner minutes for the commit that matters.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
# Incremental compilation is a lose on CI: the cache restore already covers
# warm starts, and incremental artifacts bloat the cache by gigabytes.
CARGO_INCREMENTAL: "0"
jobs:
# Lint + test on the two OSes with OS-specific code paths. Linux covers the
# pure crates; macOS covers ax-edit's real FFI. Windows joins this list the
# day a Windows backend exists.
check:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-15]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
# rustup reads rust-toolchain.toml automatically; no toolchain action
# needed for the default case, which is one less pinned dependency.
- name: Install Linux system dependencies
if: runner.os == 'Linux'
run: scripts/ci-install-linux-deps.sh
- uses: Swatinem/rust-cache@v2
with:
# One shared cache per OS for check+test; the build matrix below
# keys separately per target.
shared-key: check
- run: scripts/ci-check.sh
shell: bash
- name: Attribute failure
if: failure()
run: >-
echo "::error title=check failed on ${{ matrix.os }}::scripts/ci-check.sh (fmt/clippy/test) failed on ${{ matrix.os }}. Search the step log above for the first 'error' line."
# MSRV is validated with its own toolchain and WITHOUT --locked semantics
# being violated: same lockfile, older compiler. If this fails, either fix
# the code or bump the documented MSRV in a deliberate PR.
#
# MSRV is 1.86: the icu_* crates (transitive via idna/url) declare
# rust-version 1.86 and refuse to build on 1.85. Full rationale in
# docs/build-and-release.md#msrv-policy. Keep this version in sync with
# that doc and the rust-toolchain.toml comment.
msrv:
runs-on: ubuntu-24.04
env:
MSRV: "1.86.0"
steps:
- uses: actions/checkout@v4
- name: Install Linux system dependencies
run: scripts/ci-install-linux-deps.sh
- name: Install MSRV toolchain
run: rustup toolchain install "$MSRV" --profile minimal
- uses: Swatinem/rust-cache@v2
with:
shared-key: msrv
- name: Build with MSRV
run: cargo +"$MSRV" build --workspace --locked
- name: Attribute failure
if: failure()
run: >-
echo "::error title=MSRV build failed::Workspace does not build with Rust $MSRV. Either fix the code or bump the documented MSRV (docs/build-and-release.md#msrv-policy) in a deliberate PR."
# Licence + CVE gate. Runs on PRs (catches a bad dep at review time) and
# is additionally scheduled nightly by audit-nightly below, because new
# CVEs arrive without any commit happening.
compliance:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install Linux system dependencies
run: scripts/ci-install-linux-deps.sh
- uses: Swatinem/rust-cache@v2
with:
shared-key: compliance
# cargo-install'd tools live in ~/.cargo/bin and are cached too.
cache-all-crates: "true"
- run: scripts/ci-compliance.sh
shell: bash
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: dist/sbom/
# Cross-target build check: every triple we ship must at least COMPILE on
# every PR. Cheap (check-only where possible, no packaging) and catches
# cfg() breakage the day it is written, not at release tag time.
build-matrix:
strategy:
fail-fast: false
matrix:
include:
- os: macos-15
target: aarch64-apple-darwin
- os: macos-15
target: x86_64-apple-darwin
- os: windows-2025
target: x86_64-pc-windows-msvc
- os: windows-2025
# aarch64 Windows cross-compiles from the x86_64 runner; MSVC
# ships the cross linker so no extra toolchain is required.
target: aarch64-pc-windows-msvc
- os: ubuntu-24.04
target: x86_64-unknown-linux-gnu
- os: ubuntu-24.04
target: x86_64-unknown-linux-musl
- os: ubuntu-24.04
target: aarch64-unknown-linux-gnu
use-cross: true
- os: ubuntu-24.04
target: aarch64-unknown-linux-musl
use-cross: true
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
with:
# Per-target cache key: artifacts for different triples never
# share object files, so sharing a cache only causes eviction.
key: ${{ matrix.target }}
- name: Add target
if: ${{ !matrix.use-cross }}
run: rustup target add ${{ matrix.target }}
- name: Install Linux system dependencies
if: runner.os == 'Linux' && !matrix.use-cross
run: scripts/ci-install-linux-deps.sh
- name: Install musl tools
if: contains(matrix.target, 'musl') && !matrix.use-cross
run: sudo apt-get update && sudo apt-get install -y musl-tools
# Prebuilt binary instead of `cargo install cross`: same pinned version,
# seconds instead of ~2 minutes of compiling cross from source per run.
- name: Install cross
if: matrix.use-cross
uses: taiki-e/install-action@v2
with:
tool: cross@0.2.5
- name: Build (${{ matrix.target }})
shell: bash
run: |
FLAGS=(--workspace --locked --target ${{ matrix.target }})
# musl targets build the shape we actually ship for them: the
# static headless daemon (docs/build-and-release.md#matrix). The
# default feature set pulls cpal -> alsa-sys, and there is no musl
# ALSA on the runner or in the cross image, so a default-features
# musl build can only ever fail in alsa-sys's pkg-config probe.
# This mirrors scripts/build-headless.sh, not a weakening: every
# crate still compiles for the triple, minus the audio backend
# that a static server binary must not link anyway.
case "${{ matrix.target }}" in
*musl*) FLAGS+=(--no-default-features) ;;
esac
if [ "${{ matrix.use-cross }}" = "true" ]; then
# alsa-sys uses the pkg-config crate, which refuses to run when
# host != target unless explicitly allowed. The arm64 .pc files
# are installed by the Cross.toml pre-build hook.
export PKG_CONFIG_ALLOW_CROSS=1
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
cross build "${FLAGS[@]}"
else
cargo build "${FLAGS[@]}"
fi
- name: Attribute failure
if: failure()
shell: bash
run: >-
echo "::error title=build failed for ${{ matrix.target }}::Workspace does not compile for ${{ matrix.target }} on ${{ matrix.os }} (cross: ${{ matrix.use-cross || 'false' }}). The first 'error' line in the Build step above names the crate at fault."
# Headless build: proves the daemon path stays display-free. Runs on a
# plain ubuntu container with no X/Wayland present, which IS the deployment
# environment being claimed.
headless:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
with:
shared-key: headless
- name: Install musl tools
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Build static headless daemon
run: |
rustup target add x86_64-unknown-linux-musl
scripts/build-headless.sh x86_64-unknown-linux-musl
- name: Smoke test with no display server
# Explicitly unset every display variable; if the binary needed a
# display to even start, this is where it dies.
run: |
env -u DISPLAY -u WAYLAND_DISPLAY dist/headless/outloud-spiked-x86_64-unknown-linux-musl dry-run "change hello to goodbye"
# Performance tripwire. Every other job answers "is it correct"; this one
# answers "is it still fast", which no correctness test can see: a change
# that makes the overlay ten times slower produces exactly the same text
# and passes every assertion in the workspace.
#
# It measures only PURE paths (the rolling-window animation, hypothesis
# ingest, command parsing) precisely so it can run here. The richer gate in
# crates/ax-edit/benches/gate.rs measures the real accessibility path and
# correctly skips without a focused text field, which means on a CI runner
# it protects nothing. This one cannot skip.
#
# Budgets carry ~10x headroom over measured cost, so a shared runner being
# slow does not fail the build. See the bench's own comment for numbers.
perf:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
with:
shared-key: perf
- name: Perf gate
run: cargo bench -p overlay --bench perf_gate
- name: Attribute failure
if: failure()
run: |
echo "::error title=perf gate::A hot path got materially slower. This is not a correctness failure: the app still produces the right text, only later. See crates/overlay/benches/perf_gate.rs for the budgets and the reasoning."
# Reproducibility tripwire: double-build on Linux and require identical
# hashes. Only x86_64-linux (the cheapest runner) because determinism bugs
# are almost never target-specific; the flags are shared by all targets.
repro:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
# build-repro.sh derives SOURCE_DATE_EPOCH from git history.
fetch-depth: 1
- name: Install Linux system dependencies
run: scripts/ci-install-linux-deps.sh
# NOTE: no rust-cache here on purpose. A cache-restored target dir
# defeats the point of proving a clean double-build is deterministic.
- name: Double build and compare hashes
run: REPRO_VERIFY=1 scripts/build-repro.sh
- name: Attribute failure
if: failure()
run: >-
echo "::error title=repro job failed::Either the workspace failed to compile on x86_64 Linux (see the first 'error' line above) or the double build produced different hashes; the script prints both hashes and suggests diffoscope."