[Feature] Reusable CI harness / GitHub Action to boot a Beetroot instance and drive it (adb + Frida + LSPosed) on GitHub runners

Open
#28 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
38/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
android, docker, docker-compose, github-actions, linux, python

Research direction

Start with docs/design/stealth-posture.md §7 and issue #6, then inspect the existing Beetroot create, up, doctor, and destroy entry points. Verify the redroid boot requirements on the intended GitHub or self-hosted runner before designing the reusable action. Done means an external workflow can boot an x86_64 instance, use adb and Frida, collect logs, and always tear it down, with the runner findings documented and linked from #6.

Written by the indexing model from the issue text.

Description

ci enhancement

Summary

Make Beetroot consumable as a reusable CI harness — ideally a published composite/Docker GitHub Action (and/or a documented workflow job) — that boots a Beetroot instance on a GitHub-hosted runner, waits for it to be ready, exposes adb + Frida to subsequent steps, and tears it down. The goal is that a downstream repo can stand up an on-device e2e against a real rooted Android-14 phone without hand-rolling emulator plumbing.

Why (the concrete driver)

The Rosetta project wants exactly this. In Xiddoc/rosetta-xposed#25 (load the real DexKit native .so in the emulator e2e), the owner commented:

stand this dynamic-path e2e up on Beetroot, our own GitHub-Actions emulator framework, rather than hand-rolling more android-e2e.yml emulator plumbing. Beetroot owns the "boot an emulator on a GH runner and drive it" layer; this issue's actual work … then runs inside a Beetroot-provisioned emulator job.

Today rosetta-xposed's .github/workflows/android-e2e.yml hand-rolls reactivecircus/android-emulator-runner + LSPatch. That workflow's own comment explains why it avoids the path Beetroot is built for:

Why LSPatch and not LSPosed: running LSPosed in CI needs Magisk root + a Zygisk module on the AVD (no turnkey action, very flaky).

Beetroot is the turnkey "Magisk root + Zygisk" box. A reusable Beetroot CI harness is the missing piece that lets rosetta retire the bespoke plumbing and run against real root.

Known blocker (be honest up front)

Beetroot boots redroid, and redroid-on-GitHub-hosted-runners is not yet solved. This is already flagged in-repo:

  • docs/design/stealth-posture.md §7 PR7: "Complexity: L (mostly: getting redroid to boot in GHA is the hard part)."
  • #6 (Stealth PR7: CI fingerprint audit) hits the same wall: "The hard part is getting redroid to boot inside a GitHub Actions runner … the real redroid base can't be pulled/booted on GHA runners."
  • #21 lists "booting real redroid in CI" as out of scope, "blocked on a KVM-capable runner story."

So this issue is partly the boot story itself (redroid needs binder/ashmem kernel modules; GitHub's ubuntu-latest enables nested KVM but the redroid kernel-module prerequisites are the real question), and partly the ergonomics on top once boot works. It shares the redroid-boot blocker with #6 — solving it here unblocks both. A self-hosted/KVM-capable runner is the likely fallback if GitHub-hosted can't carry it.

Proposed shape

  • A composite action (e.g. Xiddoc/beetroot/.github/actions/boot@v0) or a documented reusable workflow that:
    • builds/pulls the base image, beetroot create + beetroot up a throwaway instance, and beetroot doctor-waits for boot-complete;
    • surfaces the instance's adb serial and Frida port to later steps;
    • streams docker compose logs as a job artifact and beetroot destroys on teardown (always()).
  • Pin to an x86_64 image so a committed lib/x86_64/*.so matches the runtime ABI (a hard requirement for the rosetta-xposed DexKit case).
  • Document the runner requirements (KVM / kernel modules / self-hosted fallback) discovered while solving the boot story.

Acceptance

  • A documented, copy-pasteable way for an external repo's workflow to boot a Beetroot instance, run adb/Frida steps against it, and tear down — green on whatever runner class we land on.
  • The redroid-boot-in-GHA findings folded back into docs/ and cross-linked from #6.

Downstream consumers (cross-repo)

  • Xiddoc/rosetta-xposed#25 — real DexKit native load via a true LSPosed install (see the companion Beetroot issue for the LSPosed recipe).
  • Xiddoc/rosetta-frida (#23) — Frida self-healing / on-device parity; Beetroot already ships Frida per-instance, so this is the natural home for a Frida-side on-device e2e (rosetta-frida currently has no device harness at all).
  • Xiddoc/rosetta-maps (#41, #34, trust-ladder) — a "trusted runner" / device-telemetry tier that proves a generated map actually resolves against the real app on-device (the off-public-CI correctness tier their AGENTS.md Hard rule 3 reserves).
Dominant language
Python
Stars
1
Forks
0
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Xiddoc/Beetroot

All issues in Xiddoc/Beetroot

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.