Derive config keys from DEFAULTS and enforce minimum memory of 512M
The config command previously maintained a separate key list and used a switch statement that would need updating for each new key. Deriving VALID_KEYS from config.DEFAULTS keeps them in sync automatically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fa077ff8f5
commit
3b3820f95f
|
|
@ -1,8 +1,7 @@
|
||||||
import { die } from "../fmt.ts"
|
import { die } from "../fmt.ts"
|
||||||
import * as config from "../config.ts"
|
import * as config from "../config.ts"
|
||||||
|
|
||||||
const VALID_KEYS = ["memory"] as const
|
const VALID_KEYS = Object.keys(config.DEFAULTS) as config.Key[]
|
||||||
type Key = (typeof VALID_KEYS)[number]
|
|
||||||
|
|
||||||
export async function action(args: string[]) {
|
export async function action(args: string[]) {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
|
|
@ -16,25 +15,18 @@ export async function action(args: string[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const [key, ...rest] = args
|
const [key, ...rest] = args
|
||||||
if (!VALID_KEYS.includes(key as Key)) die(`Unknown config key: ${key}\nAvailable keys: ${VALID_KEYS.join(", ")}`)
|
if (!VALID_KEYS.includes(key as config.Key)) die(`Unknown config key: ${key}\nAvailable keys: ${VALID_KEYS.join(", ")}`)
|
||||||
|
|
||||||
if (rest.length === 0) {
|
if (rest.length === 0) {
|
||||||
const val = await config.get(key as Key)
|
const val = await config.get(key as config.Key)
|
||||||
console.log(val ?? `${config.DEFAULTS[key as Key]} (default)`)
|
console.log(val ?? `${config.DEFAULTS[key as config.Key]} (default)`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rest.length > 1) die(`Too many arguments. Usage: sandlot config ${key} <value>`)
|
if (rest.length > 1) die(`Too many arguments. Usage: sandlot config ${key} <value>`)
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case "memory": {
|
|
||||||
let normalized: string
|
let normalized: string
|
||||||
try { normalized = config.validateMemory(rest[0]) } catch { die("Must be a number followed by G or M (e.g. 16G)") }
|
try { normalized = config.validateMemory(rest[0]) } catch { return die("Must be a number followed by G or M, minimum 512M (e.g. 16G)") }
|
||||||
await config.set("memory", normalized!)
|
await config.set("memory", normalized)
|
||||||
console.log(`memory = ${normalized!}`)
|
console.log(`memory = ${normalized}`)
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
die(`Unhandled config key: ${key}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,28 @@
|
||||||
import { mkdir } from "fs/promises"
|
import { mkdir } from "fs/promises"
|
||||||
import { homedir } from "os"
|
import { homedir } from "os"
|
||||||
import { dirname, join } from "path"
|
import { join } from "path"
|
||||||
|
|
||||||
const CONFIG_PATH = join(homedir(), ".config", "sandlot", "config.json")
|
const CONFIG_DIR = join(homedir(), ".config", "sandlot")
|
||||||
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json")
|
||||||
|
|
||||||
export const DEFAULTS = {
|
export const DEFAULTS = {
|
||||||
memory: "16G",
|
memory: "16G",
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export type Key = keyof typeof DEFAULTS
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
memory?: string
|
memory?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MIN_MEMORY_MB = 512
|
||||||
|
|
||||||
export function validateMemory(v: string): string {
|
export function validateMemory(v: string): string {
|
||||||
if (!/^[1-9]\d*[GMgm]$/.test(v)) throw new Error(`Invalid memory value: ${v} (must be a number followed by G or M, e.g. 16G)`)
|
if (!/^[1-9]\d*[GMgm]$/.test(v)) throw new Error(`Invalid memory value: ${v} (must be a number followed by G or M, e.g. 16G)`)
|
||||||
|
const num = parseInt(v)
|
||||||
|
const unit = v.slice(-1).toUpperCase()
|
||||||
|
const mb = unit === "G" ? num * 1024 : num
|
||||||
|
if (mb < MIN_MEMORY_MB) throw new Error(`Memory too low: ${v} (minimum ${MIN_MEMORY_MB}M)`)
|
||||||
return v.toUpperCase()
|
return v.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +39,7 @@ export async function load(): Promise<Config> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(config: Config): Promise<void> {
|
export async function save(config: Config): Promise<void> {
|
||||||
await mkdir(dirname(CONFIG_PATH), { recursive: true })
|
await mkdir(CONFIG_DIR, { recursive: true })
|
||||||
await Bun.write(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n")
|
await Bun.write(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,8 @@ function hostMounts(home: string): { dev: boolean; code: boolean } {
|
||||||
/** Pull the image and start the container in detached mode. */
|
/** Pull the image and start the container in detached mode. */
|
||||||
async function createContainer(home: string): Promise<void> {
|
async function createContainer(home: string): Promise<void> {
|
||||||
const mounts = hostMounts(home)
|
const mounts = hostMounts(home)
|
||||||
const raw = (await getConfig("memory")) ?? DEFAULTS.memory
|
|
||||||
let memory: string
|
let memory: string
|
||||||
try { memory = validateMemory(raw) } catch { memory = DEFAULTS.memory }
|
try { memory = validateMemory((await getConfig("memory")) ?? DEFAULTS.memory) } catch { memory = DEFAULTS.memory }
|
||||||
const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", memory]
|
const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", memory]
|
||||||
if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev,readonly`)
|
if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev,readonly`)
|
||||||
if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`)
|
if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user