mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-05-01 11:02:56 +02:00
354 lines
14 KiB
Bash
354 lines
14 KiB
Bash
#!/usr/bin/env bash
|
|
# install_docker.sh
|
|
#
|
|
# Generates a local docker-compose.yml for RemoteTerm from a guided prompt flow.
|
|
# The generated compose file is intentionally gitignored so local customization
|
|
# does not create merge churn on future pulls.
|
|
#
|
|
# Run from anywhere inside the repo:
|
|
# bash scripts/setup/install_docker.sh
|
|
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
PURPLE='\033[0;35m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
REPO_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
COMPOSE_FILE="$REPO_DIR/docker-compose.yml"
|
|
EXAMPLE_FILE="$REPO_DIR/docker-compose.example.yml"
|
|
|
|
IMAGE_MODE="image"
|
|
TRANSPORT_MODE="serial"
|
|
SERIAL_HOST_PATH="/dev/ttyACM0"
|
|
SERIAL_CONTAINER_PATH="/dev/meshcore-radio"
|
|
TCP_HOST=""
|
|
TCP_PORT="4000"
|
|
BLE_ADDRESS=""
|
|
BLE_PIN=""
|
|
ENABLE_BOTS="N"
|
|
ENABLE_AUTH="N"
|
|
AUTH_USERNAME=""
|
|
AUTH_PASSWORD=""
|
|
RUN_AS_HOST_USER="N"
|
|
BLE_MANUAL_WARNING=false
|
|
|
|
find_serial_devices() {
|
|
local -n out_host_paths_ref=$1
|
|
local -n out_labels_ref=$2
|
|
local -n out_display_ref=$3
|
|
local path
|
|
local resolved
|
|
local label
|
|
|
|
out_host_paths_ref=()
|
|
out_labels_ref=()
|
|
out_display_ref=()
|
|
|
|
if [ -d /dev/serial/by-id ]; then
|
|
while IFS= read -r path; do
|
|
[ -n "$path" ] || continue
|
|
resolved="$(readlink -f "$path" 2>/dev/null || true)"
|
|
[ -n "$resolved" ] || resolved="$path"
|
|
label="$(basename "$path")"
|
|
out_host_paths_ref+=("$path")
|
|
out_labels_ref+=("$label")
|
|
out_display_ref+=("$path -> $resolved")
|
|
done < <(find /dev/serial/by-id -maxdepth 1 -type l | sort)
|
|
fi
|
|
|
|
for path in /dev/ttyACM* /dev/ttyUSB* /dev/cu.usbmodem* /dev/cu.usbserial*; do
|
|
[ -e "$path" ] || continue
|
|
resolved="$(readlink -f "$path" 2>/dev/null || true)"
|
|
[ -n "$resolved" ] || resolved="$path"
|
|
|
|
if ((${#out_host_paths_ref[@]} > 0)); then
|
|
local existing
|
|
for existing in "${out_display_ref[@]}"; do
|
|
if [[ "$existing" = *"-> $resolved" ]]; then
|
|
resolved=""
|
|
break
|
|
fi
|
|
done
|
|
[ -n "$resolved" ] || continue
|
|
fi
|
|
|
|
out_host_paths_ref+=("$path")
|
|
out_labels_ref+=("$(basename "$path")")
|
|
out_display_ref+=("$path")
|
|
done
|
|
}
|
|
|
|
echo -e "${BOLD}=== RemoteTerm for MeshCore — Docker Setup ===${NC}"
|
|
echo
|
|
echo -e " Repo directory : ${CYAN}${REPO_DIR}${NC}"
|
|
echo -e " Example compose : ${CYAN}${EXAMPLE_FILE}${NC}"
|
|
echo -e " Output compose : ${CYAN}${COMPOSE_FILE}${NC}"
|
|
echo
|
|
|
|
if ! command -v docker &>/dev/null; then
|
|
echo -e "${RED}Error: docker was not found in PATH.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker compose version &>/dev/null; then
|
|
echo -e "${RED}Error: docker compose is required but was not available.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f "$COMPOSE_FILE" ]; then
|
|
echo -e "${YELLOW}A local docker-compose.yml already exists.${NC}"
|
|
read -rp "Overwrite it? [y/N]: " OVERWRITE
|
|
OVERWRITE="${OVERWRITE:-N}"
|
|
if ! [[ "$OVERWRITE" =~ ^[Yy]$ ]]; then
|
|
echo -e "${YELLOW}Leaving the existing compose file untouched.${NC}"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
echo -e "${BOLD}─── Image Source ────────────────────────────────────────────────────${NC}"
|
|
echo "How should Docker run RemoteTerm?"
|
|
echo " 1) Use the published Docker Hub image (default)"
|
|
echo " 2) Build locally from this checkout"
|
|
echo
|
|
read -rp "Select image mode [1-2] (default: 1): " IMAGE_CHOICE
|
|
IMAGE_CHOICE="${IMAGE_CHOICE:-1}"
|
|
echo
|
|
|
|
case "$IMAGE_CHOICE" in
|
|
1)
|
|
IMAGE_MODE="image"
|
|
echo -e "${GREEN}Using published Docker image.${NC}"
|
|
;;
|
|
2)
|
|
IMAGE_MODE="build"
|
|
echo -e "${GREEN}Using local Docker build.${NC}"
|
|
;;
|
|
*)
|
|
IMAGE_MODE="image"
|
|
echo -e "${YELLOW}Invalid selection; defaulting to published Docker image.${NC}"
|
|
;;
|
|
esac
|
|
echo
|
|
|
|
echo -e "${BOLD}─── Transport ───────────────────────────────────────────────────────${NC}"
|
|
echo "How will the container reach your MeshCore radio?"
|
|
echo " 1) Serial device passthrough (default)"
|
|
echo " 2) TCP"
|
|
echo " 3) BLE"
|
|
echo
|
|
echo "BLE can be configured here, but Docker Bluetooth access still requires manual compose customization."
|
|
echo
|
|
read -rp "Select transport [1-3] (default: 1): " TRANSPORT_CHOICE
|
|
TRANSPORT_CHOICE="${TRANSPORT_CHOICE:-1}"
|
|
echo
|
|
|
|
case "$TRANSPORT_CHOICE" in
|
|
1)
|
|
TRANSPORT_MODE="serial"
|
|
SERIAL_HOST_PATHS=()
|
|
SERIAL_LABELS=()
|
|
SERIAL_DISPLAYS=()
|
|
find_serial_devices SERIAL_HOST_PATHS SERIAL_LABELS SERIAL_DISPLAYS
|
|
|
|
if ((${#SERIAL_HOST_PATHS[@]} == 0)); then
|
|
echo -e "${YELLOW}No serial devices were auto-detected.${NC}"
|
|
read -rp "Serial device path on the host (default: /dev/ttyACM0): " SERIAL_HOST_PATH
|
|
SERIAL_HOST_PATH="${SERIAL_HOST_PATH:-/dev/ttyACM0}"
|
|
else
|
|
echo "Detected serial devices:"
|
|
for i in "${!SERIAL_HOST_PATHS[@]}"; do
|
|
printf ' %d) %s (%s)\n' "$((i + 1))" "${SERIAL_LABELS[$i]}" "${SERIAL_DISPLAYS[$i]}"
|
|
done
|
|
echo " m) Enter a path manually"
|
|
echo
|
|
read -rp "Select serial device [1-${#SERIAL_HOST_PATHS[@]} or m] (default: 1): " SERIAL_CHOICE
|
|
SERIAL_CHOICE="${SERIAL_CHOICE:-1}"
|
|
|
|
if [[ "$SERIAL_CHOICE" =~ ^[Mm]$ ]]; then
|
|
read -rp "Serial device path on the host (default: ${SERIAL_HOST_PATHS[0]}): " SERIAL_HOST_PATH
|
|
SERIAL_HOST_PATH="${SERIAL_HOST_PATH:-${SERIAL_HOST_PATHS[0]}}"
|
|
elif [[ "$SERIAL_CHOICE" =~ ^[0-9]+$ ]] && [ "$SERIAL_CHOICE" -ge 1 ] && [ "$SERIAL_CHOICE" -le "${#SERIAL_HOST_PATHS[@]}" ]; then
|
|
SERIAL_HOST_PATH="${SERIAL_HOST_PATHS[$((SERIAL_CHOICE - 1))]}"
|
|
else
|
|
SERIAL_HOST_PATH="${SERIAL_HOST_PATHS[0]}"
|
|
echo -e "${YELLOW}Invalid selection; defaulting to ${SERIAL_HOST_PATH}.${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo -e "${GREEN}Serial passthrough: ${SERIAL_HOST_PATH} -> ${SERIAL_CONTAINER_PATH}${NC}"
|
|
;;
|
|
2)
|
|
TRANSPORT_MODE="tcp"
|
|
read -rp "TCP host (IP address or hostname): " TCP_HOST
|
|
while [ -z "$TCP_HOST" ]; do
|
|
echo -e "${RED}TCP host is required.${NC}"
|
|
read -rp "TCP host: " TCP_HOST
|
|
done
|
|
read -rp "TCP port (default: 4000): " TCP_PORT
|
|
TCP_PORT="${TCP_PORT:-4000}"
|
|
echo -e "${GREEN}TCP: ${TCP_HOST}:${TCP_PORT}${NC}"
|
|
;;
|
|
3)
|
|
TRANSPORT_MODE="ble"
|
|
read -rp "BLE device address (e.g. AA:BB:CC:DD:EE:FF): " BLE_ADDRESS
|
|
while [ -z "$BLE_ADDRESS" ]; do
|
|
echo -e "${RED}BLE address is required.${NC}"
|
|
read -rp "BLE device address: " BLE_ADDRESS
|
|
done
|
|
read -rsp "BLE PIN: " BLE_PIN
|
|
echo
|
|
while [ -z "$BLE_PIN" ]; do
|
|
echo -e "${RED}BLE PIN is required.${NC}"
|
|
read -rsp "BLE PIN: " BLE_PIN
|
|
echo
|
|
done
|
|
echo -e "${GREEN}BLE: ${BLE_ADDRESS}${NC}"
|
|
echo
|
|
echo -e "${RED}BLE Docker warning:${NC} Bluetooth access is not fully automated here."
|
|
echo -e "${RED}You will need to customize docker-compose.yml manually before BLE works.${NC}"
|
|
echo "That may include passing through Bluetooth devices, enabling privileged mode,"
|
|
echo "using host networking, or other host-specific Docker changes."
|
|
echo "If you want the easier path, use the regular Python launch flow for BLE instead."
|
|
BLE_MANUAL_WARNING=true
|
|
;;
|
|
*)
|
|
TRANSPORT_MODE="serial"
|
|
SERIAL_HOST_PATH="/dev/ttyACM0"
|
|
echo -e "${YELLOW}Invalid selection; defaulting to serial passthrough at ${SERIAL_HOST_PATH}.${NC}"
|
|
;;
|
|
esac
|
|
echo
|
|
|
|
echo -e "${BOLD}─── Bot System ──────────────────────────────────────────────────────${NC}"
|
|
echo -e "${YELLOW}Warning:${NC} The bot system executes arbitrary Python code on the server."
|
|
echo "It is not recommended on untrusted networks."
|
|
echo
|
|
read -rp "Enable bots? [y/N]: " ENABLE_BOTS
|
|
ENABLE_BOTS="${ENABLE_BOTS:-N}"
|
|
echo
|
|
|
|
if [[ "$ENABLE_BOTS" =~ ^[Yy]$ ]]; then
|
|
echo -e "${GREEN}Bots enabled.${NC}"
|
|
echo
|
|
echo -e "${BOLD}─── HTTP Basic Auth ─────────────────────────────────────────────────${NC}"
|
|
echo "With bots enabled, HTTP Basic Auth is strongly recommended if this"
|
|
echo "service will be reachable beyond your local machine."
|
|
echo
|
|
read -rp "Set up HTTP Basic Auth? [Y/n]: " ENABLE_AUTH
|
|
ENABLE_AUTH="${ENABLE_AUTH:-Y}"
|
|
echo
|
|
|
|
if [[ "$ENABLE_AUTH" =~ ^[Yy]$ ]]; then
|
|
read -rp "Username: " AUTH_USERNAME
|
|
while [ -z "$AUTH_USERNAME" ]; do
|
|
echo -e "${RED}Username cannot be empty.${NC}"
|
|
read -rp "Username: " AUTH_USERNAME
|
|
done
|
|
read -rsp "Password: " AUTH_PASSWORD
|
|
echo
|
|
while [ -z "$AUTH_PASSWORD" ]; do
|
|
echo -e "${RED}Password cannot be empty.${NC}"
|
|
read -rsp "Password: " AUTH_PASSWORD
|
|
echo
|
|
done
|
|
echo -e "${GREEN}Basic Auth configured for user '${AUTH_USERNAME}'.${NC}"
|
|
fi
|
|
else
|
|
echo -e "${GREEN}Bots disabled.${NC}"
|
|
fi
|
|
echo
|
|
|
|
if [ "$(uname -s)" = "Linux" ]; then
|
|
echo -e "${BOLD}─── Container User ──────────────────────────────────────────────────${NC}"
|
|
echo "The container runs as root by default for maximum serial compatibility."
|
|
echo "You can override that and run as your host UID/GID instead to avoid"
|
|
echo "root-owned files in ./data."
|
|
echo
|
|
read -rp "Run as your current UID/GID instead of the default root user? [y/N]: " RUN_AS_HOST_USER
|
|
RUN_AS_HOST_USER="${RUN_AS_HOST_USER:-N}"
|
|
if [[ "$RUN_AS_HOST_USER" =~ ^[Yy]$ ]] && [ "$TRANSPORT_MODE" = "serial" ]; then
|
|
echo
|
|
echo -e "${YELLOW}Note:${NC} host-user mode can be less reliable for serial device access than running as root."
|
|
echo "It may require extra group setup such as dialout, or other manual"
|
|
echo "container customization, depending on your host."
|
|
echo "If serial access becomes unreliable, rerun this setup and keep the"
|
|
echo "default root user instead."
|
|
fi
|
|
echo
|
|
fi
|
|
|
|
mkdir -p "$REPO_DIR/data"
|
|
|
|
{
|
|
echo "# Generated by scripts/setup/install_docker.sh"
|
|
echo "# This file is gitignored. Re-run the setup script to regenerate it."
|
|
echo "services:"
|
|
echo " remoteterm:"
|
|
if [ "$IMAGE_MODE" = "build" ]; then
|
|
echo " build: ."
|
|
else
|
|
echo " image: jkingsman/remoteterm-meshcore:latest"
|
|
fi
|
|
if [[ "$RUN_AS_HOST_USER" =~ ^[Yy]$ ]]; then
|
|
echo " user: \"$(id -u):$(id -g)\""
|
|
fi
|
|
echo " ports:"
|
|
echo " - \"8000:8000\""
|
|
echo " volumes:"
|
|
echo " - ./data:/app/data"
|
|
if [ "$TRANSPORT_MODE" = "serial" ]; then
|
|
echo " devices:"
|
|
echo " - ${SERIAL_HOST_PATH}:${SERIAL_CONTAINER_PATH}"
|
|
fi
|
|
echo " environment:"
|
|
echo " MESHCORE_DATABASE_PATH: data/meshcore.db"
|
|
if [ "$TRANSPORT_MODE" = "serial" ]; then
|
|
echo " MESHCORE_SERIAL_PORT: ${SERIAL_CONTAINER_PATH}"
|
|
elif [ "$TRANSPORT_MODE" = "tcp" ]; then
|
|
echo " MESHCORE_TCP_HOST: ${TCP_HOST}"
|
|
echo " MESHCORE_TCP_PORT: ${TCP_PORT}"
|
|
else
|
|
echo " MESHCORE_BLE_ADDRESS: ${BLE_ADDRESS}"
|
|
echo " MESHCORE_BLE_PIN: ${BLE_PIN}"
|
|
fi
|
|
if ! [[ "$ENABLE_BOTS" =~ ^[Yy]$ ]]; then
|
|
echo " MESHCORE_DISABLE_BOTS: \"true\""
|
|
fi
|
|
if [[ "$ENABLE_AUTH" =~ ^[Yy]$ ]]; then
|
|
echo " MESHCORE_BASIC_AUTH_USERNAME: ${AUTH_USERNAME}"
|
|
echo " MESHCORE_BASIC_AUTH_PASSWORD: ${AUTH_PASSWORD}"
|
|
fi
|
|
echo " restart: unless-stopped"
|
|
} >"$COMPOSE_FILE"
|
|
|
|
echo -e "${GREEN}Generated ${COMPOSE_FILE}.${NC}"
|
|
echo
|
|
echo -e "${BOLD}Docker commands${NC}"
|
|
if [ "$IMAGE_MODE" = "build" ]; then
|
|
echo " docker compose up -d --build # build the local image and start RemoteTerm in the background"
|
|
else
|
|
echo " docker compose up -d # start RemoteTerm in the background"
|
|
fi
|
|
echo " docker compose logs -f # follow the container logs live"
|
|
echo
|
|
echo " docker compose down # stop and remove the running container"
|
|
echo " docker compose restart # restart the container without changing the image"
|
|
echo " docker compose pull && docker compose up -d # upgrade to the latest published image and restart"
|
|
if [ "$TRANSPORT_MODE" = "ble" ] || [ "$BLE_MANUAL_WARNING" = true ]; then
|
|
echo
|
|
echo -e "${RED}BLE requires more than the generated env vars.${NC}"
|
|
echo -e "${RED}Before starting, edit docker-compose.yml for Bluetooth passthrough and any privileged/network settings your host requires.${NC}"
|
|
fi
|
|
echo
|
|
echo -e "${GREEN}Your new docker file is ready at ${COMPOSE_FILE}.${NC}"
|
|
echo -e "${GREEN}Feel free to edit it by hand as desired, or:${NC}"
|
|
echo
|
|
echo -e "${PURPLE}┌──────────────────────────────────────────────┐${NC}"
|
|
echo -e "${PURPLE}│ Run ${GREEN}${BOLD}docker compose up -d${NC}${PURPLE} to get started. │${NC}"
|
|
echo -e "${PURPLE}└──────────────────────────────────────────────┘${NC}"
|