Skip to content

fix(gui): stabilize daemon terminal redraw and follow behavior #389

fix(gui): stabilize daemon terminal redraw and follow behavior

fix(gui): stabilize daemon terminal redraw and follow behavior #389

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
env:
CARGO_TERM_COLOR: always
NIGHTLY_TOOLCHAIN: nightly-2025-11-30
jobs:
zizmor:
name: Workflow Security
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: zizmorcore/zizmor-action@135698455da5c3b3e55f73f4419e481ab68cdd95 # v0.4.1
with:
advanced-security: false
online-audits: false
binary-names:
name: Binary Name Collisions
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Check for case-insensitive binary name collisions
run: ./scripts/ci/check-binary-names.sh --cargo
fmt:
name: Format
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: taiki-e/install-action@a7c938ee233ac44ef6f2fba5ac2d30fa12584c6e # just
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
components: rustfmt
- run: just format-check
clippy:
name: Clippy
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: taiki-e/install-action@a7c938ee233ac44ef6f2fba5ac2d30fa12584c6e # just
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
components: clippy
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb1-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libavahi-client-dev
- run: just lint
test:
name: Test
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: taiki-e/install-action@a7c938ee233ac44ef6f2fba5ac2d30fa12584c6e # just
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb1-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libavahi-client-dev
- run: just test
terminal-benchmark-smoke:
name: Terminal Benchmark Smoke
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
- name: Run terminal workload benchmark helper
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} test -p arbor-terminal-emulator --test workload_benchmark -- --ignored --nocapture
ghostty-vt:
name: Ghostty VT
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
contents: read
env:
RUSTFLAGS: -L native=${{ github.workspace }}/target/ghostty-vt-bridge/lib -C link-arg=-Wl,-rpath,${{ github.workspace }}/target/ghostty-vt-bridge/lib
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
submodules: recursive
persist-credentials: false
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
- name: Install Zig
run: |
ZIG_VERSION=0.15.2
curl -L "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" -o zig.tar.xz
tar -xf zig.tar.xz
echo "${PWD}/zig-x86_64-linux-${ZIG_VERSION}" >> "$GITHUB_PATH"
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb1-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libavahi-client-dev
- name: Build Ghostty VT bridge
run: ./scripts/build-ghostty-vt-bridge.sh
- name: Test arbor-terminal-emulator with Ghostty VT
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} test -p arbor-terminal-emulator --features ghostty-vt-experimental
- name: Check arbor-gui with Ghostty VT
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} check -p arbor-gui --features ghostty-vt-experimental
- name: Check arbor-httpd with Ghostty VT
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} check -p arbor-httpd --features ghostty-vt-experimental
clippy-windows:
name: Clippy (Windows)
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
components: clippy
- name: Clippy (Windows)
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} clippy --workspace --features vendored-openssl --all-targets -- -D warnings
test-windows:
name: Test (Windows)
needs: [zizmor]
if: ${{ !cancelled() && !failure() }}
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
- name: Test (Windows)
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} test --workspace --features vendored-openssl
build-matrix:
name: Build (${{ matrix.name }})
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [fmt, clippy, test, terminal-benchmark-smoke, ghostty-vt, clippy-windows, test-windows]
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- name: linux-x86_64
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- name: linux-aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- name: macos-aarch64
os: macos-14
target: aarch64-apple-darwin
- name: macos-x86_64
os: macos-14
target: x86_64-apple-darwin
- name: windows-x86_64
os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
lfs: true
persist-credentials: false
- uses: dtolnay/rust-toolchain@efa25f7f19611383d5b0ccf2d1c8914531636bf9 # master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
- name: Install Linux x86_64 dependencies
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb1-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libavahi-client-dev
- name: Install Linux ARM64 toolchain and GUI dependencies
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo dpkg --add-architecture arm64
# Restrict existing sources to amd64 only, then add the ports
# mirror for arm64 packages (the default mirrors don't carry arm64).
# Ubuntu 24.04 uses deb822 (.sources) format; pin with Architectures.
sudo sed -i '/^Types:/a Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null || true
# Also handle any traditional .list files (skip those with existing [arch=]).
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
[ -f "$f" ] && sudo sed -i '/^\s*#/!s/^deb \([^[]\)/deb [arch=amd64] \1/' "$f" 2>/dev/null || true
done
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -cs) main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/arm64.list
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -cs)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/arm64.list
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports $(lsb_release -cs)-security main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/arm64.list
sudo apt-get update
sudo apt-get install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
libxcb1-dev:arm64 \
libxkbcommon-dev:arm64 \
libxkbcommon-x11-dev:arm64 \
libssl-dev:arm64 \
zlib1g-dev:arm64 \
libavahi-client-dev:arm64
- name: Add Rust target
run: rustup target add ${{ matrix.target }}
- name: Build arbor-gui (linux arm64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
PKG_CONFIG_ALLOW_CROSS: "1"
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} build --locked --release -p arbor-gui --features vendored-openssl --target ${{ matrix.target }}
- name: Build arbor-gui
if: matrix.target != 'aarch64-unknown-linux-gnu'
run: cargo +${{ env.NIGHTLY_TOOLCHAIN }} build --locked --release -p arbor-gui --features vendored-openssl --target ${{ matrix.target }}
- name: Package macOS app
if: startsWith(matrix.target, 'aarch64-apple-darwin') || startsWith(matrix.target, 'x86_64-apple-darwin')
shell: bash
run: |
mkdir -p dist
./scripts/ci/package-macos.sh "ci" "${{ matrix.target }}" "target/${{ matrix.target }}/release/Arbor" "dist"
- name: Verify macOS deployment target
if: startsWith(matrix.target, 'aarch64-apple-darwin') || startsWith(matrix.target, 'x86_64-apple-darwin')
shell: bash
run: |
./scripts/ci/verify-macos-deployment-target.sh \
11.0 \
dist/Arbor.app/Contents/MacOS/Arbor
- name: Import signing certificate
if: (startsWith(matrix.target, 'aarch64-apple-darwin') || startsWith(matrix.target, 'x86_64-apple-darwin')) && env.MACOS_CERTIFICATE != ''
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
shell: bash
run: |
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -base64 32)"
echo -n "$MACOS_CERTIFICATE" | base64 --decode -o "$RUNNER_TEMP/certificate.p12"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$RUNNER_TEMP/certificate.p12" \
-P "$MACOS_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 \
-k "$KEYCHAIN_PATH"
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" \
"$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH"
rm -f "$RUNNER_TEMP/certificate.p12"
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
echo "KEYCHAIN_PASSWORD=$KEYCHAIN_PASSWORD" >> "$GITHUB_ENV"
- name: Codesign app bundle
if: (startsWith(matrix.target, 'aarch64-apple-darwin') || startsWith(matrix.target, 'x86_64-apple-darwin')) && env.MACOS_CERTIFICATE != ''
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
shell: bash
run: |
APP_PATH="dist/Arbor.app"
echo "Available signing identities:"
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" \
| { grep "Developer ID Application" || true; } | head -1 | sed 's/.*"\(.*\)".*/\1/')
if [[ -z "$IDENTITY" ]]; then
echo "::error::No 'Developer ID Application' identity found in keychain"
exit 1
fi
echo "Signing with identity: $IDENTITY"
CLI_BIN="${APP_PATH}/Contents/Helpers/arbor"
if [[ -f "${CLI_BIN}" ]]; then
/usr/bin/codesign --force --timestamp --options runtime \
--entitlements packaging/macos/Arbor.entitlements \
--sign "$IDENTITY" \
"${CLI_BIN}" -v
fi
/usr/bin/codesign --force --timestamp --options runtime \
--entitlements packaging/macos/Arbor.entitlements \
--sign "$IDENTITY" \
"${APP_PATH}/Contents/MacOS/Arbor" -v
/usr/bin/codesign --force --timestamp --options runtime \
--entitlements packaging/macos/Arbor.entitlements \
--sign "$IDENTITY" \
"${APP_PATH}" -v
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
- name: Clean up keychain
if: always() && env.KEYCHAIN_PATH != ''
shell: bash
run: |
if [[ -n "${KEYCHAIN_PATH:-}" ]]; then
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
fi