This commit is contained in:
eric
2026-04-09 21:01:21 -05:00
parent f8b84d84c9
commit 232d80b0ae
9 changed files with 2011 additions and 92 deletions

280
android13/README.md Normal file
View File

@@ -0,0 +1,280 @@
# redroid 13 Control Kit
This folder contains the local control entrypoint for the remote redroid 13 environment.
The goal is to keep one script and one workflow that can:
- build the image on the server
- launch one or many redroid instances
- bootstrap Magisk and Google framework settings
- check whether a running instance is actually usable
- expose a web UI for Docker management
## Architecture
The repository is split into two layers:
```mermaid
flowchart LR
A["Local workstation<br/>Windows / Git Bash"] --> B["redroid13.sh<br/>Paramiko over SSH"]
B --> C["Remote host<br/>/home/eric/redroid-android13"]
C --> D["Docker / Podman"]
D --> E["redroid container"]
E --> F["Android 13 system image"]
F --> G["Google framework"]
F --> H["Magisk / root"]
B --> I["Portainer web panel"]
```
### Local side
`redroid13.sh` is the only file you usually run from this folder. It:
- asks for the SSH password once
- connects to the server with `paramiko`
- executes build, launch, bootstrap, and health-check commands remotely
- resolves the best image for the server architecture
### Remote side
The server repo, `/home/eric/redroid-android13`, contains the actual Android build and bootstrap code:
- `redroid.py` builds the image layers
- `run_redroid.py` launches the container
- `bootstrap_redroid.py` applies the post-boot hardening
- `stuff/magisk.py` packages Magisk
- `stuff/mindthegapps.py` packages Google apps/framework
## Compatibility Model
The script is designed to work with both x86_64 and arm64 hosts.
### Image selection
The script resolves the image automatically unless you override it.
- x86_64 host: `redroid/redroid:13.0.0_mindthegapps_houdini_magisk`
- arm64 host: `redroid/redroid:13.0.0_mindthegapps_magisk`
You can still override the image explicitly with `REDROID_IMAGE`.
If your arm64 build uses a different tag, set `REDROID_IMAGE_ARM64` to match it.
### Runtime bridge
`run_redroid.py` already resolves bridge mode from the image tag when `--bridge auto` is used.
- `houdini` images use native bridge translation for x86 apps on x86 hosts
- arm64 images can stay on the default bridge path
### Root handling
`bootstrap_redroid.py` now probes root using both of the common Magisk styles:
- `su root id`
- `su 0 id`
That avoids the old `su -mm -c` mismatch and keeps Magisk-compatible bootstrapping stable across the current image variants.
## What The Script Does
The shell entrypoint supports:
- building the Android 13 image
- starting one instance
- starting multiple instances
- bootstrapping a single container
- bootstrapping all running redroid containers
- listing containers
- checking a running instance
- starting Portainer
- showing runtime info
It also resolves:
- the server architecture
- the appropriate image tag
- the right ADB / Magisk bootstrap path
## Prerequisites
Local machine:
- Bash
- Python 3
- `paramiko` installed for Python 3
Remote server:
- Docker or Podman
- access to `/home/eric/redroid-android13`
- working ADB server
- a redroid-compatible Linux host
## Quick Start
Run the script from this folder:
```bash
./redroid13.sh
```
The menu is interactive and covers the common workflow:
1. build the image
2. start one instance
3. start a batch of instances
4. bootstrap an instance
5. list containers
6. check a running instance
7. start Portainer
8. stop an instance
9. bootstrap all redroid containers
10. show runtime info
11. exit
## Non-Interactive Usage
The same script also supports command-style usage:
```bash
./redroid13.sh build
./redroid13.sh start
./redroid13.sh batch
./redroid13.sh bootstrap redroid13-1
./redroid13.sh bootstrap-all
./redroid13.sh list
./redroid13.sh redroid-list
./redroid13.sh check
./redroid13.sh portainer
./redroid13.sh info
./redroid13.sh stop redroid13-1
```
## Recommended Defaults
These defaults are tuned for a stable Android 13 desktop-style setup:
- `REDROID_GPU_MODE=guest`
- `REDROID_ADB_INSECURE=1`
- `REDROID_DEVICE_PROFILE=pixel-7-pro`
- `REDROID_ROOT_TIMEOUT=60`
- `REDROID_BOOT_TIMEOUT=120`
- `REDROID_WAIT_TCP=120`
That combination prioritizes boot reliability over host-GPU speed.
## Environment Variables
You can override the behavior without editing the script.
| Variable | Purpose | Default |
| --- | --- | --- |
| `REDROID_SSH_HOST` | Remote server address | `192.168.2.179` |
| `REDROID_SSH_USER` | SSH user | `eric` |
| `REDROID_REMOTE_REPO` | Remote repo path | `/home/eric/redroid-android13` |
| `REDROID_IMAGE` | Force a specific image | auto-select by architecture |
| `REDROID_IMAGE_X86` | x86_64 image | `redroid/redroid:13.0.0_mindthegapps_houdini_magisk` |
| `REDROID_IMAGE_ARM64` | arm64 image | `redroid/redroid:13.0.0_mindthegapps_magisk` |
| `REDROID_ARCH_PROFILE` | `auto`, `x86_64`, or `arm64` | `auto` |
| `REDROID_DEVICE_PROFILE` | Device fingerprint preset | `pixel-7-pro` |
| `REDROID_GPU_MODE` | `guest`, `host`, or `auto` | `guest` |
| `REDROID_ADB_INSECURE` | Inject insecure adb boot props | `1` |
| `REDROID_BOOT_TIMEOUT` | Seconds to wait for Android boot | `120` |
| `REDROID_ROOT_TIMEOUT` | Seconds to wait for Magisk root | `60` |
| `REDROID_WAIT_TCP` | Seconds to wait for ADB TCP | `120` |
| `REDROID_DEFAULT_PORT` | First ADB port | `5570` |
| `REDROID_PORT_STEP` | Port step in batch mode | `1` |
| `REDROID_INSTANCE_PREFIX` | Instance name prefix | `redroid13` |
| `REDROID_DEFAULT_COUNT` | Batch size prompt default | `3` |
| `REDROID_PORTAINER_PORT` | Portainer port | `9000` |
| `REDROID_DATA_PREFIX` | Host data directory prefix | `/home/eric/data-redroid13` |
## Multi-Instance Layout
The script uses one host data directory per instance:
```text
/home/eric/data-redroid13-redroid13-1
/home/eric/data-redroid13-redroid13-2
/home/eric/data-redroid13-redroid13-3
```
Ports are assigned sequentially:
- `5570`
- `5571`
- `5572`
This keeps each container isolated and makes it easy to reboot or replace a single instance.
## Bootstrap Flow
The bootstrap phase does the important post-boot cleanup:
- waits for Android boot completion
- waits for Magisk root when root-only actions are required
- sets provisioning flags
- disables setup wizard if the package exists
- sets mock location for shell
- applies the selected device profile through `resetprop`
- applies proxy settings
- installs the optional extreme script when requested
That bootstrap logic is the part that keeps Google Play and Magisk behavior stable after first boot.
## Portainer
The script can start Portainer on the remote host.
Once it is running:
- open `http://<server-ip>:9000`
- use it to inspect containers, logs, volumes, and restart policies
## Troubleshooting
### ADB connects but shell fails
Check whether the instance actually reached a usable boot state:
```bash
./redroid13.sh check
```
If `sys.boot_completed` is not `1`, the container may still be booting or the guest may have stalled.
### Magisk root is unavailable
The bootstrap logic now retries root with the currently supported `su` syntax.
If you still do not see root:
- check the container logs on the remote host
- verify the image tag you selected
- make sure the container was started from the current repository revision
### Google Play login looks broken
That usually means one of these is still wrong:
- device fingerprint
- setup wizard state
- provisioning state
- Google Play Services package state
Use `./redroid13.sh check` to inspect all of those in one shot.
### Host GPU causes boot issues
If a container never becomes reachable over ADB, switch back to guest GPU mode:
```bash
REDROID_GPU_MODE=guest ./redroid13.sh start
```
Host GPU is faster when it works, but guest GPU is the safe default for headless servers.
## Notes
- This folder is a control layer, not the Android build itself.
- The actual server-side fixes live in the remote repository at `/home/eric/redroid-android13`.
- The goal is stability first: Magisk root, Google framework, and repeatable multi-instance startup.

