426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
549 lines
27 KiB
YAML
549 lines
27 KiB
YAML
name: android-device-e2e
|
|
|
|
# Real-device Android e2e: builds the WebView-debuggable APK, boots an emulator,
|
|
# and drives the real app via Playwright's Android driver + the on-device agent
|
|
# smoke. A weekly host-backed schedule gives the lane unattended cadence without
|
|
# burning an emulator on every PR; workflow_dispatch still exposes the full
|
|
# backend selector for manual cloud/local investigations.
|
|
#
|
|
# IMPORTANT coverage note: the embedded on-device agent (bun + llama) runs on
|
|
# real arm64 hardware but SIGSEGVs on a stock x86_64 emulator, so the LOCAL
|
|
# route's full chat round-trip only goes green on a self-hosted arm64 device
|
|
# runner. On a GitHub-hosted x86_64 emulator this lane proves: build + install +
|
|
# WebView attach + route navigation, and runs the local smoke as a (currently
|
|
# expected-to-surface) signal. For green route coverage on the emulator, supply
|
|
# an Eliza Cloud token (ELIZA_CLOUD_AUTH_TOKEN secret) so the app onboards to a
|
|
# real cloud agent.
|
|
#
|
|
# iOS onboarding-smoke parity (#13580): mobile-build-smoke.yml runs
|
|
# ios-onboarding-smoke.mjs on every path-gated PR, but the Android
|
|
# onboarding->home smoke (test:e2e:android:onboarding) is intentionally NOT
|
|
# per-PR. The iOS smoke boots a simulator on a macOS runner cheaply; the
|
|
# Android smoke needs a KVM-accelerated x86_64 emulator on a Linux runner,
|
|
# which is too heavy to boot on every PR (issue #9943). Android
|
|
# onboarding->home instead runs on three cadences: the label-gated
|
|
# (ci:device) pr-device-smoke job below, the weekly host-backed schedule,
|
|
# and workflow_dispatch -- so an onboarding regression is caught on a PR
|
|
# opt-in or within one weekly cycle rather than never. This is the
|
|
# documented divergence the parity checkbox in #13580 asks for.
|
|
|
|
on:
|
|
schedule:
|
|
# Weekly hosted x86_64 emulator run, backend=host by default below. The
|
|
# self-hosted arm64 LOCAL route remains separate because bun+llama SIGSEGVs
|
|
# on the stock hosted x86_64 emulator (see coverage note above).
|
|
- cron: "17 9 * * 1"
|
|
workflow_dispatch:
|
|
inputs:
|
|
backend:
|
|
description: "WebView backend for route coverage (cloud|host|local)"
|
|
type: choice
|
|
options: [cloud, host, local]
|
|
default: cloud
|
|
api-level:
|
|
description: "Android emulator API level"
|
|
default: "34"
|
|
# Reachable on normal develop PRs and release-promotion PRs, but only when
|
|
# explicitly opted in with the `ci:device` label (issue #9943) — booting an
|
|
# emulator is slow/heavy, so we never burn a runner on every PR. The
|
|
# pr-device-smoke job below is gated on that label and runs a MINIMAL slice
|
|
# (build + install + WebView attach + onboarding→home + one host-backed chat
|
|
# turn); the full dispatch matrix above is unchanged.
|
|
pull_request:
|
|
branches: [develop, main]
|
|
types: [opened, synchronize, reopened, labeled]
|
|
|
|
concurrency:
|
|
group: android-device-e2e-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
android-e2e:
|
|
# Full lane runs on manual dispatch and on the weekly scheduled host-backed
|
|
# cadence. Never on PR; PRs use the label-gated minimal smoke below.
|
|
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
|
|
runs-on: ${{ fromJSON(vars.HETZNER_FLEET_ONLINE == 'false' && '["ubuntu-24.04"]' || '["self-hosted","hetzner-robot"]') }}
|
|
timeout-minutes: 90
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
ELIZA_MOBILE_REPO_ROOT: ${{ github.workspace }}
|
|
ELIZA_WEBVIEW_DEBUG: "1"
|
|
ELIZA_BUN_RISCV64_OPTIONAL: "1"
|
|
ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB: "1"
|
|
ELIZA_ANDROID_BACKEND: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.backend || 'host' }}
|
|
ELIZA_CLOUD_AUTH_TOKEN: ${{ secrets.ELIZA_CLOUD_AUTH_TOKEN }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3.14" # pinned: repo packageManager is bun@1.4.0, an unpublished version (GH+npm 404) that makes bare setup-bun fail here; match .github/ci-bun-version.json (#15071)
|
|
- uses: actions/setup-java@v5
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
- uses: android-actions/setup-android@v4
|
|
|
|
- name: Install workspace
|
|
run: bun install
|
|
|
|
- name: Build WebView-debuggable APK
|
|
run: bun run --cwd packages/app build:android
|
|
|
|
# KVM enables a usable emulator on GitHub ubuntu runners.
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules && sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Route coverage on emulator
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.api-level || '34' }}
|
|
arch: x86_64
|
|
ram-size: 6144M
|
|
# avx2/fma passthrough so the (x86) on-device agent doesn't SIGILL.
|
|
emulator-options: -no-window -no-snapshot -no-boot-anim -gpu swiftshader_indirect -qemu -cpu qemu64,+avx,+avx2,+f16c,+fma
|
|
script: |
|
|
set -euo pipefail
|
|
adb root || true
|
|
adb shell setenforce 0 || true
|
|
bun run --cwd packages/app build # ensure web dist
|
|
|
|
mkdir -p packages/app/test-results/android-onboarding-to-home
|
|
ELIZA_API_PORT=31337 \
|
|
ELIZA_PAIRING_DISABLED=1 \
|
|
node packages/app-core/scripts/run-node-tsx.mjs \
|
|
packages/app-core/scripts/serve-real-local-agent.ts \
|
|
> packages/app/test-results/android-onboarding-to-home/host-agent.log 2>&1 &
|
|
HOST_AGENT_PID=$!
|
|
trap 'kill "$HOST_AGENT_PID" 2>/dev/null || true' EXIT
|
|
for i in $(seq 1 90); do
|
|
if curl -fsS \
|
|
-H 'X-ElizaOS-Client-Id: android-onboarding-ci' \
|
|
http://127.0.0.1:31337/api/health >/tmp/android-host-agent-health.json; then
|
|
cat /tmp/android-host-agent-health.json
|
|
break
|
|
fi
|
|
if ! kill -0 "$HOST_AGENT_PID" 2>/dev/null; then
|
|
echo "Host agent exited before becoming healthy"
|
|
cat packages/app/test-results/android-onboarding-to-home/host-agent.log
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
done
|
|
curl -fsS \
|
|
-H 'X-ElizaOS-Client-Id: android-onboarding-ci' \
|
|
http://127.0.0.1:31337/api/health >/dev/null
|
|
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:onboarding
|
|
|
|
if [ "${ELIZA_ANDROID_BACKEND}" = "local" ]; then
|
|
# LOCAL route is still signal-only on hosted x86_64: the embedded
|
|
# bun+llama agent is known to require a self-hosted arm64 device
|
|
# runner. Keep surfacing the artifact, but do not make the hosted
|
|
# emulator falsely red for that known platform gap.
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:routes || true
|
|
else
|
|
# Host/cloud route coverage is the scheduled unattended health
|
|
# signal. Fail red here instead of hiding regressions behind a
|
|
# permanent signal-only `|| true` leg.
|
|
ELIZA_ANDROID_REQUIRE_AGENT=0 \
|
|
bun run --cwd packages/app test:e2e:android:routes
|
|
fi
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:native-plugin-view
|
|
# Local on-device agent smoke (surfaces failure loudly; expected to
|
|
# fail on stock x86 emulator — see coverage note above).
|
|
bun run --cwd packages/app test:sim:local-chat:android:live || true
|
|
|
|
# Seeded launcher gesture loop (#12377): ≥200 real device swipes/taps
|
|
# with per-action rail invariants (data-page + AX probe + inertness +
|
|
# focus), chunked screenrecord + logcat. Signal-only on the x86
|
|
# emulator until proven emulator-green; the seed prints for replay.
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:launcher-loop || true
|
|
|
|
- name: Cloud provisioning probe
|
|
run: |
|
|
if [ -z "${ELIZA_CLOUD_AUTH_TOKEN:-}" ]; then
|
|
{
|
|
echo "### Cloud provisioning probe"
|
|
echo ""
|
|
echo "skipped: missing ELIZA_CLOUD_AUTH_TOKEN"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
echo "Cloud provisioning probe skipped: missing ELIZA_CLOUD_AUTH_TOKEN"
|
|
exit 0
|
|
fi
|
|
bun run --cwd packages/app test:e2e:android:cloud
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: android-e2e-artifacts
|
|
path: |
|
|
packages/app/test-results/**
|
|
packages/app/playwright-report/**
|
|
if-no-files-found: ignore
|
|
|
|
notify-scheduled-failure:
|
|
needs: android-e2e
|
|
if: ${{ always() && github.event_name == 'schedule' && needs.android-e2e.result != 'success' }}
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
env:
|
|
ANDROID_E2E_RESULT: ${{ needs.android-e2e.result }}
|
|
ELIZA_ANDROID_BACKEND: host
|
|
steps:
|
|
- name: Open / update scheduled failure issue
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
|
|
with:
|
|
script: |
|
|
const title = "Scheduled Android device e2e is failing (#13580)";
|
|
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
|
const artifactsUrl = `${runUrl}#artifacts`;
|
|
const runDate = new Date().toISOString();
|
|
const result = process.env.ANDROID_E2E_RESULT || "unknown";
|
|
const body = [
|
|
"The weekly host-backed Android device e2e cadence failed.",
|
|
"",
|
|
`Run: ${runUrl}`,
|
|
`Artifacts: ${artifactsUrl}`,
|
|
`Workflow: \`${context.workflow}\``,
|
|
`Android job result: \`${result}\``,
|
|
`Backend: \`${process.env.ELIZA_ANDROID_BACKEND || "host"}\``,
|
|
`Observed: ${runDate}`,
|
|
"",
|
|
"This issue is auto-maintained by `.github/workflows/android-device-e2e.yml` so scheduled failures are visible outside Actions history.",
|
|
"",
|
|
"Known #13580 residuals still tracked separately:",
|
|
"- LOCAL arm64 on-device route needs a self-hosted arm64 runner.",
|
|
"- Signal-only PR legs should be promoted only after emulator-green evidence exists.",
|
|
].join("\n");
|
|
|
|
const openIssues = await github.paginate(github.rest.issues.listForRepo, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
state: "open",
|
|
per_page: 100,
|
|
});
|
|
const existing = openIssues.find((issue) => !issue.pull_request && issue.title === title);
|
|
if (existing) {
|
|
await github.rest.issues.update({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: existing.number,
|
|
body,
|
|
});
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: existing.number,
|
|
body: `Scheduled Android device e2e failed again (${result}): ${runUrl}`,
|
|
});
|
|
core.info(`Updated scheduled failure issue #${existing.number}`);
|
|
} else {
|
|
const { data: created } = await github.rest.issues.create({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
title,
|
|
body,
|
|
});
|
|
core.info(`Opened scheduled failure issue #${created.number}`);
|
|
}
|
|
|
|
# Minimal, label-gated PR device smoke (issue #9943). Reachable on a PR only
|
|
# when the `ci:device` label is applied, so it never burns an emulator runner
|
|
# on every PR. Runs the proven-green x86 slice: build the WebView-debuggable
|
|
# APK, boot the emulator, attach, and drive onboarding→home plus a chat turn
|
|
# against the host-run agent (the host backend runs the agent on the runner, so
|
|
# it works on a stock x86 emulator where the embedded on-device agent SIGSEGVs).
|
|
pr-device-smoke:
|
|
if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ci:device') }}
|
|
runs-on: ${{ fromJSON(vars.HETZNER_FLEET_ONLINE == 'false' && '["ubuntu-24.04"]' || '["self-hosted","hetzner-robot"]') }}
|
|
timeout-minutes: 60
|
|
env:
|
|
ELIZA_MOBILE_REPO_ROOT: ${{ github.workspace }}
|
|
ELIZA_WEBVIEW_DEBUG: "1"
|
|
ELIZA_BUN_RISCV64_OPTIONAL: "1"
|
|
ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB: "1"
|
|
ELIZA_ANDROID_BACKEND: "host"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3.14" # pinned: repo packageManager is bun@1.4.0, an unpublished version (GH+npm 404) that makes bare setup-bun fail here; match .github/ci-bun-version.json (#15071)
|
|
- uses: actions/setup-java@v5
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
- uses: android-actions/setup-android@v4
|
|
|
|
- name: Install workspace
|
|
run: bun install
|
|
|
|
- name: Build WebView-debuggable APK
|
|
run: bun run --cwd packages/app build:android
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules && sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Onboarding→home + chat turn on emulator (host agent)
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: "34"
|
|
arch: x86_64
|
|
ram-size: 6144M
|
|
emulator-options: -no-window -no-snapshot -no-boot-anim -gpu swiftshader_indirect -qemu -cpu qemu64,+avx,+avx2,+f16c,+fma
|
|
script: |
|
|
set -euo pipefail
|
|
adb root || true
|
|
adb shell setenforce 0 || true
|
|
bun run --cwd packages/app build # ensure web dist
|
|
|
|
mkdir -p packages/app/test-results/android-onboarding-to-home
|
|
ELIZA_API_PORT=31337 \
|
|
ELIZA_PAIRING_DISABLED=1 \
|
|
node packages/app-core/scripts/run-node-tsx.mjs \
|
|
packages/app-core/scripts/serve-real-local-agent.ts \
|
|
> packages/app/test-results/android-onboarding-to-home/host-agent.log 2>&1 &
|
|
HOST_AGENT_PID=$!
|
|
trap 'kill "$HOST_AGENT_PID" 2>/dev/null || true' EXIT
|
|
for i in $(seq 1 90); do
|
|
if curl -fsS \
|
|
-H 'X-ElizaOS-Client-Id: android-pr-device-smoke' \
|
|
http://127.0.0.1:31337/api/health >/tmp/android-host-agent-health.json; then
|
|
cat /tmp/android-host-agent-health.json
|
|
break
|
|
fi
|
|
if ! kill -0 "$HOST_AGENT_PID" 2>/dev/null; then
|
|
echo "Host agent exited before becoming healthy"
|
|
cat packages/app/test-results/android-onboarding-to-home/host-agent.log
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
done
|
|
curl -fsS \
|
|
-H 'X-ElizaOS-Client-Id: android-pr-device-smoke' \
|
|
http://127.0.0.1:31337/api/health >/dev/null
|
|
|
|
# Onboarding→home + first chat turn, asserted against the host agent.
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:onboarding
|
|
|
|
# Native plugin x WebView smoke: proves Capacitor JS calls cross
|
|
# into Android Kotlin rather than the desktop Chromium bridge shim.
|
|
# Signal-only (|| true) on the PR lane until it's proven green on the
|
|
# x86_64 API-34 emulator this job uses — the hard assertion runs in
|
|
# the workflow_dispatch `android-e2e` job (validated on a real arm64
|
|
# device). Do not make it a hard PR gate on unproven-on-emulator
|
|
# evidence; flip to a hard gate once an emulator-green run is attached.
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:native-plugin-view || true
|
|
|
|
# Real OS-level touch swipe on the chat-sheet grabber (#9943): proves
|
|
# the home→launcher pager gesture fires with real Android touch input
|
|
# (pointerMouseCount===0), not desktop mouse emulation. Signal-only on
|
|
# the PR lane for the same reason as above — flip to a hard gate once
|
|
# emulator-green evidence is attached.
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
ELIZA_ANDROID_CLEAR_APP_DATA=1 \
|
|
bun run --cwd packages/app test:e2e:android:touch-gesture || true
|
|
|
|
# View-runtime telemetry soak (#10196): activates every registered
|
|
# view through the real eliza:navigate:view channel and asserts
|
|
# bounded render/heap/cache behavior. Signal-only on the PR lane.
|
|
ELIZA_ANDROID_BACKEND=host \
|
|
ELIZA_ANDROID_REQUIRE_AGENT=1 \
|
|
bun run --cwd packages/app test:e2e:android:view-runtime-soak || true
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: android-pr-device-smoke-artifacts
|
|
path: |
|
|
packages/app/test-results/**
|
|
packages/app/playwright-report/**
|
|
if-no-files-found: ignore
|
|
|
|
# On-device instrumented (androidTest) gate for the native Capacitor plugins
|
|
# (issue #9967 "wire into CI" + issue #9943 "on-device never gates a PR"). Until
|
|
# this lane, the native-plugin Kotlin ran on no test, on no device in CI — the
|
|
# only coverage was a mocked Capacitor bridge in desktop Chromium. This builds
|
|
# every native plugin's androidTest, boots a KVM emulator, and runs the real
|
|
# on-device reads (RoleManager / AudioManager / WifiManager / TelecomManager /
|
|
# CameraManager / ContactsProvider / content://sms / UsageStatsManager /
|
|
# FusedLocationProvider) via `connectedDebugAndroidTest` — gradle fails the job
|
|
# on any test failure. Label-gated (`ci:device`) or dispatch, like the smokes
|
|
# above, because booting an emulator is heavy.
|
|
native-plugin-androidtest:
|
|
if: >-
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(github.event_name == 'pull_request' &&
|
|
contains(github.event.pull_request.labels.*.name, 'ci:device'))
|
|
runs-on: ${{ fromJSON(vars.HETZNER_FLEET_ONLINE == 'false' && '["ubuntu-24.04"]' || '["self-hosted","hetzner-robot"]') }}
|
|
timeout-minutes: 60
|
|
env:
|
|
ELIZA_BUN_RISCV64_OPTIONAL: "1"
|
|
ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB: "1"
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
|
|
- uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: "1.3.14" # pinned: repo packageManager is bun@1.4.0, an unpublished version (GH+npm 404) that makes bare setup-bun fail here; match .github/ci-bun-version.json (#15071)
|
|
- uses: actions/setup-java@v5
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21"
|
|
- uses: android-actions/setup-android@v4
|
|
|
|
- name: Install workspace
|
|
run: bun install
|
|
|
|
# Sync the Capacitor Android project so capacitor.settings.gradle points at
|
|
# this install's @capacitor/android and every plugin module is wired.
|
|
- name: Prepare Android platform
|
|
run: bun run --cwd packages/app build:android
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules && sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Native-plugin androidTest on emulator
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: "34"
|
|
arch: x86_64
|
|
ram-size: 4096M
|
|
emulator-options: -no-window -no-snapshot -no-boot-anim -gpu swiftshader_indirect
|
|
script: |
|
|
set -euo pipefail
|
|
adb wait-for-device
|
|
ANDROID_DIR="$GITHUB_WORKSPACE/packages/app-core/platforms/android"
|
|
GRADLEW="$ANDROID_DIR/gradlew"
|
|
chmod +x "$GRADLEW"
|
|
|
|
# Device-state orchestration (survives the test-APK install):
|
|
# - messages: inject a marker SMS so the SMS read-back asserts positively
|
|
# (it Assume-skips without the marker, so this is what makes it green).
|
|
# - location: high-accuracy mode (the fused fetch still Assume-skips on a
|
|
# headless GNSS HAL; the priority-mapping assertion runs regardless).
|
|
adb emu sms send 15558675309 "probe Eliza-9967-SMS-roundtrip ci" || true
|
|
adb shell settings put secure location_mode 3 || true
|
|
|
|
# The gate: build + install + run every native-plugin androidTest on the
|
|
# emulator in one invocation. gradle fails the job on any test failure.
|
|
# `:app:connectedDebugAndroidTest` adds the app-level assistant/IME/
|
|
# assist-surface instrumented tests (#13581) — the RETAIL-path
|
|
# ElizaAssistantSurfaceInstrumentedTest runs on the plain debug APK
|
|
# (not assumeSystemEliza-gated), so this app run is not vacuously
|
|
# green off-AOSP.
|
|
"$GRADLEW" -p "$ANDROID_DIR" --no-daemon \
|
|
:app:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-system:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-wifi:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-phone:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-camera:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-contacts:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-messages:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-mobile-signals:connectedDebugAndroidTest \
|
|
:elizaos-capacitor-location:connectedDebugAndroidTest
|
|
|
|
# mobile-signals PACKAGE_USAGE_STATS is special-access (no runtime dialog)
|
|
# and the grant does NOT survive the gate's reinstall, so its usage reads
|
|
# Assume-skip above. For a positive on-device read: re-install the test APK
|
|
# the gate built (the gate uninstalls it after running), grant
|
|
# GET_USAGE_STATS, and re-run the usage tests via am instrument. Require a
|
|
# clean `OK (N tests)` completion and fail the job on any error/failure.
|
|
MS_APK="$GITHUB_WORKSPACE/plugins/plugin-native-mobile-signals/android/build/outputs/apk/androidTest/debug/elizaos-capacitor-mobile-signals-debug-androidTest.apk"
|
|
adb install -r -t "$MS_APK"
|
|
adb shell appops set ai.eliza.plugins.mobilesignals.test android:get_usage_stats allow
|
|
MS_OUT="$(adb shell am instrument -w -r \
|
|
-e class ai.eliza.plugins.mobilesignals.UsageStatsReaderInstrumentedTest \
|
|
ai.eliza.plugins.mobilesignals.test/androidx.test.runner.AndroidJUnitRunner 2>&1)"
|
|
printf '%s\n' "$MS_OUT"
|
|
if printf '%s\n' "$MS_OUT" | grep -qE 'FAILURES!!!|INSTRUMENTATION_FAILED|INSTRUMENTATION_RESULT: shortMsg'; then
|
|
echo "mobile-signals UsageStats assertions failed under granted PACKAGE_USAGE_STATS"
|
|
exit 1
|
|
fi
|
|
printf '%s\n' "$MS_OUT" | grep -qE 'OK \([0-9]+ test' || {
|
|
echo "mobile-signals UsageStats run did not complete cleanly"
|
|
exit 1
|
|
}
|
|
|
|
# Assistant-role / voice-IME / assist-key adb verification lane
|
|
# (#13581). The gradle gate above uninstalled its test APKs, so
|
|
# install the app APK the Capacitor build produced, then drive the
|
|
# RUNTIME surface: re-apply the role + IME that `adb install -r`
|
|
# clears, assert the secure settings, fire `cmd voiceinteraction
|
|
# show` / `KEYCODE_ASSIST` / the IME deep-link, and assert they land
|
|
# in MainActivity. `--require-device` makes a missing device fatal
|
|
# (it is present here, so this only guards against a silent skip).
|
|
# `--require-engine` is intentionally NOT passed: the emulator carries
|
|
# no on-device ASR engine, so the IME ASR round-trip legitimately
|
|
# resolves to the designed ENGINE_OFF state — the lane asserts that
|
|
# state rather than requiring a transcript. All other assertions
|
|
# (registration, role, IME selection, deep-link landing) still gate
|
|
# the job.
|
|
APP_APK="$GITHUB_WORKSPACE/packages/app-core/platforms/android/app/build/outputs/apk/debug/app-debug.apk"
|
|
if [ -f "$APP_APK" ]; then
|
|
adb install -r -t "$APP_APK"
|
|
else
|
|
echo "app-debug.apk not found at $APP_APK — build:android should have produced it"
|
|
exit 1
|
|
fi
|
|
ASSISTANT_ARTIFACT_DIR="$GITHUB_WORKSPACE/packages/app/test-results/android-assistant-verify"
|
|
mkdir -p "$ASSISTANT_ARTIFACT_DIR"
|
|
VERIFY_STATUS=0
|
|
node "$GITHUB_WORKSPACE/packages/app/scripts/android-assistant-verify.mjs" \
|
|
--require-device --json \
|
|
| tee "$ASSISTANT_ARTIFACT_DIR/verdict.json" || VERIFY_STATUS=$?
|
|
adb shell settings get secure voice_interaction_service \
|
|
> "$ASSISTANT_ARTIFACT_DIR/voice_interaction_service.txt" || true
|
|
adb shell settings get secure default_input_method \
|
|
> "$ASSISTANT_ARTIFACT_DIR/default_input_method.txt" || true
|
|
adb shell ime list -s \
|
|
> "$ASSISTANT_ARTIFACT_DIR/enabled_imes.txt" || true
|
|
adb shell cmd role holders android.app.role.ASSISTANT \
|
|
> "$ASSISTANT_ARTIFACT_DIR/assistant_role_holders.txt" || true
|
|
adb shell dumpsys activity activities \
|
|
> "$ASSISTANT_ARTIFACT_DIR/dumpsys-activity.txt" || true
|
|
adb logcat -d -v brief \
|
|
> "$ASSISTANT_ARTIFACT_DIR/logcat.txt" || true
|
|
exit "$VERIFY_STATUS"
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: native-plugin-androidtest-reports
|
|
path: |
|
|
plugins/plugin-native-*/android/build/reports/androidTests/**
|
|
plugins/plugin-native-*/android/build/outputs/androidTest-results/**
|
|
packages/app/test-results/android-assistant-verify/**
|
|
if-no-files-found: ignore
|