Replace Bash qvm scripts with Go CLI implementation
This commit is contained in:
parent
ffb456707f
commit
2a6a333721
27 changed files with 2551 additions and 1702 deletions
47
internal/config/config.go
Normal file
47
internal/config/config.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
VM VMConfig `toml:"vm"`
|
||||
}
|
||||
|
||||
type VMConfig struct {
|
||||
Memory string `toml:"memory"`
|
||||
CPUs int `toml:"cpus"`
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
cfg := &Config{
|
||||
VM: VMConfig{
|
||||
Memory: "30G",
|
||||
CPUs: 30,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := os.Stat(ConfigFile); err == nil {
|
||||
if _, err := toml.DecodeFile(ConfigFile, cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config file %s: %w", ConfigFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
if memEnv := os.Getenv("QVM_MEMORY"); memEnv != "" {
|
||||
cfg.VM.Memory = memEnv
|
||||
}
|
||||
|
||||
if cpusEnv := os.Getenv("QVM_CPUS"); cpusEnv != "" {
|
||||
cpus, err := strconv.Atoi(cpusEnv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("QVM_CPUS must be a valid integer: %w", err)
|
||||
}
|
||||
cfg.VM.CPUs = cpus
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
125
internal/config/paths.go
Normal file
125
internal/config/paths.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Package config handles QVM configuration loading and management.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// XDG-compliant directory paths matching bash lib/common.sh exactly
|
||||
|
||||
var (
|
||||
// DataDir is the base directory for QVM data files (base image, runner)
|
||||
// Defaults to $HOME/.local/share/qvm
|
||||
DataDir = getXDGPath("XDG_DATA_HOME", ".local/share", "qvm")
|
||||
|
||||
// StateDir is the directory for QVM runtime state (overlay, PID, port files)
|
||||
// Defaults to $HOME/.local/state/qvm
|
||||
StateDir = getXDGPath("XDG_STATE_HOME", ".local/state", "qvm")
|
||||
|
||||
// CacheDir is the directory for shared build caches (cargo, pnpm, sccache)
|
||||
// Defaults to $HOME/.cache/qvm
|
||||
CacheDir = getXDGPath("XDG_CACHE_HOME", ".cache", "qvm")
|
||||
|
||||
// ConfigDir is the directory for QVM configuration (flake, qvm.toml)
|
||||
// Defaults to $HOME/.config/qvm
|
||||
ConfigDir = getXDGPath("XDG_CONFIG_HOME", ".config", "qvm")
|
||||
)
|
||||
|
||||
// Path constants for VM artifacts
|
||||
var (
|
||||
// BaseImage is the path to the base VM image (read-only)
|
||||
BaseImage = filepath.Join(DataDir, "base.qcow2")
|
||||
|
||||
// VMRunner is the path to the VM runner script
|
||||
VMRunner = filepath.Join(DataDir, "run-vm")
|
||||
|
||||
// Overlay is the path to the VM overlay image (copy-on-write)
|
||||
Overlay = filepath.Join(StateDir, "qvm-dev.qcow2")
|
||||
|
||||
// PIDFile is the path to the VM process ID file
|
||||
PIDFile = filepath.Join(StateDir, "vm.pid")
|
||||
|
||||
// SSHPortFile is the path to the SSH port file
|
||||
SSHPortFile = filepath.Join(StateDir, "ssh.port")
|
||||
|
||||
// SerialLog is the path to the VM serial console log
|
||||
SerialLog = filepath.Join(StateDir, "serial.log")
|
||||
|
||||
// WorkspacesFile is the path to the workspaces registry JSON
|
||||
WorkspacesFile = filepath.Join(StateDir, "workspaces.json")
|
||||
|
||||
// QMPSocket is the path to the QMP (QEMU Machine Protocol) socket
|
||||
QMPSocket = filepath.Join(StateDir, "qmp.sock")
|
||||
|
||||
// UserFlake is the path to the user's customizable NixOS flake
|
||||
UserFlake = filepath.Join(ConfigDir, "flake")
|
||||
|
||||
// ConfigFile is the path to the QVM TOML configuration file
|
||||
ConfigFile = filepath.Join(ConfigDir, "qvm.toml")
|
||||
)
|
||||
|
||||
// Cache directories for 9p mounts (shared between host and VM)
|
||||
var (
|
||||
// CargoHome is the shared Cargo registry/cache directory
|
||||
CargoHome = filepath.Join(CacheDir, "cargo-home")
|
||||
|
||||
// CargoTarget is the shared Cargo build artifacts directory
|
||||
CargoTarget = filepath.Join(CacheDir, "cargo-target")
|
||||
|
||||
// PnpmStore is the shared pnpm content-addressable store
|
||||
PnpmStore = filepath.Join(CacheDir, "pnpm-store")
|
||||
|
||||
// Sccache is the shared sccache compilation cache
|
||||
Sccache = filepath.Join(CacheDir, "sccache")
|
||||
)
|
||||
|
||||
// Host config directories to mount in VM (read-write for tools that need it)
|
||||
var (
|
||||
// HostOpencodeConfig is the path to the host's opencode configuration
|
||||
// Defaults to $HOME/.config/opencode
|
||||
HostOpencodeConfig = getXDGPath("XDG_CONFIG_HOME", ".config", "opencode")
|
||||
)
|
||||
|
||||
// getXDGPath returns an XDG-compliant path with fallback.
|
||||
// Args:
|
||||
// - xdgEnv: XDG environment variable to check (e.g., "XDG_DATA_HOME")
|
||||
// - fallbackPath: relative path from $HOME if xdgEnv is not set (e.g., ".local/share")
|
||||
// - suffix: additional path suffix to append (e.g., "qvm")
|
||||
//
|
||||
// Returns the resolved absolute path.
|
||||
func getXDGPath(xdgEnv, fallbackPath, suffix string) string {
|
||||
base := os.Getenv(xdgEnv)
|
||||
if base == "" {
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
// Fallback to current directory if HOME is not set
|
||||
home = "."
|
||||
}
|
||||
base = filepath.Join(home, fallbackPath)
|
||||
}
|
||||
return filepath.Join(base, suffix)
|
||||
}
|
||||
|
||||
// EnsureDirs creates all required QVM directories if they don't exist.
|
||||
// Returns error if directory creation fails.
|
||||
func EnsureDirs() error {
|
||||
dirs := []string{
|
||||
DataDir,
|
||||
StateDir,
|
||||
CacheDir,
|
||||
ConfigDir,
|
||||
CargoHome,
|
||||
CargoTarget,
|
||||
PnpmStore,
|
||||
Sccache,
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue