Replace Bash qvm scripts with Go CLI implementation

This commit is contained in:
Joshua Bell 2026-01-26 20:48:32 -06:00
parent ffb456707f
commit 2a6a333721
27 changed files with 2551 additions and 1702 deletions

View file

@ -0,0 +1,139 @@
package workspace
import (
"crypto/sha256"
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/samber/mo"
)
type Workspace struct {
HostPath string `json:"host_path"`
Hash string `json:"hash"`
MountTag string `json:"mount_tag"`
GuestPath string `json:"guest_path"`
}
type Registry struct {
filePath string
workspaces map[string]Workspace
}
// Hash generates an 8-character hash from a path, matching bash behavior:
// echo -n "$path" | sha256sum | cut -c1-8
func Hash(path string) string {
h := sha256.Sum256([]byte(path))
return fmt.Sprintf("%x", h)[:8]
}
// NewRegistry creates a new empty registry
func NewRegistry(filePath string) *Registry {
return &Registry{
filePath: filePath,
workspaces: make(map[string]Workspace),
}
}
// Load reads the registry from a JSON file
func Load(filePath string) mo.Result[*Registry] {
registry := NewRegistry(filePath)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return mo.Ok(registry)
}
data, err := os.ReadFile(filePath)
if err != nil {
return mo.Err[*Registry](fmt.Errorf("failed to read workspaces file: %w", err))
}
if len(data) == 0 {
return mo.Ok(registry)
}
var workspaceList []Workspace
if err := json.Unmarshal(data, &workspaceList); err != nil {
return mo.Err[*Registry](fmt.Errorf("failed to parse workspaces JSON: %w", err))
}
for _, ws := range workspaceList {
registry.workspaces[ws.HostPath] = ws
}
return mo.Ok(registry)
}
// Save writes the registry to the JSON file
func (r *Registry) Save() mo.Result[struct{}] {
dir := filepath.Dir(r.filePath)
if err := os.MkdirAll(dir, 0755); err != nil {
return mo.Err[struct{}](fmt.Errorf("failed to create directory: %w", err))
}
workspaceList := make([]Workspace, 0, len(r.workspaces))
for _, ws := range r.workspaces {
workspaceList = append(workspaceList, ws)
}
data, err := json.MarshalIndent(workspaceList, "", " ")
if err != nil {
return mo.Err[struct{}](fmt.Errorf("failed to marshal JSON: %w", err))
}
if err := os.WriteFile(r.filePath, data, 0644); err != nil {
return mo.Err[struct{}](fmt.Errorf("failed to write workspaces file: %w", err))
}
return mo.Ok(struct{}{})
}
// Register adds a workspace to the registry if it doesn't already exist
func (r *Registry) Register(hostPath string) mo.Result[*Workspace] {
absPath, err := filepath.Abs(hostPath)
if err != nil {
return mo.Err[*Workspace](fmt.Errorf("failed to resolve absolute path: %w", err))
}
if existing, exists := r.workspaces[absPath]; exists {
return mo.Ok(&existing)
}
hash := Hash(absPath)
ws := Workspace{
HostPath: absPath,
Hash: hash,
MountTag: fmt.Sprintf("ws_%s", hash),
GuestPath: fmt.Sprintf("/workspace/%s", hash),
}
r.workspaces[absPath] = ws
return mo.Ok(&ws)
}
// List returns all registered workspaces
func (r *Registry) List() []Workspace {
result := make([]Workspace, 0, len(r.workspaces))
for _, ws := range r.workspaces {
result = append(result, ws)
}
return result
}
// Find looks up a workspace by host path
func (r *Registry) Find(hostPath string) mo.Option[Workspace] {
absPath, err := filepath.Abs(hostPath)
if err != nil {
if ws, exists := r.workspaces[hostPath]; exists {
return mo.Some(ws)
}
return mo.None[Workspace]()
}
if ws, exists := r.workspaces[absPath]; exists {
return mo.Some(ws)
}
return mo.None[Workspace]()
}