View File

@@ -0,0 +1,143 @@
from pathlib import Path
path = Path('/home/eric/redroid-android13/bootstrap_redroid.py')
text = path.read_text()
old = ''' def connect(self):
if self.mode == "adb" and ":" in self.serial:
run(["adb", "connect", self.serial], check=False)
def has_root(self):
if self.mode == "container":
return True
if not self._root_checked or not self._root_available:
result = adb(
self.serial,
["shell", "su", "-mm", "-c", "id"],
check=False,
capture_output=True,
)
self._root_available = result.returncode == 0 and "uid=0(" in result.stdout
self._root_checked = self._root_available
return self._root_available
def wait_for_root(self, timeout):
if self.mode == "container":
return True
deadline = time.time() + timeout
while time.time() < deadline:
if self.has_root():
return True
time.sleep(2)
return False
def exec_shell(self, command, check=True, capture_output=False, root=False):
if self.mode == "container":
return run(
[
self.container_runtime,
"exec",
self.container_name,
"/system/bin/sh",
"-c",
command,
],
check=check,
capture_output=capture_output,
)
if root and self.has_root():
return adb(
self.serial,
["shell", "su", "-mm", "-c", command],
check=check,
capture_output=capture_output,
)
return adb(
self.serial,
["shell", command],
check=check,
capture_output=capture_output,
)
'''
new = ''' def connect(self):
if self.mode == "adb" and ":" in self.serial:
run(["adb", "connect", self.serial], check=False)
def _root_probe_args(self):
return [
["shell", "su", "root", "id"],
["shell", "su", "0", "id"],
]
def _root_shell_args(self, command):
return [
["shell", "su", "root", "sh", "-c", command],
["shell", "su", "0", "sh", "-c", command],
]
def has_root(self):
if self.mode == "container":
return True
if not self._root_checked or not self._root_available:
for args in self._root_probe_args():
result = adb(
self.serial,
args,
check=False,
capture_output=True,
)
if result.returncode == 0 and "uid=0(" in result.stdout:
self._root_available = True
self._root_checked = True
return True
self._root_available = False
self._root_checked = False
return self._root_available
def wait_for_root(self, timeout):
if self.mode == "container":
return True
deadline = time.time() + timeout
while time.time() < deadline:
if self.has_root():
return True
time.sleep(2)
return False
def exec_shell(self, command, check=True, capture_output=False, root=False):
if self.mode == "container":
return run(
[
self.container_runtime,
"exec",
self.container_name,
"/system/bin/sh",
"-c",
command,
],
check=check,
capture_output=capture_output,
)
if root and self.has_root():
last_result = None
for args in self._root_shell_args(command):
result = adb(
self.serial,
args,
check=False,
capture_output=capture_output,
)
last_result = result
if result.returncode == 0:
return result
if check and last_result is not None:
last_result.check_returncode()
return last_result
return adb(
self.serial,
["shell", command],
check=check,
capture_output=capture_output,
)
'''
if old not in text:
raise SystemExit('expected bootstrap_redroid.py block not found')
path.write_text(text.replace(old, new))

