c9cdd2feca
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Previously, launching a sandbox on a machine without the backend binary died with a raw "not installed" error and no path forward. Now the missing backend can be installed in-line, gated on explicit consent, so a fresh checkout can go from zero to a running sandbox without leaving the TUI. Folded into the existing `/sbx launch` verb rather than a new command (menu is already dense): `/sbx launch docker|multipass [image] install`. The `install` token opts in; without it the user gets an actionable error naming the exact retry. The token is filtered out of positional image parsing so it never shadows a custom image. The install runs off-thread inside the existing spawn_launch task, before provisioning, so the TUI never blocks and the *launching guard is cleared via BrokerMsg on failure. A fresh Docker install also leaves its daemon up, so launch proceeds straight through. Scripts (detect-first, never silent, --plan dry-run, idempotent if already present): - ensure-multipass.sh (new): Linux→snap (clear failure if snapd absent), macOS→brew cask, Windows→winget. - ensure-docker.sh: new --install mode using Docker's OFFICIAL, GPG-verified apt repo (docker-ce) on Debian/Ubuntu, with dnf (Fedora/RHEL) and pacman (Arch) fallbacks. Deliberately avoids piping get.docker.com into a root shell. Existing daemon-start path intact. sbx.rs: docker_installed()/multipass_installed() detectors and ensure_docker_install()/ensure_multipass_install() wrappers; ENSURE_MULTIPASS const. ui.rs: help text documents the [install] option on both backends. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.9 KiB
Bash
111 lines
3.9 KiB
Bash
#!/usr/bin/env bash
|
|
# ensure-multipass.sh — make sure Multipass is installed before /sbx launch multipass.
|
|
#
|
|
# Detect-first, never silent: if Multipass is already present this exits 0 and
|
|
# changes nothing. If it's missing it prints the EXACT command it would run and
|
|
# only installs with explicit consent (--yes). --plan shows a real, no-change
|
|
# plan so you can see what would be fetched.
|
|
#
|
|
# On Linux, Multipass ships ONLY via snap (no apt/dnf package), so snapd must be
|
|
# present; if it isn't, this says so rather than guessing a package name.
|
|
#
|
|
# usage:
|
|
# ./ensure-multipass.sh # interactive: prompt before installing
|
|
# ./ensure-multipass.sh --yes # install without prompting
|
|
# ./ensure-multipass.sh --check # test only; exit 0 if present, 1 if missing
|
|
# ./ensure-multipass.sh --plan # show the install plan; change nothing
|
|
set -uo pipefail
|
|
|
|
ASSUME_YES=0
|
|
CHECK_ONLY=0
|
|
PLAN_ONLY=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
-y|--yes) ASSUME_YES=1 ;;
|
|
--check) CHECK_ONLY=1 ;;
|
|
--plan|--dry-run) PLAN_ONLY=1 ;;
|
|
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) echo "✖ unknown arg: $arg" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
installed() { command -v multipass >/dev/null 2>&1 && multipass version >/dev/null 2>&1; }
|
|
mp_version() { multipass version 2>/dev/null | head -1; }
|
|
|
|
# Already present: report and stop (idempotent). --plan still prints the plan.
|
|
if installed; then
|
|
if [[ $PLAN_ONLY -ne 1 ]]; then
|
|
[[ $CHECK_ONLY -eq 1 ]] || echo "Multipass already installed ($(mp_version)) — nothing to do" >&2
|
|
exit 0
|
|
fi
|
|
fi
|
|
[[ $CHECK_ONLY -eq 1 ]] && exit 1
|
|
|
|
# Work out how to install on this platform, and the matching no-change plan cmd.
|
|
install_cmd=""
|
|
plan_cmd=""
|
|
need_sudo=0
|
|
manual_note=""
|
|
case "$(uname -s)" in
|
|
Linux)
|
|
if command -v snap >/dev/null 2>&1; then
|
|
install_cmd="snap install multipass"
|
|
plan_cmd="snap info multipass" # no root needed to inspect
|
|
need_sudo=1
|
|
else
|
|
echo "✖ Multipass on Linux requires snap (snapd), which isn't installed." >&2
|
|
echo " Install snapd first (e.g. 'sudo apt-get install snapd'), then re-run," >&2
|
|
echo " or see https://multipass.run/install for alternatives." >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
Darwin)
|
|
if command -v brew >/dev/null 2>&1; then
|
|
install_cmd="brew install --cask multipass"
|
|
plan_cmd="brew info --cask multipass"
|
|
manual_note="macOS: Multipass installs a system helper; you may be prompted for your password."
|
|
fi
|
|
;;
|
|
MINGW*|MSYS*|CYGWIN*)
|
|
if command -v winget >/dev/null 2>&1; then
|
|
install_cmd="winget install -e --id Canonical.Multipass"
|
|
plan_cmd="winget show -e --id Canonical.Multipass"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [[ -z "$install_cmd" ]]; then
|
|
echo "✖ don't know how to install Multipass here — get it from https://multipass.run/install" >&2
|
|
exit 1
|
|
fi
|
|
[[ $need_sudo -eq 1 ]] && install_cmd="sudo $install_cmd"
|
|
[[ -n "$manual_note" ]] && echo "ⓘ $manual_note" >&2
|
|
|
|
# --plan: show the real plan and change nothing.
|
|
if [[ $PLAN_ONLY -eq 1 ]]; then
|
|
echo "plan (no changes will be made): $plan_cmd" >&2
|
|
eval "$plan_cmd"
|
|
exit 0
|
|
fi
|
|
|
|
# Confirmation (skipped with --yes).
|
|
if [[ $ASSUME_YES -ne 1 ]]; then
|
|
printf 'Multipass is not installed. Install it with "%s"? [y/N] ' "$install_cmd" >&2
|
|
read -r reply
|
|
case "$reply" in
|
|
y|Y|yes|YES) ;;
|
|
*) echo "✖ aborted — Multipass left uninstalled" >&2; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
echo "installing Multipass: $install_cmd" >&2
|
|
eval "$install_cmd" || { echo "✖ install failed (sudo password needed? run it in a terminal)" >&2; exit 1; }
|
|
|
|
# Confirm the binary is now callable.
|
|
if installed; then
|
|
echo "Multipass is ready ($(mp_version))" >&2
|
|
exit 0
|
|
fi
|
|
echo "✖ install ran but multipass is still not callable — check the install log" >&2
|
|
exit 1
|