Use SCRIPT_DIR, add sshpass and auto-mount workspaces, update flakes
This commit is contained in:
parent
8534f7efb9
commit
9aa72fade7
8 changed files with 292 additions and 413 deletions
7
bin/qvm
7
bin/qvm
|
|
@ -8,8 +8,11 @@
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Determine script directory for locating sibling scripts
|
||||||
|
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
# Source common library (use QVM_LIB_DIR from wrapper or relative path for dev)
|
# Source common library (use QVM_LIB_DIR from wrapper or relative path for dev)
|
||||||
source "${QVM_LIB_DIR:-$(dirname "$0")/../lib}/common.sh"
|
source "${QVM_LIB_DIR:-${SCRIPT_DIR}/../lib}/common.sh"
|
||||||
|
|
||||||
readonly VERSION="0.1.0"
|
readonly VERSION="0.1.0"
|
||||||
|
|
||||||
|
|
@ -72,7 +75,7 @@ main() {
|
||||||
start|stop|run|ssh|status|rebuild|reset)
|
start|stop|run|ssh|status|rebuild|reset)
|
||||||
# Route to the appropriate qvm-* script
|
# Route to the appropriate qvm-* script
|
||||||
# Use exec to replace this process with the subcommand
|
# Use exec to replace this process with the subcommand
|
||||||
exec "qvm-${subcommand}" "$@"
|
exec "${SCRIPT_DIR}/qvm-${subcommand}" "$@"
|
||||||
;;
|
;;
|
||||||
help|--help|-h)
|
help|--help|-h)
|
||||||
show_help
|
show_help
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,17 @@ ensure_user_flake() {
|
||||||
log_info "User flake not found, copying default template..."
|
log_info "User flake not found, copying default template..."
|
||||||
|
|
||||||
# Determine default flake location
|
# Determine default flake location
|
||||||
# In installed version: $QVM_LIB_DIR/../flake/default-vm/
|
# In installed version: $QVM_LIB_DIR/../share/qvm/default-vm/
|
||||||
# In development: $(dirname "$0")/../flake/default-vm/
|
# In development: $(dirname "$0")/../flake/default-vm/
|
||||||
local default_flake_dir="$QVM_LIB_DIR/../flake/default-vm"
|
local default_flake_dir
|
||||||
|
|
||||||
|
# Try installed location first
|
||||||
|
if [[ -d "$QVM_LIB_DIR/../share/qvm/default-vm" ]]; then
|
||||||
|
default_flake_dir="$QVM_LIB_DIR/../share/qvm/default-vm"
|
||||||
|
else
|
||||||
|
# Fall back to development location
|
||||||
|
default_flake_dir="$(dirname "$(readlink -f "$0")")/../flake/default-vm"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -d "$default_flake_dir" ]]; then
|
if [[ ! -d "$default_flake_dir" ]]; then
|
||||||
die "Default flake template not found at: $default_flake_dir"
|
die "Default flake template not found at: $default_flake_dir"
|
||||||
|
|
|
||||||
54
bin/qvm-run
54
bin/qvm-run
|
|
@ -95,6 +95,34 @@ register_workspace() {
|
||||||
return 1 # Indicate new workspace added
|
return 1 # Indicate new workspace added
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# ensure_workspace_mounted - Mount workspace in VM if not already mounted
|
||||||
|
# Args: $1 - SSH port
|
||||||
|
# $2 - mount tag (e.g., ws_abc123)
|
||||||
|
# $3 - guest path (e.g., /workspace/abc123)
|
||||||
|
# Returns: 0 on success
|
||||||
|
#
|
||||||
|
ensure_workspace_mounted() {
|
||||||
|
local ssh_port="$1"
|
||||||
|
local mount_tag="$2"
|
||||||
|
local guest_path="$3"
|
||||||
|
|
||||||
|
# SSH into VM and mount the workspace
|
||||||
|
# - mkdir creates the mount point if missing
|
||||||
|
# - mount attempts to mount the 9p virtfs
|
||||||
|
# - || true ensures we don't fail if already mounted
|
||||||
|
sshpass -p root ssh -o StrictHostKeyChecking=no \
|
||||||
|
-o UserKnownHostsFile=/dev/null \
|
||||||
|
-o LogLevel=ERROR \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$ssh_port" \
|
||||||
|
root@localhost \
|
||||||
|
"mkdir -p '$guest_path' && mount -t 9p -o trans=virtio,version=9p2000.L,msize=104857600 '$mount_tag' '$guest_path' 2>/dev/null || true" >/dev/null 2>&1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# is_workspace_mounted - Check if workspace is actually mounted in VM
|
# is_workspace_mounted - Check if workspace is actually mounted in VM
|
||||||
# Args: $1 - SSH port
|
# Args: $1 - SSH port
|
||||||
|
|
@ -106,9 +134,11 @@ is_workspace_mounted() {
|
||||||
local guest_path="$2"
|
local guest_path="$2"
|
||||||
|
|
||||||
# SSH into VM and check if guest path exists and is a directory
|
# SSH into VM and check if guest path exists and is a directory
|
||||||
if ssh -o StrictHostKeyChecking=no \
|
if sshpass -p root ssh -o StrictHostKeyChecking=no \
|
||||||
-o UserKnownHostsFile=/dev/null \
|
-o UserKnownHostsFile=/dev/null \
|
||||||
-o LogLevel=ERROR \
|
-o LogLevel=ERROR \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
-p "$ssh_port" \
|
-p "$ssh_port" \
|
||||||
root@localhost \
|
root@localhost \
|
||||||
"test -d '$guest_path'" 2>/dev/null; then
|
"test -d '$guest_path'" 2>/dev/null; then
|
||||||
|
|
@ -169,14 +199,22 @@ main() {
|
||||||
local ssh_port
|
local ssh_port
|
||||||
ssh_port=$(get_ssh_port)
|
ssh_port=$(get_ssh_port)
|
||||||
|
|
||||||
# Check if workspace is actually mounted in VM
|
# Get mount tag from workspaces.json
|
||||||
|
local mount_tag
|
||||||
|
mount_tag=$(jq -r --arg path "$workspace_path" '.[] | select(.host_path == $path) | .mount_tag' "$QVM_WORKSPACES_FILE")
|
||||||
|
|
||||||
|
# Ensure workspace is mounted (auto-mount if not)
|
||||||
|
log_info "Ensuring workspace is mounted..."
|
||||||
|
ensure_workspace_mounted "$ssh_port" "$mount_tag" "$guest_path"
|
||||||
|
|
||||||
|
# Verify workspace is actually mounted
|
||||||
if ! is_workspace_mounted "$ssh_port" "$guest_path"; then
|
if ! is_workspace_mounted "$ssh_port" "$guest_path"; then
|
||||||
log_error "Workspace not mounted in VM"
|
log_error "Failed to mount workspace in VM"
|
||||||
echo ""
|
echo ""
|
||||||
echo "This workspace was just registered but is not mounted in the VM."
|
echo "The workspace could not be mounted automatically."
|
||||||
echo "Workspaces must be mounted at VM start time."
|
echo "This may indicate the VM was started before this workspace was registered."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Please restart the VM to mount this workspace:"
|
echo "Please restart the VM to properly configure the workspace:"
|
||||||
echo " qvm stop"
|
echo " qvm stop"
|
||||||
echo " qvm start"
|
echo " qvm start"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
@ -185,14 +223,18 @@ main() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build SSH command
|
# Build SSH command
|
||||||
|
# - Use sshpass for automated password auth (password: root)
|
||||||
# - Use -t if stdin is a TTY (for interactive commands)
|
# - Use -t if stdin is a TTY (for interactive commands)
|
||||||
# - Suppress SSH warnings (ephemeral VM, host keys change)
|
# - Suppress SSH warnings (ephemeral VM, host keys change)
|
||||||
# - cd to guest path and execute command
|
# - cd to guest path and execute command
|
||||||
local ssh_cmd=(
|
local ssh_cmd=(
|
||||||
|
sshpass -p root
|
||||||
ssh
|
ssh
|
||||||
-o StrictHostKeyChecking=no
|
-o StrictHostKeyChecking=no
|
||||||
-o UserKnownHostsFile=/dev/null
|
-o UserKnownHostsFile=/dev/null
|
||||||
-o LogLevel=ERROR
|
-o LogLevel=ERROR
|
||||||
|
-o PubkeyAuthentication=no
|
||||||
|
-o PasswordAuthentication=yes
|
||||||
-p "$ssh_port"
|
-p "$ssh_port"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,12 +85,15 @@ main() {
|
||||||
local port
|
local port
|
||||||
port=$(get_ssh_port)
|
port=$(get_ssh_port)
|
||||||
|
|
||||||
# Build SSH command
|
# Build SSH command with sshpass for automated password auth
|
||||||
local ssh_cmd=(
|
local ssh_cmd=(
|
||||||
|
sshpass -p root
|
||||||
ssh
|
ssh
|
||||||
-o StrictHostKeyChecking=no
|
-o StrictHostKeyChecking=no
|
||||||
-o UserKnownHostsFile=/dev/null
|
-o UserKnownHostsFile=/dev/null
|
||||||
-o LogLevel=ERROR # Suppress host key warnings
|
-o LogLevel=ERROR # Suppress host key warnings
|
||||||
|
-o PubkeyAuthentication=no # Use password auth (password: root)
|
||||||
|
-o PasswordAuthentication=yes
|
||||||
-p "$port"
|
-p "$port"
|
||||||
root@localhost
|
root@localhost
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ mount_workspaces() {
|
||||||
local i=0
|
local i=0
|
||||||
while (( i < workspace_count )); do
|
while (( i < workspace_count )); do
|
||||||
local path mount_tag
|
local path mount_tag
|
||||||
path=$(jq -r ".[$i].path" "$QVM_WORKSPACES_FILE")
|
path=$(jq -r ".[$i].host_path" "$QVM_WORKSPACES_FILE")
|
||||||
mount_tag=$(jq -r ".[$i].mount_tag" "$QVM_WORKSPACES_FILE")
|
mount_tag=$(jq -r ".[$i].mount_tag" "$QVM_WORKSPACES_FILE")
|
||||||
|
|
||||||
if [[ -z "$path" || -z "$mount_tag" || "$path" == "null" || "$mount_tag" == "null" ]]; then
|
if [[ -z "$path" || -z "$mount_tag" || "$path" == "null" || "$mount_tag" == "null" ]]; then
|
||||||
|
|
@ -93,9 +93,9 @@ mount_workspaces() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info " - $path -> $mount_tag"
|
log_info " - $path -> $mount_tag"
|
||||||
cmd_array+=(-virtfs "local,path=$path,mount_tag=$mount_tag,security_model=mapped-xattr,trans=virtio,version=9p2000.L,msize=104857600")
|
cmd_array+=(-virtfs "local,path=$path,mount_tag=$mount_tag,security_model=mapped-xattr")
|
||||||
|
|
||||||
(( i++ ))
|
(( i++ )) || true # Prevent set -e from exiting when i was 0
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,10 +173,11 @@ main() {
|
||||||
-device "virtio-net-pci,netdev=net0"
|
-device "virtio-net-pci,netdev=net0"
|
||||||
|
|
||||||
# 9p mounts for shared caches (security_model=mapped-xattr for proper permissions)
|
# 9p mounts for shared caches (security_model=mapped-xattr for proper permissions)
|
||||||
-virtfs "local,path=$QVM_CARGO_HOME,mount_tag=cargo_home,security_model=mapped-xattr,trans=virtio,version=9p2000.L,msize=104857600"
|
# Note: trans, version, msize are kernel-side mount options (in NixOS flake), not QEMU options
|
||||||
-virtfs "local,path=$QVM_CARGO_TARGET,mount_tag=cargo_target,security_model=mapped-xattr,trans=virtio,version=9p2000.L,msize=104857600"
|
-virtfs "local,path=$QVM_CARGO_HOME,mount_tag=cargo_home,security_model=mapped-xattr"
|
||||||
-virtfs "local,path=$QVM_PNPM_STORE,mount_tag=pnpm_store,security_model=mapped-xattr,trans=virtio,version=9p2000.L,msize=104857600"
|
-virtfs "local,path=$QVM_CARGO_TARGET,mount_tag=cargo_target,security_model=mapped-xattr"
|
||||||
-virtfs "local,path=$QVM_SCCACHE,mount_tag=sccache,security_model=mapped-xattr,trans=virtio,version=9p2000.L,msize=104857600"
|
-virtfs "local,path=$QVM_PNPM_STORE,mount_tag=pnpm_store,security_model=mapped-xattr"
|
||||||
|
-virtfs "local,path=$QVM_SCCACHE,mount_tag=sccache,security_model=mapped-xattr"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add workspace mounts from registry
|
# Add workspace mounts from registry
|
||||||
|
|
@ -187,8 +188,8 @@ main() {
|
||||||
# Serial console to log file
|
# Serial console to log file
|
||||||
-serial "file:$QVM_SERIAL_LOG"
|
-serial "file:$QVM_SERIAL_LOG"
|
||||||
|
|
||||||
# No graphics
|
# No graphics (use -display none for daemonized mode)
|
||||||
-nographic
|
-display none
|
||||||
|
|
||||||
# Daemonize with PID file
|
# Daemonize with PID file
|
||||||
-daemonize
|
-daemonize
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,16 @@
|
||||||
# Create output directories
|
# Create output directories
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
mkdir -p $out/lib/qvm
|
mkdir -p $out/lib/qvm
|
||||||
|
mkdir -p $out/share/qvm
|
||||||
|
|
||||||
# Install library files
|
# Install library files
|
||||||
install -Dm755 lib/common.sh $out/lib/qvm/common.sh
|
install -Dm755 lib/common.sh $out/lib/qvm/common.sh
|
||||||
|
|
||||||
|
# Install default VM flake template
|
||||||
|
if [ -d "flake/default-vm" ]; then
|
||||||
|
cp -r flake/default-vm $out/share/qvm/default-vm
|
||||||
|
fi
|
||||||
|
|
||||||
# Install all scripts from bin/
|
# Install all scripts from bin/
|
||||||
for script in bin/*; do
|
for script in bin/*; do
|
||||||
if [ -f "$script" ]; then
|
if [ -f "$script" ]; then
|
||||||
|
|
@ -57,6 +63,7 @@
|
||||||
pkgs.netcat-gnu
|
pkgs.netcat-gnu
|
||||||
pkgs.bc
|
pkgs.bc
|
||||||
pkgs.procps
|
pkgs.procps
|
||||||
|
pkgs.sshpass
|
||||||
]} \
|
]} \
|
||||||
--set QVM_LIB_DIR "$out/lib/qvm"
|
--set QVM_LIB_DIR "$out/lib/qvm"
|
||||||
done
|
done
|
||||||
|
|
@ -104,6 +111,7 @@
|
||||||
netcat-gnu
|
netcat-gnu
|
||||||
bc
|
bc
|
||||||
procps
|
procps
|
||||||
|
sshpass
|
||||||
|
|
||||||
# Development tools
|
# Development tools
|
||||||
shellcheck
|
shellcheck
|
||||||
|
|
|
||||||
|
|
@ -3,210 +3,234 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
home-manager = {
|
||||||
|
url = "github:nix-community/home-manager";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
nixos-generators = {
|
nixos-generators = {
|
||||||
url = "github:nix-community/nixos-generators";
|
url = "github:nix-community/nixos-generators";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
home-manager = {
|
|
||||||
url = "github:nix-community/home-manager";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
opencode.url = "github:anomalyco/opencode";
|
opencode.url = "github:anomalyco/opencode";
|
||||||
|
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
||||||
|
ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs =
|
||||||
self,
|
{
|
||||||
nixpkgs,
|
self,
|
||||||
nixos-generators,
|
nixpkgs,
|
||||||
home-manager,
|
nixos-generators,
|
||||||
opencode,
|
...
|
||||||
...
|
}@inputs:
|
||||||
}: let
|
let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
stateVersion = "24.11";
|
stateVersion = "26.05";
|
||||||
|
|
||||||
vmModule = { config, pkgs, lib, ... }: {
|
vmModule =
|
||||||
imports = [
|
{
|
||||||
home-manager.nixosModules.home-manager
|
config,
|
||||||
];
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.home-manager.nixosModules.home-manager
|
||||||
|
|
||||||
nixpkgs.config = {
|
inputs.ros_neovim.nixosModules.default
|
||||||
allowUnfree = true;
|
inputs.common.nixosModules.essentials
|
||||||
allowUnfreePredicate = (_: true);
|
inputs.common.nixosModules.git
|
||||||
};
|
inputs.common.nixosModules.zsh
|
||||||
|
inputs.common.nixosModules.tmux
|
||||||
|
];
|
||||||
|
|
||||||
# Distinctive hostname for easy identification
|
nixpkgs.config = {
|
||||||
networking.hostName = "qvm-dev";
|
allowUnfree = true;
|
||||||
|
allowUnfreePredicate = (_: true);
|
||||||
|
};
|
||||||
|
|
||||||
# SSH enabled with password auth for root
|
# Distinctive hostname for easy identification
|
||||||
services.openssh = {
|
networking.hostName = "qvm-dev";
|
||||||
enable = true;
|
|
||||||
settings.PasswordAuthentication = true;
|
|
||||||
settings.PermitRootLogin = "yes";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Root user with password and zsh
|
# SSH enabled with password auth for root
|
||||||
users.users.root = {
|
services.openssh = {
|
||||||
password = "root";
|
|
||||||
shell = pkgs.zsh;
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.zsh.enable = true;
|
|
||||||
|
|
||||||
# Home manager configuration for nice shell
|
|
||||||
home-manager = {
|
|
||||||
useUserPackages = true;
|
|
||||||
useGlobalPkgs = true;
|
|
||||||
backupFileExtension = "bak";
|
|
||||||
|
|
||||||
users.root = {
|
|
||||||
home.stateVersion = stateVersion;
|
|
||||||
programs.home-manager.enable = true;
|
|
||||||
|
|
||||||
# Starship prompt that shows we're in qvm-dev
|
|
||||||
programs.starship = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings.PasswordAuthentication = true;
|
||||||
add_newline = false;
|
settings.PermitRootLogin = "yes";
|
||||||
format = lib.concatStrings [
|
};
|
||||||
"[┌─](bold green)"
|
|
||||||
"[$hostname](bold red)"
|
# Root user with password and zsh
|
||||||
"[$directory](bold blue)"
|
users.users.root = {
|
||||||
"$git_branch"
|
password = "root";
|
||||||
"$git_status"
|
shell = pkgs.zsh;
|
||||||
"\n"
|
};
|
||||||
"[└─>](bold green) "
|
|
||||||
|
programs.zsh.enable = true;
|
||||||
|
|
||||||
|
# Home manager configuration for nice shell
|
||||||
|
home-manager = {
|
||||||
|
useUserPackages = true;
|
||||||
|
useGlobalPkgs = true;
|
||||||
|
backupFileExtension = "bak";
|
||||||
|
|
||||||
|
users.root = {
|
||||||
|
home.stateVersion = stateVersion;
|
||||||
|
programs.home-manager.enable = true;
|
||||||
|
|
||||||
|
sharedModules = [
|
||||||
|
inputs.common.homeManagerModules.atuin
|
||||||
|
inputs.common.homeManagerModules.git
|
||||||
|
inputs.common.homeManagerModules.postgres_cli_options
|
||||||
|
inputs.common.homeManagerModules.starship
|
||||||
|
inputs.common.homeManagerModules.zoxide
|
||||||
|
inputs.common.homeManagerModules.zsh
|
||||||
|
inputs.common.homeManagerModules.tmux
|
||||||
|
inputs.common.homeManagerModules.direnv
|
||||||
];
|
];
|
||||||
hostname = {
|
|
||||||
ssh_only = false;
|
|
||||||
format = "[@$hostname](bold red) ";
|
|
||||||
disabled = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.zsh = {
|
# Avoid slow boots due to wait-online
|
||||||
enable = true;
|
systemd.network.wait-online.enable = false;
|
||||||
enableCompletion = true;
|
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
|
||||||
autosuggestion.enable = true;
|
systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
|
||||||
syntaxHighlighting.enable = true;
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
|
|
||||||
|
# Enable flakes
|
||||||
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Josh's timezone
|
||||||
|
time.timeZone = "America/Chicago";
|
||||||
|
|
||||||
|
# Git safe.directory for 9p ownership issues
|
||||||
|
environment.etc."gitconfig".text = ''
|
||||||
|
[safe]
|
||||||
|
directory = *
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 9p mount points for caches (must match qvm-start mount tags)
|
||||||
|
fileSystems."/cache/cargo" = {
|
||||||
|
device = "cargo_home";
|
||||||
|
fsType = "9p";
|
||||||
|
options = [
|
||||||
|
"trans=virtio"
|
||||||
|
"version=9p2000.L"
|
||||||
|
"msize=104857600"
|
||||||
|
"_netdev"
|
||||||
|
"nofail"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fileSystems."/cache/target" = {
|
||||||
|
device = "cargo_target";
|
||||||
|
fsType = "9p";
|
||||||
|
options = [
|
||||||
|
"trans=virtio"
|
||||||
|
"version=9p2000.L"
|
||||||
|
"msize=104857600"
|
||||||
|
"_netdev"
|
||||||
|
"nofail"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/cache/pnpm" = {
|
||||||
|
device = "pnpm_store";
|
||||||
|
fsType = "9p";
|
||||||
|
options = [
|
||||||
|
"trans=virtio"
|
||||||
|
"version=9p2000.L"
|
||||||
|
"msize=104857600"
|
||||||
|
"_netdev"
|
||||||
|
"nofail"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/cache/sccache" = {
|
||||||
|
device = "sccache";
|
||||||
|
fsType = "9p";
|
||||||
|
options = [
|
||||||
|
"trans=virtio"
|
||||||
|
"version=9p2000.L"
|
||||||
|
"msize=104857600"
|
||||||
|
"_netdev"
|
||||||
|
"nofail"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Environment variables for cache directories
|
||||||
|
environment.variables = {
|
||||||
|
CARGO_HOME = "/cache/cargo";
|
||||||
|
CARGO_TARGET_DIR = "/cache/target";
|
||||||
|
PNPM_HOME = "/cache/pnpm";
|
||||||
|
SCCACHE_DIR = "/cache/sccache";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Ensure workspace directory exists
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /workspace 0755 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Essential packages for development
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
git
|
||||||
|
vim
|
||||||
|
tmux
|
||||||
|
htop
|
||||||
|
curl
|
||||||
|
jq
|
||||||
|
ripgrep
|
||||||
|
fd
|
||||||
|
inputs.opencode.packages.${system}.default
|
||||||
|
];
|
||||||
|
|
||||||
|
# Opencode aliases without proxy interference
|
||||||
|
environment.shellAliases = {
|
||||||
|
"oc" = "all_proxy='' http_proxy='' https_proxy='' opencode";
|
||||||
|
"occ" = "oc -c";
|
||||||
|
};
|
||||||
|
|
||||||
|
# MOTD to clearly show this is qvm-dev
|
||||||
|
users.motd = ''
|
||||||
|
╔════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ QVM Development VM ║
|
||||||
|
║ Hostname: qvm-dev ║
|
||||||
|
║ ║
|
||||||
|
║ Caches: /cache/{cargo,target,...} ║
|
||||||
|
║ Workspace: /workspace ║
|
||||||
|
║ ║
|
||||||
|
╚════════════════════════════════════════╝
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 20GB disk size
|
||||||
|
virtualisation.diskSize = 20 * 1024;
|
||||||
|
|
||||||
|
system.stateVersion = stateVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
let
|
||||||
|
qcow2Image = nixos-generators.nixosGenerate {
|
||||||
|
inherit system;
|
||||||
|
format = "qcow";
|
||||||
|
modules = [ vmModule ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Export the qcow2 image
|
||||||
|
packages.${system} = {
|
||||||
|
qcow2 = qcow2Image;
|
||||||
|
default = qcow2Image;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Avoid slow boots due to wait-online
|
# Export the module for reuse
|
||||||
systemd.network.wait-online.enable = false;
|
nixosModules.default = vmModule;
|
||||||
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
|
|
||||||
systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
|
||||||
|
|
||||||
# Enable flakes
|
|
||||||
nix.settings.experimental-features = [
|
|
||||||
"nix-command"
|
|
||||||
"flakes"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Josh's timezone
|
|
||||||
time.timeZone = "America/Chicago";
|
|
||||||
|
|
||||||
# Git safe.directory for 9p ownership issues
|
|
||||||
environment.etc."gitconfig".text = ''
|
|
||||||
[safe]
|
|
||||||
directory = *
|
|
||||||
'';
|
|
||||||
|
|
||||||
# 9p mount points for caches (must match qvm-start mount tags)
|
|
||||||
fileSystems."/cache/cargo" = {
|
|
||||||
device = "cargo_home";
|
|
||||||
fsType = "9p";
|
|
||||||
options = [ "trans=virtio" "version=9p2000.L" "msize=104857600" "_netdev" "nofail" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/cache/target" = {
|
|
||||||
device = "cargo_target";
|
|
||||||
fsType = "9p";
|
|
||||||
options = [ "trans=virtio" "version=9p2000.L" "msize=104857600" "_netdev" "nofail" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/cache/pnpm" = {
|
|
||||||
device = "pnpm_store";
|
|
||||||
fsType = "9p";
|
|
||||||
options = [ "trans=virtio" "version=9p2000.L" "msize=104857600" "_netdev" "nofail" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/cache/sccache" = {
|
|
||||||
device = "sccache";
|
|
||||||
fsType = "9p";
|
|
||||||
options = [ "trans=virtio" "version=9p2000.L" "msize=104857600" "_netdev" "nofail" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Environment variables for cache directories
|
|
||||||
environment.variables = {
|
|
||||||
CARGO_HOME = "/cache/cargo";
|
|
||||||
CARGO_TARGET_DIR = "/cache/target";
|
|
||||||
PNPM_HOME = "/cache/pnpm";
|
|
||||||
SCCACHE_DIR = "/cache/sccache";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Ensure workspace directory exists
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d /workspace 0755 root root -"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Essential packages for development
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
git
|
|
||||||
vim
|
|
||||||
tmux
|
|
||||||
htop
|
|
||||||
curl
|
|
||||||
jq
|
|
||||||
ripgrep
|
|
||||||
fd
|
|
||||||
opencode.packages.${system}.default
|
|
||||||
];
|
|
||||||
|
|
||||||
# Opencode aliases without proxy interference
|
|
||||||
environment.shellAliases = {
|
|
||||||
"oc" = "all_proxy='' http_proxy='' https_proxy='' opencode";
|
|
||||||
"occ" = "oc -c";
|
|
||||||
};
|
|
||||||
|
|
||||||
# MOTD to clearly show this is qvm-dev
|
|
||||||
users.motd = ''
|
|
||||||
╔════════════════════════════════════════╗
|
|
||||||
║ ║
|
|
||||||
║ QVM Development VM ║
|
|
||||||
║ Hostname: qvm-dev ║
|
|
||||||
║ ║
|
|
||||||
║ Caches: /cache/{cargo,target,...} ║
|
|
||||||
║ Workspace: /workspace ║
|
|
||||||
║ ║
|
|
||||||
╚════════════════════════════════════════╝
|
|
||||||
'';
|
|
||||||
|
|
||||||
# 20GB disk size
|
|
||||||
virtualisation.diskSize = 20 * 1024;
|
|
||||||
|
|
||||||
system.stateVersion = stateVersion;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
|
||||||
# Export the qcow2 image
|
|
||||||
packages.${system}.qcow2 = nixos-generators.nixosGenerate {
|
|
||||||
inherit system;
|
|
||||||
format = "qcow";
|
|
||||||
modules = [ vmModule ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Also export a default package
|
|
||||||
packages.${system}.default = self.packages.${system}.qcow2;
|
|
||||||
|
|
||||||
# Export the module for reuse
|
|
||||||
nixosModules.default = vmModule;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
{
|
|
||||||
description = "Qai base NixOS VM image";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
home-manager = {
|
|
||||||
url = "github:rycee/home-manager";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
flake-utils = {
|
|
||||||
url = "github:numtide/flake-utils";
|
|
||||||
};
|
|
||||||
nixos-generators = {
|
|
||||||
url = "github:nix-community/nixos-generators";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
|
||||||
opencode.url = "github:anomalyco/opencode?ref=ad4bdd9f0fb7670949b5c47917bb656247ac60ac";
|
|
||||||
ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs =
|
|
||||||
inputs@{
|
|
||||||
self,
|
|
||||||
nixpkgs,
|
|
||||||
flake-utils,
|
|
||||||
nixos-generators,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
baseModule =
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
stateVersion = "26.05";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
inputs."home-manager".nixosModules.default
|
|
||||||
|
|
||||||
inputs.ros_neovim.nixosModules.default
|
|
||||||
|
|
||||||
inputs.common.nixosModules.essentials
|
|
||||||
inputs.common.nixosModules.git
|
|
||||||
inputs.common.nixosModules.zsh
|
|
||||||
inputs.common.nixosModules.tmux
|
|
||||||
|
|
||||||
(
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
home-manager = {
|
|
||||||
useUserPackages = true;
|
|
||||||
useGlobalPkgs = true;
|
|
||||||
backupFileExtension = "bak";
|
|
||||||
|
|
||||||
users.root = {
|
|
||||||
home.stateVersion = stateVersion;
|
|
||||||
programs.home-manager.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
sharedModules = [
|
|
||||||
inputs.common.homeManagerModules.atuin
|
|
||||||
inputs.common.homeManagerModules.git
|
|
||||||
inputs.common.homeManagerModules.postgres_cli_options
|
|
||||||
inputs.common.homeManagerModules.starship
|
|
||||||
inputs.common.homeManagerModules.zoxide
|
|
||||||
inputs.common.homeManagerModules.zsh
|
|
||||||
inputs.common.homeManagerModules.tmux
|
|
||||||
inputs.common.homeManagerModules.direnv
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
nixpkgs.config = {
|
|
||||||
allowUnfree = true;
|
|
||||||
allowUnfreePredicate = (_: true);
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.hostName = "qai-base";
|
|
||||||
|
|
||||||
# SSH enabled for terminal access via WebSocket proxy.
|
|
||||||
services.openssh = {
|
|
||||||
enable = true;
|
|
||||||
settings.PasswordAuthentication = true;
|
|
||||||
settings.PermitRootLogin = "yes";
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.root.password = "root";
|
|
||||||
|
|
||||||
# Avoid slow boots due to wait-online.
|
|
||||||
systemd.network.wait-online.enable = false;
|
|
||||||
systemd.services.NetworkManager-wait-online.enable = false;
|
|
||||||
systemd.services.systemd-networkd-wait-online.enable = false;
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
|
||||||
22
|
|
||||||
];
|
|
||||||
|
|
||||||
# Needed so `nix develop` works inside the VM.
|
|
||||||
nix.settings.experimental-features = [
|
|
||||||
"nix-command"
|
|
||||||
"flakes"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Host binary cache (QEMU user-net host is reachable at 10.0.2.2).
|
|
||||||
# Only effective at runtime, not during image build.
|
|
||||||
networking.hosts."10.0.2.2" = [ "lio" ];
|
|
||||||
|
|
||||||
# Note: These substituters are for runtime use. The build VM can't reach them.
|
|
||||||
nix.settings.substituters = lib.mkAfter [ "http://lio:5000" ];
|
|
||||||
nix.settings.trusted-public-keys = lib.mkAfter [
|
|
||||||
"lio:9jKQ2xJyZjD0AWFzMcLe5dg3s8vOJ3uffujbUkBg4ms="
|
|
||||||
];
|
|
||||||
# Fallback timeout so nix doesn't hang if lio is unreachable
|
|
||||||
nix.settings.connect-timeout = 5;
|
|
||||||
|
|
||||||
time.timeZone = "America/Chicago";
|
|
||||||
|
|
||||||
# Git 2.35+ blocks repos owned by different uid; 9p shares can trip this.
|
|
||||||
# Use wildcard to allow all subdirectories under /workspace (task-1, task-2, etc.)
|
|
||||||
environment.etc."gitconfig".text = ''
|
|
||||||
[safe]
|
|
||||||
directory = *
|
|
||||||
'';
|
|
||||||
|
|
||||||
programs.zsh.enable = true;
|
|
||||||
users.users.root.shell = pkgs.zsh;
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
zsh
|
|
||||||
git
|
|
||||||
htop
|
|
||||||
vim
|
|
||||||
inputs.opencode.packages.${pkgs.system}.default
|
|
||||||
];
|
|
||||||
|
|
||||||
environment.shellAliases = {
|
|
||||||
"oc" = "all_proxy='' http_proxy='' https_proxy='' opencode";
|
|
||||||
"occ" = "oc -c";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Default disk is too small for `nix develop` / direnv.
|
|
||||||
virtualisation.diskSize = 20 * 1024;
|
|
||||||
|
|
||||||
virtualisation.vmVariant = {
|
|
||||||
virtualisation = {
|
|
||||||
memorySize = 4096;
|
|
||||||
cores = 2;
|
|
||||||
graphics = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.forwardPorts = [
|
|
||||||
{
|
|
||||||
from = "host";
|
|
||||||
host.port = 2221;
|
|
||||||
guest.port = 22;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
system.stateVersion = stateVersion;
|
|
||||||
};
|
|
||||||
|
|
||||||
in
|
|
||||||
{
|
|
||||||
nixosModules.default = baseModule;
|
|
||||||
}
|
|
||||||
// flake-utils.lib.eachDefaultSystem (
|
|
||||||
system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
|
|
||||||
baseVm = nixpkgs.lib.nixosSystem {
|
|
||||||
inherit system;
|
|
||||||
modules = [ baseModule ];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
nixosConfigurations.base = baseVm;
|
|
||||||
|
|
||||||
# Runnable VM (./result/bin/run-nixos-vm)
|
|
||||||
packages.vm = baseVm.config.system.build.vm;
|
|
||||||
|
|
||||||
# Bootable qcow2 disk image (./result/nixos.qcow2)
|
|
||||||
packages.qcow2 = nixos-generators.nixosGenerate {
|
|
||||||
inherit system;
|
|
||||||
format = "qcow";
|
|
||||||
modules = [ baseModule ];
|
|
||||||
};
|
|
||||||
|
|
||||||
apps.default = {
|
|
||||||
type = "app";
|
|
||||||
program = "${baseVm.config.system.build.vm}/bin/run-nixos-vm";
|
|
||||||
};
|
|
||||||
|
|
||||||
devShells.default = pkgs.mkShellNoCC {
|
|
||||||
QEMU_NET_OPTS = "hostfwd=tcp::2221-:22";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue