[iiab-tools] add initial release for both amd64 and arm (android) tools.
This commit is contained in:
parent
7eaf1987b8
commit
d4646d4286
|
|
@ -0,0 +1,183 @@
|
||||||
|
#!/data/data/com.termux/files/usr/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST="127.0.0.1"
|
||||||
|
CONNECT_PORT=""
|
||||||
|
TIMEOUT_SECS=180
|
||||||
|
STATE_DIR="${TMPDIR:-/data/data/com.termux/files/usr/tmp}/adbw_pair"
|
||||||
|
NOTIF_ID=9400
|
||||||
|
CLEANUP_OFFLINE=1
|
||||||
|
DEBUG=0
|
||||||
|
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
|
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1; }
|
||||||
|
die(){ echo "[!] $*" >&2; exit 1; }
|
||||||
|
dbg(){ [[ "$DEBUG" == "1" ]] && echo "[DBG] $*" >&2 || true; }
|
||||||
|
|
||||||
|
install_if_missing() {
|
||||||
|
local pkgs=()
|
||||||
|
need adb || pkgs+=("android-tools")
|
||||||
|
need termux-notification || pkgs+=("termux-api")
|
||||||
|
if ((${#pkgs[@]})); then
|
||||||
|
echo "[*] Installing: ${pkgs[*]}"
|
||||||
|
pkg install -y "${pkgs[@]}" >/dev/null
|
||||||
|
fi
|
||||||
|
need adb || die "Missing adb. Install: pkg install android-tools"
|
||||||
|
need termux-notification || die "Missing termux-notification. Install: pkg install termux-api (and install Termux:API app)"
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_notif() {
|
||||||
|
termux-notification-remove "$NOTIF_ID" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_ask_one() {
|
||||||
|
# args: key title content
|
||||||
|
local key="$1" title="$2" content="$3"
|
||||||
|
local out="$STATE_DIR/$key.txt"
|
||||||
|
rm -f "$out"
|
||||||
|
|
||||||
|
# Force a "fresh" notification so Android plays sound each time
|
||||||
|
termux-notification-remove "$NOTIF_ID" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
termux-notification \
|
||||||
|
--id "$NOTIF_ID" \
|
||||||
|
--ongoing \
|
||||||
|
--alert-once \
|
||||||
|
--priority max \
|
||||||
|
--title "$title" \
|
||||||
|
--content "$content" \
|
||||||
|
--sound \
|
||||||
|
--button1 "Answer" \
|
||||||
|
--button1-action "sh -lc 'echo \"\$REPLY\" > \"$out\"'"
|
||||||
|
|
||||||
|
local start now
|
||||||
|
start="$(date +%s)"
|
||||||
|
while true; do
|
||||||
|
if [[ -s "$out" ]]; then
|
||||||
|
tr -d '\r\n' < "$out"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
now="$(date +%s)"
|
||||||
|
if (( now - start >= TIMEOUT_SECS )); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_port_5digits() {
|
||||||
|
# args: key title
|
||||||
|
local key="$1" title="$2"
|
||||||
|
local v=""
|
||||||
|
while true; do
|
||||||
|
v="$(notify_ask_one "$key" "$title" "(5 digits)")" || return 1
|
||||||
|
v="${v//[[:space:]]/}"
|
||||||
|
[[ "$v" =~ ^[0-9]{5}$ ]] || continue
|
||||||
|
echo "$v"
|
||||||
|
return 0
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_code_6digits() {
|
||||||
|
local v=""
|
||||||
|
while true; do
|
||||||
|
v="$(notify_ask_one code "PAIR CODE" "(6 digits)")" || return 1
|
||||||
|
v="${v//[[:space:]]/}"
|
||||||
|
[[ -n "$v" ]] || continue
|
||||||
|
[[ "$v" =~ ^[0-9]+$ ]] || continue
|
||||||
|
|
||||||
|
# Allow missing leading zeros, then normalize to exactly 6 digits
|
||||||
|
if ((${#v} < 6)); then
|
||||||
|
v="$(printf "%06d" "$v")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ "$v" =~ ^[0-9]{6}$ ]] || continue
|
||||||
|
echo "$v"
|
||||||
|
return 0
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_offline_loopback() {
|
||||||
|
local keep_serial="$1" # e.g. 127.0.0.1:42371
|
||||||
|
local line serial state
|
||||||
|
while read -r line; do
|
||||||
|
serial="$(echo "$line" | awk '{print $1}')"
|
||||||
|
state="$(echo "$line" | awk '{print $2}')"
|
||||||
|
[[ "$serial" == ${HOST}:* ]] || continue
|
||||||
|
[[ "$state" == "offline" ]] || continue
|
||||||
|
[[ "$serial" == "$keep_serial" ]] && continue
|
||||||
|
adb disconnect "$serial" >/dev/null 2>&1 || true
|
||||||
|
done < <(adb devices 2>/dev/null | tail -n +2 | sed '/^\s*$/d')
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage:
|
||||||
|
$0 [--connect-port 41313] [--host 127.0.0.1] [--no-cleanup-offline] [--debug]
|
||||||
|
|
||||||
|
Prompts:
|
||||||
|
CONNECT PORT (5 digits) # only if --connect-port not provided
|
||||||
|
PAIR PORT (5 digits)
|
||||||
|
PAIR CODE (6 digits)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--host) HOST="${2:-}"; shift 2 ;;
|
||||||
|
--connect-port) CONNECT_PORT="${2:-}"; shift 2 ;;
|
||||||
|
--no-cleanup-offline) CLEANUP_OFFLINE=0; shift ;;
|
||||||
|
--debug) DEBUG=1; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) die "Unknown arg: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
install_if_missing
|
||||||
|
trap cleanup_notif EXIT
|
||||||
|
|
||||||
|
adb start-server >/dev/null 2>&1 || true
|
||||||
|
echo "[*] adb: $(adb version | head -n 1)"
|
||||||
|
|
||||||
|
if [[ -n "$CONNECT_PORT" ]]; then
|
||||||
|
CONNECT_PORT="${CONNECT_PORT//[[:space:]]/}"
|
||||||
|
[[ "$CONNECT_PORT" =~ ^[0-9]{5}$ ]] || die "Invalid --connect-port (must be 5 digits): '$CONNECT_PORT'"
|
||||||
|
else
|
||||||
|
echo "[*] Asking CONNECT PORT..."
|
||||||
|
CONNECT_PORT="$(ask_port_5digits connect "CONNECT PORT")" || die "Timeout waiting CONNECT PORT."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Asking PAIR PORT..."
|
||||||
|
local pair_port
|
||||||
|
pair_port="$(ask_port_5digits pair "PAIR PORT")" || die "Timeout waiting PAIR PORT."
|
||||||
|
|
||||||
|
echo "[*] Asking PAIR CODE..."
|
||||||
|
local code
|
||||||
|
code="$(ask_code_6digits)" || die "Timeout waiting PAIR CODE."
|
||||||
|
|
||||||
|
local serial="${HOST}:${CONNECT_PORT}"
|
||||||
|
|
||||||
|
adb disconnect "$serial" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "[*] adb pair ${HOST}:${pair_port}"
|
||||||
|
printf '%s\n' "$code" | adb pair "${HOST}:${pair_port}"
|
||||||
|
|
||||||
|
echo "[*] adb connect $serial"
|
||||||
|
adb connect "$serial" >/dev/null
|
||||||
|
|
||||||
|
if [[ "$CLEANUP_OFFLINE" == "1" ]]; then
|
||||||
|
cleanup_offline_loopback "$serial"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Devices:"
|
||||||
|
adb devices -l
|
||||||
|
|
||||||
|
echo "[*] ADB check (shell):"
|
||||||
|
adb -s "$serial" shell sh -lc 'echo "it worked: adb shell is working"; getprop ro.product.model; getprop ro.build.version.release' || true
|
||||||
|
|
||||||
|
echo "[+] OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
|
@ -0,0 +1,517 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# run_iiab_code.sh
|
||||||
|
#
|
||||||
|
# Modes:
|
||||||
|
# --run (default) Launch if needed, seed UNITTEST local_vars, run install.txt, then auto-resume (sudo iiab -f)
|
||||||
|
# --continue-after-reboot Only resume: sudo iiab -f (no long wait; just run)
|
||||||
|
# --clean Stop+delete+purge target VMs (by BASE-<number>, regardless of COUNT)
|
||||||
|
# Distro selection:
|
||||||
|
# --debian-13 Debian 13 only (sets IMAGE=$DEBIAN13_IMAGE_URL and BASE=deb13)
|
||||||
|
# --debian-13 Debian 13 only (sets IMAGE=$DEBIAN13_IMAGE_URL and BASE=deb13)
|
||||||
|
# --both-distros Run Ubuntu + Debian 13 in parallel: COUNT=N => 2N VMs (default order: interleaved)
|
||||||
|
# --first-ubuntu (with --both-distros) order: all Ubuntu first, then all Debian
|
||||||
|
# --first-debian (with --both-distros) order: all Debian first, then all Ubuntu
|
||||||
|
#
|
||||||
|
# PR selection:
|
||||||
|
# --pr 4122 (repeatable) add PR numbers passed to install.txt
|
||||||
|
# --run-pr 4122 same as --pr but also forces --run (alias: --test-pr)
|
||||||
|
#
|
||||||
|
# Env vars:
|
||||||
|
# IIAB_PR="4122 4191" Space-separated PRs
|
||||||
|
# (compat) IIAB_INSTALL_ARGS If set, used as fallback for IIAB_PR
|
||||||
|
#
|
||||||
|
# VM naming:
|
||||||
|
# BASE=ubu2404, COUNT=3 => ubu2404-0 ubu2404-1 ubu2404-2
|
||||||
|
#
|
||||||
|
# Parallel + stagger:
|
||||||
|
# Each phase runs in parallel, but each VM starts its phase offset by STAGGER seconds.
|
||||||
|
#
|
||||||
|
# Logs:
|
||||||
|
# iiab_multipass_runs_YYYYMMDD/
|
||||||
|
# <vm>.<action>.HHMMSS.log / .rc
|
||||||
|
# latest.<vm>.<action>.log / .rc (symlinks)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Debian 13 (Trixie) official cloud image (qcow2). Multipass can launch from URL/file:// on Linux.
|
||||||
|
# Source: Debian cloud images live under cloud.debian.org/images/cloud/ ('genericcloud' includes cloud-init).
|
||||||
|
DEBIAN13_IMAGE_URL="${DEBIAN13_IMAGE_URL:-https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2}"
|
||||||
|
|
||||||
|
IMAGE="${IMAGE:-24.04}"
|
||||||
|
BASE="${BASE:-ubu2404}"
|
||||||
|
COUNT="${COUNT:-1}"
|
||||||
|
CPUS="${CPUS:-3}"
|
||||||
|
MEM="${MEM:-4G}"
|
||||||
|
DISK="${DISK:-20G}"
|
||||||
|
|
||||||
|
# PRs: prefer IIAB_PR, fall back to IIAB_INSTALL_ARGS (back-compat)
|
||||||
|
IIAB_PR="${IIAB_PR:-${IIAB_INSTALL_ARGS:-}}"
|
||||||
|
|
||||||
|
IIAB_FAST="${IIAB_FAST:-1}"
|
||||||
|
LOCAL_VARS_URL="${LOCAL_VARS_URL:-https://raw.githubusercontent.com/iiab/iiab/refs/heads/master/vars/local_vars_unittest.yml}"
|
||||||
|
|
||||||
|
WAIT_TRIES="${WAIT_TRIES:-60}" # used ONLY for the first auto-resume
|
||||||
|
WAIT_SLEEP="${WAIT_SLEEP:-5}"
|
||||||
|
STAGGER="${STAGGER:-15}"
|
||||||
|
|
||||||
|
ACTION="run"
|
||||||
|
modules=()
|
||||||
|
prs=()
|
||||||
|
only_clean_vms=()
|
||||||
|
|
||||||
|
BOTH_DISTROS=0
|
||||||
|
DEBIAN13_ONLY=0
|
||||||
|
|
||||||
|
FIRST_UBUNTU=0
|
||||||
|
FIRST_DEBIAN=0
|
||||||
|
|
||||||
|
# Save original ubuntu settings so --both-distros can restore them even if --debian-13 was used earlier
|
||||||
|
UBU_IMAGE_ORIG="$IMAGE"
|
||||||
|
UBU_BASE_ORIG="$BASE"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage:
|
||||||
|
$0 --run [--module code] [--module X...] [--pr N]...
|
||||||
|
$0 --continue-after-reboot
|
||||||
|
$0 --clean [--only-vm NAME]...
|
||||||
|
$0 --debian-13 [--run|--clean|--continue-after-reboot] ...
|
||||||
|
$0 --both-distros [--first-ubuntu|--first-debian|--clean] [...]
|
||||||
|
|
||||||
|
Aliases:
|
||||||
|
--continue, --resume, --continue-after-upgrade (same as --continue-after-reboot)
|
||||||
|
--test-pr N (same as --run-pr N)
|
||||||
|
Image shortcuts:
|
||||||
|
--debian-13 Use Debian 13 cloud image; sets IMAGE=\$DEBIAN13_IMAGE_URL and BASE=deb13
|
||||||
|
--both-distros Run both Ubuntu + Debian 13 (COUNT=N => N + N VMs)
|
||||||
|
--first-ubuntu With --both-distros: run all Ubuntu first, then Debian
|
||||||
|
--first-debian With --both-distros: run all Debian first, then Ubuntu
|
||||||
|
|
||||||
|
PR options:
|
||||||
|
--pr N Add PR number (repeatable)
|
||||||
|
--run-pr N Add PR number and force --run
|
||||||
|
|
||||||
|
Env:
|
||||||
|
IMAGE BASE COUNT CPUS MEM DISK IIAB_PR IIAB_FAST LOCAL_VARS_URL WAIT_TRIES WAIT_SLEEP STAGGER
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse args
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--run) ACTION="run"; shift ;;
|
||||||
|
--continue-after-reboot|--continue-after-upgrade|--continue|--resume) ACTION="continue"; shift ;;
|
||||||
|
--clean) ACTION="clean"; shift ;;
|
||||||
|
|
||||||
|
--both-distros) BOTH_DISTROS=1; shift ;;
|
||||||
|
--first-ubuntu) FIRST_UBUNTU=1; shift ;;
|
||||||
|
--first-debian) FIRST_DEBIAN=1; shift ;;
|
||||||
|
|
||||||
|
--debian-13)
|
||||||
|
DEBIAN13_ONLY=1
|
||||||
|
IMAGE="$DEBIAN13_IMAGE_URL"
|
||||||
|
BASE="deb13"
|
||||||
|
shift ;;
|
||||||
|
|
||||||
|
--module)
|
||||||
|
[[ $# -lt 2 ]] && { echo "[ERROR] --module needs a value"; exit 2; }
|
||||||
|
modules+=("$2"); shift 2 ;;
|
||||||
|
|
||||||
|
--pr)
|
||||||
|
[[ $# -lt 2 ]] && { echo "[ERROR] --pr needs a number"; exit 2; }
|
||||||
|
prs+=("$2"); shift 2 ;;
|
||||||
|
|
||||||
|
--run-pr|--test-pr)
|
||||||
|
[[ $# -lt 2 ]] && { echo "[ERROR] $1 needs a number"; exit 2; }
|
||||||
|
ACTION="run"
|
||||||
|
prs+=("$2"); shift 2 ;;
|
||||||
|
|
||||||
|
--only-vm)
|
||||||
|
[[ $# -lt 2 ]] && { echo "[ERROR] --only-vm needs a name"; exit 2; }
|
||||||
|
only_clean_vms+=("$2"); shift 2 ;;
|
||||||
|
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "[ERROR] Unknown option: $1"; usage; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ---- Incoherency checks (fail fast) ----
|
||||||
|
if [[ "$FIRST_UBUNTU" == "1" && "$FIRST_DEBIAN" == "1" ]]; then
|
||||||
|
echo "[ERROR] Incoherent options: --first-ubuntu and --first-debian cannot be used together."
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ( "$FIRST_UBUNTU" == "1" || "$FIRST_DEBIAN" == "1" ) && "$BOTH_DISTROS" != "1" ]]; then
|
||||||
|
echo "[ERROR] --first-ubuntu/--first-debian requires --both-distros."
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
# ----
|
||||||
|
|
||||||
|
# Default module
|
||||||
|
if [[ "${#modules[@]}" -eq 0 ]]; then
|
||||||
|
modules=("code")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If no --pr provided, take from env IIAB_PR (space-separated)
|
||||||
|
if [[ "${#prs[@]}" -eq 0 && -n "${IIAB_PR:-}" ]]; then
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
prs=(${IIAB_PR})
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Uniform VM names for run/continue: BASE-0..BASE-(COUNT-1)
|
||||||
|
# If both distros is enabled, restore Ubuntu image/base (in case --debian-13 was used too)
|
||||||
|
if [[ "$BOTH_DISTROS" == "1" ]]; then
|
||||||
|
UBU_IMAGE="$UBU_IMAGE_ORIG"
|
||||||
|
UBU_BASE="$UBU_BASE_ORIG"
|
||||||
|
DEB_IMAGE="$DEBIAN13_IMAGE_URL"
|
||||||
|
DEB_BASE="deb13"
|
||||||
|
else
|
||||||
|
UBU_IMAGE="$IMAGE"
|
||||||
|
UBU_BASE="$BASE"
|
||||||
|
DEB_IMAGE="$DEBIAN13_IMAGE_URL"
|
||||||
|
DEB_BASE="deb13"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOGROOT="${LOGROOT:-iiab_multipass_runs_$(date +%Y%m%d)}"
|
||||||
|
mkdir -p "$LOGROOT"
|
||||||
|
|
||||||
|
stamp() { date +%H%M%S; }
|
||||||
|
|
||||||
|
vm_exists() { multipass info "$1" >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
wait_for_vm() {
|
||||||
|
local vm="$1"
|
||||||
|
local i
|
||||||
|
for ((i=1; i<=WAIT_TRIES; i++)); do
|
||||||
|
if multipass exec "$vm" -- bash -lc 'true' >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep "$WAIT_SLEEP"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
set_latest_links() {
|
||||||
|
local vm="$1" action="$2" log="$3" rc="$4"
|
||||||
|
ln -sfn "$(basename "$log")" "$LOGROOT/latest.${vm}.${action}.log"
|
||||||
|
ln -sfn "$(basename "$rc")" "$LOGROOT/latest.${vm}.${action}.rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_all() {
|
||||||
|
local rc=0
|
||||||
|
local pid
|
||||||
|
for pid in "$@"; do
|
||||||
|
wait "$pid" || rc=1
|
||||||
|
done
|
||||||
|
return "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Escape BASE for regex usage
|
||||||
|
re_escape() { printf '%s' "$1" | sed -e 's/[].[^$*+?(){}|\\]/\\&/g'; }
|
||||||
|
|
||||||
|
declare -A VM_IMAGE
|
||||||
|
names=()
|
||||||
|
|
||||||
|
build_vm_lists() {
|
||||||
|
names=()
|
||||||
|
VM_IMAGE=()
|
||||||
|
|
||||||
|
if [[ "$BOTH_DISTROS" == "1" ]]; then
|
||||||
|
if [[ "$FIRST_UBUNTU" == "1" ]]; then
|
||||||
|
# all Ubuntu first, then all Debian
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local u="${UBU_BASE}-${n}"
|
||||||
|
names+=("$u")
|
||||||
|
VM_IMAGE["$u"]="$UBU_IMAGE"
|
||||||
|
done
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local d="${DEB_BASE}-${n}"
|
||||||
|
names+=("$d")
|
||||||
|
VM_IMAGE["$d"]="$DEB_IMAGE"
|
||||||
|
done
|
||||||
|
|
||||||
|
elif [[ "$FIRST_DEBIAN" == "1" ]]; then
|
||||||
|
# all Debian first, then all Ubuntu
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local d="${DEB_BASE}-${n}"
|
||||||
|
names+=("$d")
|
||||||
|
VM_IMAGE["$d"]="$DEB_IMAGE"
|
||||||
|
done
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local u="${UBU_BASE}-${n}"
|
||||||
|
names+=("$u")
|
||||||
|
VM_IMAGE["$u"]="$UBU_IMAGE"
|
||||||
|
done
|
||||||
|
|
||||||
|
else
|
||||||
|
# default interleaved
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local u="${UBU_BASE}-${n}"
|
||||||
|
local d="${DEB_BASE}-${n}"
|
||||||
|
names+=("$u" "$d")
|
||||||
|
VM_IMAGE["$u"]="$UBU_IMAGE"
|
||||||
|
VM_IMAGE["$d"]="$DEB_IMAGE"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
for n in $(seq 0 $((COUNT-1))); do
|
||||||
|
local vm="${BASE}-${n}"
|
||||||
|
names+=("$vm")
|
||||||
|
VM_IMAGE["$vm"]="$IMAGE"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine clean targets:
|
||||||
|
# - If --only-vm is given: use those exact names
|
||||||
|
# - Else: delete ALL VMs matching "^BASE-[0-9]+$" found in "multipass list"
|
||||||
|
clean_targets() {
|
||||||
|
if [[ "${#only_clean_vms[@]}" -gt 0 ]]; then
|
||||||
|
printf '%s\n' "${only_clean_vms[@]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local list
|
||||||
|
list="$(multipass list 2>/dev/null | awk 'NR>1 {print $1}' || true)"
|
||||||
|
|
||||||
|
if [[ "$BOTH_DISTROS" == "1" ]]; then
|
||||||
|
local ubu_re deb_re
|
||||||
|
ubu_re="$(re_escape "$UBU_BASE")"
|
||||||
|
deb_re="$(re_escape "$DEB_BASE")"
|
||||||
|
printf '%s\n' "$list" | grep -E "^(${ubu_re}|${deb_re})-[0-9]+$" || true
|
||||||
|
else
|
||||||
|
local base_re
|
||||||
|
base_re="$(re_escape "$BASE")"
|
||||||
|
printf '%s\n' "$list" | grep -E "^${base_re}-[0-9]+$" || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_vms() {
|
||||||
|
local targets=()
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -n "$line" ]] && targets+=("$line")
|
||||||
|
done < <(clean_targets)
|
||||||
|
|
||||||
|
if [[ "${#targets[@]}" -eq 0 ]]; then
|
||||||
|
echo "[INFO] No VMs found to clean."
|
||||||
|
echo "[INFO] Tip: use --only-vm NAME to force a specific VM."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[INFO] Cleaning VMs: ${targets[*]}"
|
||||||
|
|
||||||
|
echo "[INFO] Stopping VMs (best-effort)..."
|
||||||
|
multipass stop "${targets[@]}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "[INFO] Deleting VMs (best-effort)..."
|
||||||
|
multipass delete "${targets[@]}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "[INFO] Purging deleted VMs (best-effort)..."
|
||||||
|
multipass purge >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "[INFO] Done."
|
||||||
|
}
|
||||||
|
|
||||||
|
launch_one() {
|
||||||
|
local vm="$1"
|
||||||
|
local img="${VM_IMAGE[$vm]:-}"
|
||||||
|
[[ -z "$img" ]] && { echo "[ERROR] No image mapping for VM '$vm'"; return 2; }
|
||||||
|
|
||||||
|
if vm_exists "$vm"; then
|
||||||
|
echo "[INFO] VM already exists: $vm"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "[INFO] Launching $vm ..."
|
||||||
|
multipass launch "$img" -n "$vm" -c "$CPUS" -m "$MEM" -d "$DISK" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
run_install_txt() {
|
||||||
|
local vm="$1"
|
||||||
|
local t log rc
|
||||||
|
t="$(stamp)"
|
||||||
|
log="$LOGROOT/${vm}.install.${t}.log"
|
||||||
|
rc="$LOGROOT/${vm}.install.${t}.rc"
|
||||||
|
|
||||||
|
echo "[INFO] Logging files stored at: $LOGROOT"
|
||||||
|
echo "[INFO] install.txt in $vm (log $(basename "$log")) ..."
|
||||||
|
|
||||||
|
local modules_str pr_str
|
||||||
|
modules_str="$(printf "%s " "${modules[@]}")"
|
||||||
|
pr_str="$(printf "%s " "${prs[@]}")"
|
||||||
|
|
||||||
|
set +e
|
||||||
|
multipass exec "$vm" -- env \
|
||||||
|
IIAB_FAST="$IIAB_FAST" \
|
||||||
|
LOCAL_VARS_URL="$LOCAL_VARS_URL" \
|
||||||
|
MODULES="$modules_str" \
|
||||||
|
IIAB_PR_LIST="$pr_str" \
|
||||||
|
bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y curl python3 ca-certificates
|
||||||
|
|
||||||
|
# 1) Seed UNITTEST local_vars (Size 0)
|
||||||
|
sudo mkdir -p /etc/iiab
|
||||||
|
echo "[INFO] Seeding /etc/iiab/local_vars.yml from: ${LOCAL_VARS_URL}"
|
||||||
|
curl -fsSL "${LOCAL_VARS_URL}" | sudo tee /etc/iiab/local_vars.yml >/dev/null
|
||||||
|
|
||||||
|
# 2) Set YAML key to True (edit if exists, else append)
|
||||||
|
set_yaml_true() {
|
||||||
|
local key="$1"
|
||||||
|
if sudo grep -qE "^${key}:" /etc/iiab/local_vars.yml; then
|
||||||
|
sudo sed -i -E "s/^${key}:.*/${key}: True/" /etc/iiab/local_vars.yml
|
||||||
|
else
|
||||||
|
echo "${key}: True" | sudo tee -a /etc/iiab/local_vars.yml >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3) Enable module(s)
|
||||||
|
for m in ${MODULES}; do
|
||||||
|
set_yaml_true "${m}_install"
|
||||||
|
set_yaml_true "${m}_enabled"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "--- local_vars.yml (module keys) ---"
|
||||||
|
for m in ${MODULES}; do
|
||||||
|
sudo grep -nE "^(${m}_install|${m}_enabled):" /etc/iiab/local_vars.yml || true
|
||||||
|
done
|
||||||
|
echo "--- end local_vars.yml ---"
|
||||||
|
|
||||||
|
# 4) Run install.txt
|
||||||
|
install_args=()
|
||||||
|
if [[ "${IIAB_FAST:-1}" == "1" ]]; then
|
||||||
|
install_args+=("-f")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# IIAB_PR_LIST is space-separated PR numbers
|
||||||
|
if [[ -n "${IIAB_PR_LIST:-}" ]]; then
|
||||||
|
read -r -a extra <<<"${IIAB_PR_LIST}"
|
||||||
|
install_args+=("${extra[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "--- install.txt ---"
|
||||||
|
echo "curl -fsSL https://iiab.io/install.txt | bash -s -- ${install_args[*]}"
|
||||||
|
curl -fsSL https://iiab.io/install.txt | bash -s -- "${install_args[@]}"
|
||||||
|
|
||||||
|
echo "--- install done ---"
|
||||||
|
' >"$log" 2>&1
|
||||||
|
echo "$?" >"$rc"
|
||||||
|
set -e
|
||||||
|
|
||||||
|
set_latest_links "$vm" "install" "$log" "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
resume_iiab() {
|
||||||
|
local vm="$1"
|
||||||
|
local do_long_wait="$2" # 1 => wait_for_vm (only for first auto-resume), 0 => no long wait
|
||||||
|
local t log rc
|
||||||
|
t="$(stamp)"
|
||||||
|
log="$LOGROOT/${vm}.resume.${t}.log"
|
||||||
|
rc="$LOGROOT/${vm}.resume.${t}.rc"
|
||||||
|
|
||||||
|
echo "[INFO] resume (iiab -f) in $vm (log $(basename "$log")) ..."
|
||||||
|
|
||||||
|
set +e
|
||||||
|
{
|
||||||
|
multipass start "$vm" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
if [[ "$do_long_wait" == "1" ]]; then
|
||||||
|
echo "[INFO] Waiting for VM readiness (first auto-resume): $vm"
|
||||||
|
if ! wait_for_vm "$vm"; then
|
||||||
|
echo "[ERROR] VM did not become ready in time: $vm"
|
||||||
|
exit 88
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
multipass exec "$vm" -- bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
|
echo "--- resume: sudo iiab -f ---"
|
||||||
|
if command -v iiab >/dev/null 2>&1; then
|
||||||
|
sudo iiab -f
|
||||||
|
else
|
||||||
|
echo "[ERROR] iiab command not found; install likely not finished."
|
||||||
|
exit 89
|
||||||
|
fi
|
||||||
|
echo "--- resume done ---"
|
||||||
|
'
|
||||||
|
} >"$log" 2>&1
|
||||||
|
echo "$?" >"$rc"
|
||||||
|
set -e
|
||||||
|
|
||||||
|
set_latest_links "$vm" "resume" "$log" "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
summary() {
|
||||||
|
printf "\n================ SUMMARY (%s) ================\n" "$ACTION"
|
||||||
|
printf "%-12s %-8s %-8s %s\n" "VM" "INSTALL" "RESUME" "Latest logs"
|
||||||
|
printf "%-12s %-8s %-8s %s\n" "------------" "--------" "--------" "----------------------------------------------"
|
||||||
|
|
||||||
|
for vm in "${names[@]}"; do
|
||||||
|
local ir="n/a" rr="n/a"
|
||||||
|
[[ -f "$LOGROOT/latest.${vm}.install.rc" ]] && ir="$(cat "$LOGROOT/latest.${vm}.install.rc" 2>/dev/null || echo n/a)"
|
||||||
|
[[ -f "$LOGROOT/latest.${vm}.resume.rc" ]] && rr="$(cat "$LOGROOT/latest.${vm}.resume.rc" 2>/dev/null || echo n/a)"
|
||||||
|
|
||||||
|
printf "%-12s %-8s %-8s %s\n" \
|
||||||
|
"$vm" "$ir" "$rr" \
|
||||||
|
"latest.${vm}.install.log / latest.${vm}.resume.log"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "[INFO] Logs are in: $LOGROOT/"
|
||||||
|
echo "[INFO] Clean: $0 --clean"
|
||||||
|
echo "[INFO] Resume: $0 --continue-after-reboot"
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_parallel_stagger() {
|
||||||
|
# Runs a phase function in parallel, staggering start by STAGGER seconds.
|
||||||
|
# Args: phase_name (launch|install|resume) [resume_wait=0|1]
|
||||||
|
local phase="$1"
|
||||||
|
local resume_wait="${2:-0}"
|
||||||
|
local pids=()
|
||||||
|
|
||||||
|
for i in "${!names[@]}"; do
|
||||||
|
local vm="${names[$i]}"
|
||||||
|
(
|
||||||
|
sleep $((i * STAGGER))
|
||||||
|
case "$phase" in
|
||||||
|
launch) launch_one "$vm" ;;
|
||||||
|
install) run_install_txt "$vm" ;;
|
||||||
|
resume) resume_iiab "$vm" "$resume_wait" ;;
|
||||||
|
*) echo "[ERROR] Unknown phase: $phase" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
) &
|
||||||
|
pids+=("$!")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Don't abort the whole script if one VM fails; we still want logs + summary.
|
||||||
|
set +e
|
||||||
|
wait_all "${pids[@]}"
|
||||||
|
set -e
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Main ----
|
||||||
|
build_vm_lists
|
||||||
|
|
||||||
|
if [[ "$ACTION" == "clean" ]]; then
|
||||||
|
cleanup_vms
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#prs[@]}" -gt 0 ]]; then
|
||||||
|
echo "[INFO] PRs: ${prs[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
run)
|
||||||
|
phase_parallel_stagger launch
|
||||||
|
phase_parallel_stagger install
|
||||||
|
phase_parallel_stagger resume 1
|
||||||
|
summary
|
||||||
|
;;
|
||||||
|
continue)
|
||||||
|
phase_parallel_stagger resume 0
|
||||||
|
summary
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Loading…
Reference in New Issue