Native GNOME app · GTK4 · MIT

Unity builds,
one dashboard

Build Android APKs and AABs, ship iOS through a remote Mac, install on devices, run tests and watch FPS — from one native Linux app. And when an agent needs a build, ubd does the same from the terminal.

Unity Builder Dash
Projects Devices History Profiler Settings
Mac reachablessh · battery 84%
Projects+1 buildScripts onlyBuild all
MyGame
A 452 · Meta Quest
Android
ARViewer
iOS 325 / A 452
AndroidiOS
Unity build · IL2CPPETA 6:10
ArcadeRunner
A 211 · OpenXR
Android
build log· search · level · wrap0:00
ARViewer · iOS 326 installed on iPhone
10–15×faster iterations with Scripts Only — C# patched into the existing player
APK · AAB · iOSsideload builds, Google Play bundles and Xcode projects
6 metricslive FPS, frame time, GPU, RAM, CPU and temperature — Quest-aware
1 buildper machine, enforced — no OOM from parallel Unity runs
iOS remote build

iOS builds, without leaving Linux

Press iOS and the dashboard runs the whole chain over SSH, streaming the Mac's log back into the same log view.

Unity

-executeMethod

BuildScript.BuildiOS exports the Xcode project, bumping the iOS build number.

zip

iOS.zip

The export is packed into one archive for a fast transfer.

scp → Mac

ssh

Uploaded to the work folder, unpacked, pod install, WidgetKit target added.

xcodebuild

signed build

Built with your team ID and bundle IDs from config.json.

iPhone

install

Installed on the device by name — ready to open when you look up.

fullxcodebuild_onlywithout_xcodeupdatePodstop
ubd CLI

The same build, from one command

If the dashboard is open, ubd hands the build to it over a local API — it shows up in the window like any other. If not, a headless runner builds with the same code.

  • --wait streams the log and exits with the build result.
  • --install <device> — adb serial or iOS device name, installed right after the build.
  • --scripts-only, --aab, --no-increment — the same toggles as the toolbar.
  • --dry-run prints the plan and who would run it. Every command takes --json.
~/projects/unity-builder-dash
Features

Everything between “build” and “it’s on my phone”

Five pages — Projects, Devices, History, Profiler, Settings — in a native Adwaita window that follows your system theme.

Scripts Only builds

Rebuild only the C# assemblies and patch them into the existing player — no IL2CPP, asset import, shader compile or Gradle/Xcode pass.

full9:40
scripts only0:45

EditMode & PlayMode tests

Unity Test Runner in batchmode with a live counter and ETA. NUnit XML parsed into pass / fail / skip, with failed tests and their messages.

PlayMode212 passed · 3 failed · 6 skipped
✕ PlacementTests.SnapToSurface

Devices

USB and Wi-Fi adb devices, apps with permissions, inline logcat, scrcpy cast, screenshots, drag-and-drop APK install.

Quest 3R58M123ABC
Pixel 8192.168.0.42
iPhone 15xcodebuild

Live profiler

Real-time charts for FPS, frame time, GPU, RAM (PSS), CPU and temperature. On Meta Quest it reads the VrApi stream: GPU/CPU levels, stale frames, FFR, app GPU time, SoC temperature.

FPS72
GPU61%
SoC °C38.4

History

Every build and test run on charts — duration and APK size over time, pass/fail per run — filterable by project, with the full log one click away.

iOS widgets

The WidgetKit extension target is added to the Unity-exported project on the Mac, with your bundle, team and App Group IDs.

pod install+ widgetxcodebuild

AAB for Google Play

Context menu → Build AAB. Android and iOS build numbers increment independently, both shown on the project row.

.aab.apkiOS 325 / A 452

Project health

Version, Cloud ID, build scenes, errors and git status at a glance. Clean Build drops Library/Bee from the context menu.

● scenes 4● git clean● 2 warnings

Upload & notify

FTP upload per project with a rename pattern, deploy via APK Dash, and a desktop notification when the build lands.

ftpapp-{version}.apknotify
For agents

Built to be driven by an agent

A CLI with exit codes and --json, and a localhost HTTP API on 127.0.0.1:47816 that describes itself at /commands.json. Full rules in AGENTS.md.

# projects, state and logs
./ubd projects
./ubd status --json
./ubd log --tail 50          # or -f to follow
./ubd history 10
./ubd devices
./ubd ios-status            # Mac reachable over SSH? battery?

# builds
./ubd build ARViewer ios --dry-run
./ubd build ARViewer ios --wait
./ubd build MyGame android --scripts-only --install R58M123ABC
./ubd build ARViewer ios --ios-action xcode
./ubd cancel
GET  /health          → {ok, app, owner: gui|headless, pid}
GET  /commands.json   → command manifest for agents
GET  /status · /projects · /log?tail=N · /history?n=N
POST /build           {project, target, scripts_only?, aab?, increment?, install?, ios_action?}
                      → 200 {ok, log_path, status} · 400 bad request · 409 busy
POST /cancel

# port: agent_api_port in config.json or UBD_API_PORT · localhost only
0ok — with --wait, the build succeeded
1build failed or was cancelled; ios-status: Mac unreachable
2bad arguments, unknown project or target
3busy — a Unity build is already running
4GUI is running without the agent API (old version) — restart it
5API error

One Unity build per machine

ubd build refuses with exit code 3 while the dashboard or a headless runner is building, while any Unity -batchmode process runs, or while the target project is open in the Editor. Agents wait with ubd status instead of running out of memory.

Install

Clone, configure, build

Python 3.10+, GTK4 and Libadwaita, a Unity Editor with Android/iOS support, and adb. On first launch Settings opens and finds your Unity Editor.

# Arch:   sudo pacman -S python-gobject gtk4 libadwaita android-tools
# Ubuntu: sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 adb
$ git clone https://github.com/PavelKhabusov/Unity-Builder-Dash.git
$ cd Unity-Builder-Dash
$ cp config.example.json config.json
$ ./install.sh   # GNOME launcher entry — or just ./build.py
More by me