mirror of
https://github.com/openclaw/openclaw.git
synced 2026-11-01 04:58:23 +00:00
* fix(release): expose Linux builder admission and retain failed bundles * fix(release): preserve unconfirmed Linux dispatch status * fix(ci): make Linux build event admission explicit * fix(ci): separate Linux mirror dispatch from release builds * fix(ci): budget cold Linux source package builds
448 lines
18 KiB
YAML
448 lines
18 KiB
YAML
name: Linux App
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- "apps/linux/**"
|
|
- "scripts/lib/openclaw-bun.json"
|
|
- "scripts/stage-openclaw-bun.sh"
|
|
- "test/scripts/stage-openclaw-bun.test.ts"
|
|
- ".github/workflows/linux-app.yml"
|
|
- ".github/workflows/linux-app-release.yml"
|
|
- "!apps/linux/README.md"
|
|
- "!apps/linux/omarchy/README.md"
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: linux-app-${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
name: Build Linux companion
|
|
# Match the release build base for native tests and manually built bundles.
|
|
runs-on: ubuntu-22.04
|
|
# Leave room for cold source packaging and the native desktop proof afterward.
|
|
timeout-minutes: 105
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 1
|
|
fetch-tags: false
|
|
persist-credentials: false
|
|
submodules: false
|
|
|
|
- name: Install Tauri system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends \
|
|
at-spi2-core \
|
|
build-essential \
|
|
curl \
|
|
dbus-x11 \
|
|
ffmpeg \
|
|
file \
|
|
gir1.2-atspi-2.0 \
|
|
gnome-keyring \
|
|
gstreamer1.0-libav \
|
|
gstreamer1.0-plugins-bad \
|
|
gstreamer1.0-plugins-good \
|
|
gstreamer1.0-tools \
|
|
imagemagick \
|
|
libatk-adaptor \
|
|
libayatana-appindicator3-dev \
|
|
libegl1 \
|
|
libgles2 \
|
|
librsvg2-dev \
|
|
libssl-dev \
|
|
libwebkit2gtk-4.1-dev \
|
|
libxdo-dev \
|
|
mesa-utils \
|
|
openbox \
|
|
patchelf \
|
|
unzip \
|
|
pciutils \
|
|
procps \
|
|
psmisc \
|
|
python3-gi \
|
|
wget \
|
|
wmctrl \
|
|
x11-utils \
|
|
xauth \
|
|
xdg-utils \
|
|
xdotool \
|
|
xvfb
|
|
|
|
- name: Install Rust
|
|
run: rustup toolchain install stable --profile minimal --component rustfmt
|
|
|
|
- name: Cache Cargo
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
apps/linux/src-tauri/target
|
|
key: linux-app-${{ runner.os }}-${{ hashFiles('apps/linux/src-tauri/Cargo.lock') }}
|
|
restore-keys: |
|
|
linux-app-${{ runner.os }}-
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
cache-mode: restore
|
|
install-bun: "false"
|
|
|
|
- name: Test native dashboard authentication and runtime boundaries
|
|
run: |
|
|
node --test \
|
|
apps/linux/scripts/test-native-control-auth.mjs \
|
|
apps/linux/scripts/test-runtime-boundary.mjs \
|
|
apps/linux/scripts/test-stage-runtime.mjs \
|
|
apps/linux/scripts/test-stage-package.mjs
|
|
|
|
- name: Test packaged runtime ABI scanner
|
|
run: |
|
|
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover \
|
|
-s apps/linux/tests -p 'test_packaged_runtime_smoke.py'
|
|
|
|
- name: Test desktop sharing proof report ordering
|
|
run: |
|
|
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover \
|
|
-s apps/linux/tests -p 'test_desktop_sharing_reports.py'
|
|
|
|
- name: Test native first-run accessibility role matching
|
|
run: |
|
|
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover \
|
|
-s apps/linux/tests -p 'test_first_run.py'
|
|
|
|
- name: Check Rust formatting
|
|
working-directory: apps/linux/src-tauri
|
|
run: cargo +stable fmt --check
|
|
|
|
- name: Test Omarchy desktop handoff
|
|
run: |
|
|
PYTHONDONTWRITEBYTECODE=1 /usr/bin/python3 -m unittest discover \
|
|
-s apps/linux/omarchy -p 'test_*.py'
|
|
|
|
- name: Run Rust tests
|
|
working-directory: apps/linux/src-tauri
|
|
run: cargo +stable test --locked --all-targets
|
|
|
|
- name: Build native inline browser candidate
|
|
working-directory: apps/linux/src-tauri
|
|
env:
|
|
# Cold Linux declarations also exceed the package builder's 45-minute default.
|
|
OPENCLAW_DOCKER_PACKAGE_BUILD_TIMEOUT_MS: "3600000"
|
|
run: |
|
|
node ../scripts/stage-runtime.mjs
|
|
# The native fixture supplies its CLI at the same build-time boundary.
|
|
python3 ../tests/stage_onboarding_runtime.py target/desktop-runtime
|
|
cargo +stable build --locked
|
|
|
|
- name: Test native inline browser
|
|
id: inline-browser
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "${RUNNER_TEMP}/linux-inline-browser"
|
|
xvfb-run -a -s '-screen 0 1280x1024x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/debug/openclaw-desktop --inline-browser \
|
|
--artifacts-dir "${RUNNER_TEMP}/linux-inline-browser" \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-inline-browser/run.log"
|
|
|
|
- name: Upload native inline browser proof
|
|
if: always() && steps.inline-browser.outcome != 'skipped'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-inline-browser
|
|
retention-days: 14
|
|
if-no-files-found: warn
|
|
path: ${{ runner.temp }}/linux-inline-browser
|
|
|
|
- name: Test native Gateway switching
|
|
id: gateway-switch
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "${RUNNER_TEMP}/linux-gateway-switch"
|
|
xvfb-run -a -s '-screen 0 1440x1080x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/debug/openclaw-desktop --gateway-switch \
|
|
--artifacts-dir "${RUNNER_TEMP}/linux-gateway-switch" \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-gateway-switch/run.log"
|
|
mkdir -p "${RUNNER_TEMP}/linux-gateway-switch/onboarding"
|
|
node scripts/run-node-package-bin.mts vite build --config apps/linux/tests/fixtures/vite.onboarding.config.ts
|
|
xvfb-run -a -s '-screen 0 1440x1080x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/debug/openclaw-desktop --gateway-onboarding \
|
|
--artifacts-dir "${RUNNER_TEMP}/linux-gateway-switch/onboarding" \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-gateway-switch/onboarding/run.log"
|
|
|
|
- name: Upload native Gateway switching proof
|
|
if: always() && steps.gateway-switch.outcome != 'skipped'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-gateway-switch
|
|
retention-days: 14
|
|
if-no-files-found: warn
|
|
path: ${{ runner.temp }}/linux-gateway-switch
|
|
|
|
- name: Test native desktop sharing
|
|
id: desktop-sharing
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "${RUNNER_TEMP}/linux-desktop-sharing"
|
|
xvfb-run -a -s '-screen 0 1440x1080x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/debug/openclaw-desktop --desktop-sharing \
|
|
--artifacts-dir "${RUNNER_TEMP}/linux-desktop-sharing" \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-desktop-sharing/run.log"
|
|
|
|
- name: Upload native desktop sharing proof
|
|
if: always() && steps.desktop-sharing.outcome != 'skipped'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-desktop-sharing
|
|
retention-days: 14
|
|
if-no-files-found: warn
|
|
path: ${{ runner.temp }}/linux-desktop-sharing
|
|
|
|
- name: Test native Quick Chat
|
|
id: quick-chat
|
|
if: github.event_name == 'workflow_dispatch'
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "${RUNNER_TEMP}/linux-quick-chat"
|
|
xvfb-run -a -s '-screen 0 1440x1080x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/debug/openclaw-desktop --quick-chat \
|
|
--artifacts-dir "${RUNNER_TEMP}/linux-quick-chat" \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-quick-chat/run.log"
|
|
|
|
- name: Upload native Quick Chat proof
|
|
if: always() && steps.quick-chat.outcome != 'skipped'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-quick-chat
|
|
retention-days: 14
|
|
if-no-files-found: warn
|
|
path: ${{ runner.temp }}/linux-quick-chat
|
|
|
|
- name: Stage AppImage GStreamer plugins
|
|
if: github.event_name == 'workflow_dispatch'
|
|
run: |
|
|
plugins="${RUNNER_TEMP}/openclaw-gstreamer-plugins"
|
|
apps/linux/scripts/stage-appimage-gstreamer.sh "$plugins" \
|
|
| tee "${RUNNER_TEMP}/openclaw-gstreamer-elements.tsv"
|
|
echo "GSTREAMER_PLUGINS_DIR=$plugins" >> "$GITHUB_ENV"
|
|
|
|
- name: Prepare pinned AppImage tools
|
|
if: github.event_name == 'workflow_dispatch'
|
|
env:
|
|
XDG_CACHE_HOME: ${{ runner.temp }}/openclaw-tauri-cache
|
|
run: | # zizmor: ignore[github-env] trusted checked-in helper returns the native runtime path
|
|
set -euo pipefail
|
|
apps/linux/scripts/tauri-appimage-tools.sh prepare
|
|
apps/linux/scripts/tauri-appimage-tools.sh verify pre-build
|
|
runtime_file=$(apps/linux/scripts/tauri-appimage-tools.sh runtime-path)
|
|
printf 'LDAI_RUNTIME_FILE=%s\n' "$runtime_file" >> "$GITHUB_ENV"
|
|
|
|
- name: Build Linux companion bundles
|
|
if: github.event_name == 'workflow_dispatch'
|
|
working-directory: apps/linux/src-tauri
|
|
env:
|
|
# appimagetool strips fail on CI runners; Tauri documents NO_STRIP for Actions.
|
|
NO_STRIP: "true"
|
|
XDG_CACHE_HOME: ${{ runner.temp }}/openclaw-tauri-cache
|
|
# Validation builds have no TAURI_SIGNING_PRIVATE_KEY, and the configured updater
|
|
# pubkey makes the bundler hard-require it. Skip updater artifacts here;
|
|
# linux-app-release.yml builds the signed updater bundles.
|
|
run: pnpm dlx @tauri-apps/cli@2.11.4 build --bundles deb,appimage --config '{"bundle":{"createUpdaterArtifacts":false,"useLocalToolsDir":false}}'
|
|
|
|
- name: Finalize AppImage
|
|
if: github.event_name == 'workflow_dispatch'
|
|
env:
|
|
XDG_CACHE_HOME: ${{ runner.temp }}/openclaw-tauri-cache
|
|
run: apps/linux/scripts/finalize-appimage.sh apps/linux/src-tauri/target/release/bundle/appimage
|
|
|
|
- name: Test native first-run setup and failed Gateway startup
|
|
id: first-run
|
|
if: github.event_name == 'workflow_dispatch'
|
|
shell: bash
|
|
run: |
|
|
xvfb-run -a -s '-screen 0 1280x1024x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/release/openclaw-desktop \
|
|
2>&1 | tee "${RUNNER_TEMP}/linux-first-run.log"
|
|
xvfb-run -a -s '-screen 0 1280x1024x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/first_run.py \
|
|
apps/linux/src-tauri/target/release/openclaw-desktop --local-start-failure \
|
|
2>&1 | tee -a "${RUNNER_TEMP}/linux-first-run.log"
|
|
|
|
- name: Upload first-run failure log
|
|
if: github.event_name == 'workflow_dispatch' && failure() && steps.first-run.outcome == 'failure'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-first-run-failure
|
|
retention-days: 14
|
|
if-no-files-found: error
|
|
path: ${{ runner.temp }}/linux-first-run.log
|
|
|
|
- name: Test packaged AppImage runtime
|
|
id: packaged-runtime
|
|
if: github.event_name == 'workflow_dispatch'
|
|
shell: bash
|
|
run: |
|
|
shopt -s nullglob
|
|
appimages=(apps/linux/src-tauri/target/release/bundle/appimage/*.AppImage)
|
|
if [[ ${#appimages[@]} -ne 1 ]]; then
|
|
echo "Expected one AppImage, found ${#appimages[@]}"
|
|
exit 1
|
|
fi
|
|
xvfb-run -a -s '-screen 0 1280x1024x24' dbus-run-session -- \
|
|
/usr/bin/python3 apps/linux/tests/packaged_runtime_smoke.py \
|
|
"${appimages[0]}" \
|
|
--output "${RUNNER_TEMP}/linux-packaged-runtime" \
|
|
--require-fuse \
|
|
--shell-container \
|
|
"archlinux@sha256:82b1b08faae9d61e3e7e13d562f4d09114d939105b0d59ff34140f3bd418593a"
|
|
|
|
- name: Upload packaged runtime failure evidence
|
|
if: github.event_name == 'workflow_dispatch' && failure() && steps.packaged-runtime.outcome == 'failure'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: linux-packaged-runtime-failure
|
|
retention-days: 14
|
|
if-no-files-found: warn
|
|
path: ${{ runner.temp }}/linux-packaged-runtime
|
|
|
|
- name: Upload bundles
|
|
if: github.event_name == 'workflow_dispatch'
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: openclaw-linux-companion
|
|
retention-days: 14
|
|
if-no-files-found: error
|
|
path: |
|
|
apps/linux/src-tauri/target/release/bundle/deb/*.deb
|
|
apps/linux/src-tauri/target/release/bundle/appimage/*.AppImage
|
|
|
|
test-macos:
|
|
name: Test macOS companion
|
|
# This app also ships macOS desktop-test bundles, but the only job that
|
|
# compiles them (linux-app-release.yml build_macos) runs behind a manual
|
|
# dispatch input. Without a per-PR check, a macOS-gated Tauri API can break
|
|
# the macOS target for weeks unnoticed - `WebviewWindowBuilder::transparent`
|
|
# did exactly that.
|
|
#
|
|
# macos-15, not macos-14: tauri-plugin-notifications' Swift sources use
|
|
# typed throws (`throws(FFIResult)`), which needs Swift 6. The macos-14
|
|
# image still ships Swift 5.x and fails to parse them.
|
|
runs-on: macos-15
|
|
timeout-minutes: 30
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 1
|
|
fetch-tags: false
|
|
persist-credentials: false
|
|
submodules: false
|
|
|
|
- name: Install Rust
|
|
run: rustup toolchain install stable --profile minimal
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
cache-mode: restore
|
|
install-bun: "false"
|
|
|
|
- name: Cache Cargo
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
apps/linux/src-tauri/target
|
|
key: macos-app-${{ runner.os }}-${{ hashFiles('apps/linux/src-tauri/Cargo.lock') }}
|
|
restore-keys: |
|
|
macos-app-${{ runner.os }}-
|
|
|
|
- name: Test macOS companion
|
|
working-directory: apps/linux/src-tauri
|
|
# `test` rather than `check`: it additionally links and runs the
|
|
# binaries, which is the only way macOS link-time breakage (a missing
|
|
# Swift runtime rpath, say) shows up at all. `--all-targets` keeps the
|
|
# compile coverage `check --all-targets` used to give.
|
|
run: cargo +stable test --locked --all-targets
|
|
|
|
- name: Verify native macOS power lifecycle
|
|
working-directory: apps/linux/src-tauri
|
|
run: cargo +stable test --locked --test native_keep_awake native_power_lifecycle -- --nocapture
|
|
|
|
test-windows-power:
|
|
name: Test Windows desktop
|
|
# Match the regular Windows installer build runner.
|
|
runs-on: windows-2022
|
|
timeout-minutes: 105
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 1
|
|
fetch-tags: false
|
|
persist-credentials: false
|
|
submodules: false
|
|
|
|
- name: Install Rust
|
|
run: rustup toolchain install stable --profile minimal
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
cache-mode: off
|
|
install-bun: "false"
|
|
|
|
- name: Cache Cargo
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
apps/linux/src-tauri/target
|
|
key: windows-app-${{ runner.os }}-${{ hashFiles('apps/linux/src-tauri/Cargo.lock') }}
|
|
restore-keys: |
|
|
windows-app-${{ runner.os }}-
|
|
|
|
- name: Verify native Windows power lifecycle
|
|
working-directory: apps/linux/src-tauri
|
|
run: cargo +stable test --locked --test native_keep_awake native_power_lifecycle -- --nocapture
|
|
|
|
- name: Verify Windows runtime, installer, and desktop boundaries
|
|
working-directory: apps/linux/src-tauri
|
|
run: |
|
|
node --test ../scripts/test-stage-runtime.mjs ../scripts/test-stage-package.mjs
|
|
cargo +stable test --locked --bin openclaw-desktop
|
|
|
|
- name: Build Windows installer without publishing
|
|
working-directory: apps/linux/src-tauri
|
|
env:
|
|
# Cold Windows declarations can exceed the package builder's 45-minute default.
|
|
OPENCLAW_DOCKER_PACKAGE_BUILD_TIMEOUT_MS: "3600000"
|
|
run: pnpm dlx @tauri-apps/cli@2.11.4 build --bundles nsis --config '{"bundle":{"createUpdaterArtifacts":false}}'
|
|
|
|
- name: Upload Windows installer
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: openclaw-windows-desktop
|
|
retention-days: 14
|
|
if-no-files-found: error
|
|
path: apps/linux/src-tauri/target/release/bundle/nsis/*.exe
|