118 lines
3.8 KiB
Text
118 lines
3.8 KiB
Text
# Helper Functions for Workspace Configuration Composition
|
|
# Provides utilities for deep merging, validation, and composition
|
|
|
|
let std = import "std" in
|
|
|
|
{
|
|
# Deep merge records recursively
|
|
# Unlike & operator (shallow merge), this preserves nested structure
|
|
compose_config | doc "Recursively merge three records: base → mode_config → overrides"
|
|
= fun base => fun mode_config => fun user_overrides => (
|
|
let merge_records = fun left => fun right => (
|
|
# For each field in right, either:
|
|
# - If both are records: recursively merge
|
|
# - Otherwise: right overwrites left
|
|
std.record.to_array left
|
|
| std.array.fold_left
|
|
(fun acc => fun kv =>
|
|
let key = kv.key in
|
|
let left_val = kv.value in
|
|
let right_val = right | get key? in
|
|
|
|
if right_val != null && left_val != null then
|
|
if (std.typeof left_val) == "record" && (std.typeof right_val) == "record" then
|
|
acc & {[key] = merge_records left_val right_val}
|
|
else
|
|
acc & {[key] = right_val}
|
|
else if right_val != null then
|
|
acc & {[key] = right_val}
|
|
else
|
|
acc & {[key] = left_val}
|
|
)
|
|
right
|
|
) in
|
|
|
|
merge_records
|
|
(merge_records base mode_config)
|
|
user_overrides
|
|
),
|
|
|
|
# Validate required fields in config
|
|
validate_required | doc "Ensure all required fields exist in config"
|
|
= fun required_fields => fun config => (
|
|
let missing = (
|
|
required_fields
|
|
| std.array.filter (fun field =>
|
|
(config | get field? | std.value.is_null)
|
|
)
|
|
) in
|
|
|
|
if (missing | std.array.length) > 0 then
|
|
std.contract.blame $"Missing required fields: " ++ (std.array.join ", " missing)
|
|
else
|
|
config
|
|
),
|
|
|
|
# Validate no_use field and filter inactive servers
|
|
filter_active_servers | doc "Filter out servers with not_use = true"
|
|
= fun servers => (
|
|
servers
|
|
| std.array.filter (fun server =>
|
|
(server | get not_use? | default false) == false
|
|
)
|
|
),
|
|
|
|
# Extract provider type from config
|
|
get_provider | doc "Extract provider name from workspace config"
|
|
= fun config => (
|
|
config | get provider? | default "generic"
|
|
),
|
|
|
|
# Map server to NixOS flake path
|
|
to_flake_path | doc "Generate NixOS flake.nix directory path for server"
|
|
= fun server => fun workspace_name => (
|
|
let hostname = server.hostname in
|
|
let explicit_path = server | get flake_path? in
|
|
|
|
if explicit_path != null && explicit_path != "" then
|
|
explicit_path
|
|
else
|
|
$"workspaces/($workspace_name)/nixos/($hostname)"
|
|
),
|
|
|
|
# Collect all unique taskservs across servers
|
|
collect_taskservs | doc "Extract and deduplicate taskserv names from all servers"
|
|
= fun servers => (
|
|
servers
|
|
| std.array.fold_left
|
|
(fun acc => fun server =>
|
|
let ts = server | get taskservs? | default [] in
|
|
if (std.typeof ts) == "array" then
|
|
ts
|
|
| std.array.fold_left
|
|
(fun acc2 => fun taskserv =>
|
|
let name = taskserv | get name? | default "unknown" in
|
|
if (acc2 | std.array.contains name) then
|
|
acc2
|
|
else
|
|
acc2 | std.array.push name
|
|
)
|
|
acc
|
|
else
|
|
acc
|
|
)
|
|
[]
|
|
),
|
|
|
|
# Create minimal server stub for testing
|
|
minimal_server | doc "Generate minimal valid server record for testing"
|
|
= fun hostname => {
|
|
hostname = hostname,
|
|
title = $"Server ($hostname)",
|
|
plan = "1xCPU-1GB",
|
|
not_use = false,
|
|
extra_hostnames = [hostname],
|
|
labels = "",
|
|
taskservs = [],
|
|
},
|
|
}
|