View File

@@ -0,0 +1,20 @@
from pathlib import Path
path = Path('/home/eric/redroid-android13/bootstrap_redroid.py')
text = path.read_text()
old = ''' try_shell(backend, "pm disable-user --user 0 com.android.setupwizard")
try_shell(backend, "pm disable-user --user 0 com.google.android.setupwizard")
try_shell(backend, "appops set com.android.shell android:mock_location allow")
'''
new = ''' for package in ("com.android.setupwizard", "com.google.android.setupwizard"):
packages = backend.exec_shell(
"pm list packages --user 0",
check=False,
capture_output=True,
).stdout
if package in packages:
try_shell(backend, "pm disable-user --user 0 {}".format(shell_quote(package)))
try_shell(backend, "appops set com.android.shell android:mock_location allow")
'''
if old not in text:
raise SystemExit('expected setupwizard block not found')
path.write_text(text.replace(old, new))

427
android13/redroid13.sh Normal file
View File

@@ -0,0 +1,427 @@
#!/usr/bin/env bash
set -euo pipefail
SSH_HOST="${REDROID_SSH_HOST:-192.168.2.179}"
SSH_USER="${REDROID_SSH_USER:-eric}"
REMOTE_REPO="${REDROID_REMOTE_REPO:-/home/eric/redroid-android13}"
REDROID_IMAGE_OVERRIDE="${REDROID_IMAGE:-}"
REDROID_IMAGE_X86="${REDROID_IMAGE_X86:-redroid/redroid:13.0.0_mindthegapps_houdini_magisk}"
REDROID_IMAGE_ARM64="${REDROID_IMAGE_ARM64:-redroid/redroid:13.0.0_mindthegapps_magisk}"
REDROID_ARCH_PROFILE="${REDROID_ARCH_PROFILE:-auto}"
DEVICE_PROFILE="${REDROID_DEVICE_PROFILE:-pixel-7-pro}"
PORTAINER_PORT="${REDROID_PORTAINER_PORT:-9000}"
DEFAULT_PORT="${REDROID_DEFAULT_PORT:-5570}"
DATA_PREFIX="${REDROID_DATA_PREFIX:-/home/eric/data-redroid13}"
PORT_STEP="${REDROID_PORT_STEP:-1}"
INSTANCE_PREFIX="${REDROID_INSTANCE_PREFIX:-redroid13}"
DEFAULT_COUNT="${REDROID_DEFAULT_COUNT:-3}"
GPU_MODE="${REDROID_GPU_MODE:-guest}"
ADB_INSECURE="${REDROID_ADB_INSECURE:-1}"
BOOT_TIMEOUT="${REDROID_BOOT_TIMEOUT:-120}"
ROOT_TIMEOUT="${REDROID_ROOT_TIMEOUT:-60}"
WAIT_TCP="${REDROID_WAIT_TCP:-120}"
RESOLVED_IMAGE=""
RESOLVED_ARCH=""
if ! command -v python3 >/dev/null 2>&1; then
echo "python3 is required." >&2
exit 1
fi
if ! python3 - <<'PY' >/dev/null 2>&1
import importlib.util
raise SystemExit(0 if importlib.util.find_spec("paramiko") else 1)
PY
then
echo "python3 paramiko is required." >&2
exit 1
fi
read -r -s -p "SSH password for ${SSH_USER}@${SSH_HOST}: " SSH_PASSWORD
echo
sq() {
printf '%q' "$1"
}
remote_exec() {
local remote_cmd="$1"
SSH_HOST="$SSH_HOST" \
SSH_USER="$SSH_USER" \
SSH_PASSWORD="$SSH_PASSWORD" \
SSH_CMD="$remote_cmd" \
python3 - <<'PY'
import os
import sys
import paramiko
host = os.environ["SSH_HOST"]
user = os.environ["SSH_USER"]
password = os.environ["SSH_PASSWORD"]
cmd = os.environ["SSH_CMD"]
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
hostname=host,
username=user,
password=password,
timeout=20,
banner_timeout=20,
auth_timeout=20,
)
stdin, stdout, stderr = client.exec_command(cmd, get_pty=True)
out = stdout.read().decode(errors="replace")
err = stderr.read().decode(errors="replace")
if out:
sys.stdout.write(out)
if err:
sys.stderr.write(err)
status = stdout.channel.recv_exit_status()
client.close()
sys.exit(status)
PY
}
remote_repo_cmd() {
local inner="$1"
remote_exec "cd $(sq "$REMOTE_REPO") && ${inner}"
}
normalize_arch() {
case "${1,,}" in
x86_64|amd64)
printf '%s' "x86_64"
;;
aarch64|arm64)
printf '%s' "arm64"
;;
*)
printf '%s' "unknown"
;;
esac
}
detect_remote_arch() {
remote_exec "uname -m | tr -d '\r'"
}
resolve_image() {
if [[ -n "$RESOLVED_IMAGE" ]]; then
printf '%s' "$RESOLVED_IMAGE"
return 0
fi
if [[ -n "$REDROID_IMAGE_OVERRIDE" ]]; then
RESOLVED_IMAGE="$REDROID_IMAGE_OVERRIDE"
RESOLVED_ARCH="custom"
printf '%s' "$RESOLVED_IMAGE"
return 0
fi
local arch="${REDROID_ARCH_PROFILE}"
if [[ "$arch" == "auto" ]]; then
arch="$(normalize_arch "$(detect_remote_arch)")"
else
arch="$(normalize_arch "$arch")"
fi
case "$arch" in
x86_64)
RESOLVED_IMAGE="$REDROID_IMAGE_X86"
RESOLVED_ARCH="x86_64"
;;
arm64)
RESOLVED_IMAGE="$REDROID_IMAGE_ARM64"
RESOLVED_ARCH="arm64"
;;
*)
RESOLVED_IMAGE="$REDROID_IMAGE_X86"
RESOLVED_ARCH="x86_64"
;;
esac
printf '%s' "$RESOLVED_IMAGE"
}
resolve_arch() {
if [[ -z "$RESOLVED_IMAGE" ]]; then
resolve_image >/dev/null
fi
printf '%s' "${RESOLVED_ARCH:-x86_64}"
}
remote_docker_ps() {
remote_exec "docker ps --format 'table {{.Names}}\\t{{.Image}}\\t{{.Ports}}\\t{{.Status}}'"
}
remote_redroid_ps() {
local image
image="$(resolve_image)"
remote_exec "docker ps --format '{{.Names}} {{.Image}} {{.Ports}} {{.Status}}' | awk -v image=$(sq "$image") '\$2 == image || index(\$2, \"redroid/redroid:\") == 1 { print }'"
}
remote_redroid_names() {
local image
image="$(resolve_image)"
remote_exec "docker ps --format '{{.Names}} {{.Image}}' | awk -v image=$(sq "$image") '\$2 == image || index(\$2, \"redroid/redroid:\") == 1 {print \$1}'"
}
show_runtime_info() {
local image arch
image="$(resolve_image)"
arch="$(resolve_arch)"
printf 'Remote arch profile: %s\n' "$arch"
printf 'Resolved image: %s\n' "$image"
printf 'Device profile: %s\n' "$DEVICE_PROFILE"
printf 'GPU mode: %s\n' "$GPU_MODE"
printf 'ADB insecure: %s\n' "$ADB_INSECURE"
}
build_image() {
remote_repo_cmd "env -u REDROID_MAGISK_APK -u REDROID_MAGISK_URL REDROID_OFFLINE=1 REDROID_MAGISK_BOOT_COMPLETE=1 python3 redroid.py -a 13.0.0 -mtg -i -m --offline --no-compat-check"
}
make_instance_name() {
local prefix="$1"
local index="$2"
printf '%s-%s' "$prefix" "$index"
}
make_data_dir() {
local prefix="$1"
local name="$2"
printf '%s-%s' "$prefix" "$name"
}
start_single_instance() {
local name="$1"
local port="$2"
local data_dir="$3"
local image arch
local adb_arg=""
if [[ "$ADB_INSECURE" != "0" ]]; then
adb_arg="--adb-insecure"
fi
image="$(resolve_image)"
arch="$(resolve_arch)"
remote_repo_cmd "python3 run_redroid.py --image $(sq "$image") --bridge auto --device-profile $(sq "$DEVICE_PROFILE") --gpu-mode $(sq "$GPU_MODE") --data-dir $(sq "$data_dir") --port $(sq "$port") --name $(sq "$name") --replace --keep-container --wait-tcp $(sq "$WAIT_TCP") ${adb_arg}"
bootstrap_instance "$name"
echo "Local ADB connect:"
echo " adb connect ${SSH_HOST}:${port}"
echo "Resolved arch/image: ${arch} / ${image}"
}
start_instance() {
local name port data_dir
read -r -p "Instance name [${INSTANCE_PREFIX}-1]: " name
name="${name:-${INSTANCE_PREFIX}-1}"
read -r -p "ADB port [${DEFAULT_PORT}]: " port
port="${port:-$DEFAULT_PORT}"
read -r -p "Data dir [${DATA_PREFIX}-${name}]: " data_dir
data_dir="${data_dir:-${DATA_PREFIX}-${name}}"
start_single_instance "$name" "$port" "$data_dir"
}
start_batch_instances() {
local count start_port prefix data_prefix
read -r -p "How many instances [${DEFAULT_COUNT}]: " count
count="${count:-$DEFAULT_COUNT}"
read -r -p "Start ADB port [${DEFAULT_PORT}]: " start_port
start_port="${start_port:-$DEFAULT_PORT}"
read -r -p "Name prefix [${INSTANCE_PREFIX}]: " prefix
prefix="${prefix:-$INSTANCE_PREFIX}"
read -r -p "Data prefix [${DATA_PREFIX}]: " data_prefix
data_prefix="${data_prefix:-$DATA_PREFIX}"
local i name port data_dir
for ((i = 1; i <= count; i++)); do
name="$(make_instance_name "$prefix" "$i")"
port="$((start_port + (i - 1) * PORT_STEP))"
data_dir="$(make_data_dir "$data_prefix" "$name")"
echo "Starting ${name} on port ${port}..."
start_single_instance "$name" "$port" "$data_dir"
echo
done
local open_panel
read -r -p "Start Portainer web panel now? [Y/n]: " open_panel
case "${open_panel:-Y}" in
n|N|no|NO|No) ;;
*) start_portainer ;;
esac
}
bootstrap_instance() {
local name="${1:?instance name required}"
remote_repo_cmd "python3 bootstrap_redroid.py --container-name $(sq "$name") --container-runtime docker --device-profile $(sq "$DEVICE_PROFILE") --proxy-host none --timeout $(sq "$BOOT_TIMEOUT") --root-timeout $(sq "$ROOT_TIMEOUT")"
}
bootstrap_all_instances() {
local names
names="$(remote_redroid_names)"
if [[ -z "$names" ]]; then
echo "No running redroid containers found."
return 0
fi
local name
while IFS= read -r name; do
[[ -z "$name" ]] && continue
echo "Bootstrapping ${name}..."
bootstrap_instance "$name"
echo
done <<< "$names"
}
check_instance() {
local port
read -r -p "ADB port [${DEFAULT_PORT}]: " port
port="${port:-$DEFAULT_PORT}"
remote_exec "
set +e
adb connect 127.0.0.1:${port} >/dev/null 2>&1
adb -s 127.0.0.1:${port} shell getprop sys.boot_completed
adb -s 127.0.0.1:${port} shell settings get global device_provisioned
adb -s 127.0.0.1:${port} shell settings get secure user_setup_complete
adb -s 127.0.0.1:${port} shell settings get global http_proxy
adb -s 127.0.0.1:${port} shell getprop ro.build.fingerprint
adb -s 127.0.0.1:${port} shell getprop ro.product.model
adb -s 127.0.0.1:${port} shell su root id 2>/dev/null || adb -s 127.0.0.1:${port} shell su 0 id 2>/dev/null
adb -s 127.0.0.1:${port} shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER io.github.huskydg.magisk 2>/dev/null | tail -n 2 || true
adb -s 127.0.0.1:${port} shell pm path io.github.huskydg.magisk 2>/dev/null || true
adb -s 127.0.0.1:${port} shell pm list packages 2>/dev/null | grep -E 'com.google.android.gms|com.google.android.setupwizard|io.github.huskydg.magisk' | sort || true
adb -s 127.0.0.1:${port} shell dumpsys package com.google.android.gms 2>/dev/null | grep -E 'versionName=|pkg=|enabled=' | head -n 10 || true
"
}
start_portainer() {
remote_exec "
docker volume create portainer_data >/dev/null
docker rm -f portainer >/dev/null 2>&1 || true
docker run -d \
--name portainer \
--restart unless-stopped \
-p ${PORTAINER_PORT}:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
"
echo "Portainer: http://${SSH_HOST}:${PORTAINER_PORT}"
}
stop_instance() {
local name
read -r -p "Container name [redroid13-1]: " name
name="${name:-redroid13-1}"
remote_exec "docker rm -f $(sq "$name")"
}
menu() {
while true; do
cat <<EOF
redroid13 menu
1) Build Android 13 image
2) Start redroid instance
3) Start multiple instances
4) Bootstrap instance
5) List containers
6) Check instance health
7) Start Portainer web panel
8) Stop instance
9) Bootstrap all redroid containers
10) Show runtime info
11) Exit
EOF
read -r -p "Choose: " choice
case "$choice" in
1) build_image ;;
2) start_instance ;;
3) start_batch_instances ;;
4)
local name
read -r -p "Container name [${INSTANCE_PREFIX}-1]: " name
name="${name:-${INSTANCE_PREFIX}-1}"
bootstrap_instance "$name"
;;
5) remote_docker_ps ;;
6) check_instance ;;
7) start_portainer ;;
8) stop_instance ;;
9) bootstrap_all_instances ;;
10) show_runtime_info ;;
11) exit 0 ;;
*) echo "Invalid choice." ;;
esac
echo
done
}
case "${1:-}" in
build)
build_image
;;
start)
start_instance
;;
batch|multi)
start_batch_instances
;;
bootstrap)
if [[ $# -lt 2 ]]; then
echo "Usage: $0 bootstrap <container-name>" >&2
exit 1
fi
bootstrap_instance "$2"
;;
list)
remote_docker_ps
;;
redroid-list|rlist)
remote_redroid_ps
;;
info|runtime)
show_runtime_info
;;
check)
check_instance
;;
status)
check_instance
;;
portainer)
start_portainer
;;
panel|web)
start_portainer
;;
stop)
if [[ $# -lt 2 ]]; then
echo "Usage: $0 stop <container-name>" >&2
exit 1
fi
remote_exec "docker rm -f $(sq "$2")"
;;
bootstrap-all|ball)
bootstrap_all_instances
;;
""|menu)
menu
;;
*)
cat <<EOF
Usage: $0 [build|start|batch|multi|bootstrap <name>|bootstrap-all|ball|list|redroid-list|check|status|portainer|info|runtime|stop <name>]
EOF
exit 1
;;
esac