169 lines
4.3 KiB
Bash
169 lines
4.3 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# common.sh - Shared functions and configuration for QVM CLI tool
|
|
#
|
|
# This file defines XDG-compliant directory paths, constants, and utility
|
|
# functions used across all qvm-* commands. It should be sourced by each
|
|
# command script via: source "${QVM_LIB_DIR}/common.sh"
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# XDG-compliant directory paths
|
|
readonly QVM_DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/qvm"
|
|
readonly QVM_STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/qvm"
|
|
readonly QVM_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/qvm"
|
|
readonly QVM_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/qvm"
|
|
|
|
# Path constants for VM artifacts
|
|
readonly QVM_BASE_IMAGE="$QVM_DATA_DIR/base.qcow2"
|
|
readonly QVM_OVERLAY="$QVM_STATE_DIR/overlay.qcow2"
|
|
readonly QVM_PID_FILE="$QVM_STATE_DIR/vm.pid"
|
|
readonly QVM_SSH_PORT_FILE="$QVM_STATE_DIR/ssh.port"
|
|
readonly QVM_SERIAL_LOG="$QVM_STATE_DIR/serial.log"
|
|
readonly QVM_WORKSPACES_FILE="$QVM_STATE_DIR/workspaces.json"
|
|
readonly QVM_USER_FLAKE="$QVM_CONFIG_DIR/flake"
|
|
|
|
# Cache directories for 9p mounts (shared between host and VM)
|
|
readonly QVM_CARGO_HOME="$QVM_CACHE_DIR/cargo-home"
|
|
readonly QVM_CARGO_TARGET="$QVM_CACHE_DIR/cargo-target"
|
|
readonly QVM_PNPM_STORE="$QVM_CACHE_DIR/pnpm-store"
|
|
readonly QVM_SCCACHE="$QVM_CACHE_DIR/sccache"
|
|
|
|
# Color codes (only used if stdout is a TTY)
|
|
if [[ -t 1 ]]; then
|
|
readonly COLOR_INFO='\033[0;36m' # Cyan
|
|
readonly COLOR_WARN='\033[0;33m' # Yellow
|
|
readonly COLOR_ERROR='\033[0;31m' # Red
|
|
readonly COLOR_RESET='\033[0m' # Reset
|
|
else
|
|
readonly COLOR_INFO=''
|
|
readonly COLOR_WARN=''
|
|
readonly COLOR_ERROR=''
|
|
readonly COLOR_RESET=''
|
|
fi
|
|
|
|
#
|
|
# log_info - Print informational message in cyan
|
|
# Usage: log_info "message"
|
|
#
|
|
log_info() {
|
|
echo -e "${COLOR_INFO}[INFO]${COLOR_RESET} $*" >&2
|
|
}
|
|
|
|
#
|
|
# log_warn - Print warning message in yellow
|
|
# Usage: log_warn "message"
|
|
#
|
|
log_warn() {
|
|
echo -e "${COLOR_WARN}[WARN]${COLOR_RESET} $*" >&2
|
|
}
|
|
|
|
#
|
|
# log_error - Print error message in red
|
|
# Usage: log_error "message"
|
|
#
|
|
log_error() {
|
|
echo -e "${COLOR_ERROR}[ERROR]${COLOR_RESET} $*" >&2
|
|
}
|
|
|
|
#
|
|
# die - Print error message and exit with status 1
|
|
# Usage: die "error message"
|
|
#
|
|
die() {
|
|
log_error "$@"
|
|
exit 1
|
|
}
|
|
|
|
#
|
|
# ensure_dirs - Create all required QVM directories
|
|
# Usage: ensure_dirs
|
|
#
|
|
ensure_dirs() {
|
|
mkdir -p "$QVM_DATA_DIR" \
|
|
"$QVM_STATE_DIR" \
|
|
"$QVM_CACHE_DIR" \
|
|
"$QVM_CONFIG_DIR" \
|
|
"$QVM_CARGO_HOME" \
|
|
"$QVM_CARGO_TARGET" \
|
|
"$QVM_PNPM_STORE" \
|
|
"$QVM_SCCACHE"
|
|
}
|
|
|
|
#
|
|
# is_vm_running - Check if VM process is running
|
|
# Returns: 0 if running, 1 if not
|
|
# Usage: if is_vm_running; then ... fi
|
|
#
|
|
is_vm_running() {
|
|
if [[ ! -f "$QVM_PID_FILE" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
local pid
|
|
pid=$(cat "$QVM_PID_FILE")
|
|
|
|
# Check if process exists and is a QEMU process
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
return 0
|
|
else
|
|
# Stale PID file, remove it
|
|
rm -f "$QVM_PID_FILE"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# get_ssh_port - Read SSH port from state file
|
|
# Returns: SSH port number on stdout
|
|
# Usage: port=$(get_ssh_port)
|
|
#
|
|
get_ssh_port() {
|
|
if [[ ! -f "$QVM_SSH_PORT_FILE" ]]; then
|
|
die "SSH port file not found. Is the VM running?"
|
|
fi
|
|
cat "$QVM_SSH_PORT_FILE"
|
|
}
|
|
|
|
#
|
|
# workspace_hash - Generate short hash from absolute path
|
|
# Args: $1 - absolute path to workspace
|
|
# Returns: 8-character hash on stdout
|
|
# Usage: hash=$(workspace_hash "/path/to/workspace")
|
|
#
|
|
workspace_hash() {
|
|
local path="$1"
|
|
echo -n "$path" | sha256sum | cut -c1-8
|
|
}
|
|
|
|
#
|
|
# wait_for_ssh - Wait for SSH to become available on VM
|
|
# Args: $1 - SSH port number
|
|
# $2 - timeout in seconds (default: 60)
|
|
# Returns: 0 if SSH is available, 1 on timeout
|
|
# Usage: wait_for_ssh "$port" 30
|
|
#
|
|
wait_for_ssh() {
|
|
local port="${1:-}"
|
|
local timeout="${2:-60}"
|
|
local elapsed=0
|
|
|
|
if [[ -z "$port" ]]; then
|
|
die "wait_for_ssh requires port argument"
|
|
fi
|
|
|
|
log_info "Waiting for SSH on port $port (timeout: ${timeout}s)..."
|
|
|
|
while (( elapsed < timeout )); do
|
|
if nc -z -w 1 localhost "$port" 2>/dev/null; then
|
|
log_info "SSH is ready"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
(( elapsed++ ))
|
|
done
|
|
|
|
log_error "SSH did not become available within ${timeout}s"
|
|
return 1
|
|
}
|