Add initial QVM CLI, Nix flake, scripts and README
This commit is contained in:
parent
25b1cca0e6
commit
8534f7efb9
14 changed files with 2359 additions and 0 deletions
169
lib/common.sh
Normal file
169
lib/common.sh
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#!/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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue