refactor(wrapper): delete 3 fast-paths — single-route principle (ADR-025 Phase 4)
Removed fast-path intercepts and their scripts for taskserv/server/cluster list
commands. These commands now route exclusively to their thin handlers which
invoke the full semantic path (middleware + live provider state).
Bash wrapper — removed intercept blocks:
- `taskserv/task list` (was: scripts/query-taskservs.nu)
- `server/s list/l` (was: scripts/query-servers.nu via lib_minimal)
- `cluster/cl list` (was: scripts/query-clusters.nu via lib_minimal)
Fast-path scripts deleted:
- nulib/scripts/query-taskservs.nu
- nulib/scripts/query-servers.nu
- nulib/scripts/query-clusters.nu
Preserved:
- Daemon routing for server list/ls/l (added by parallel work — still routes
to daemon; daemon internally dispatches to thin handler)
- Thin handlers provisioning-{taskserv,server,cluster}.nu (ADR-025 canonical)
- Their `list/ls/l` cases already exist and call `main list` in the full path
Rationale — why single route matters:
The parallel server-list investigation documented that `server list` fast-path
returned different columns than `server ls` (incomplete data — workspace
detection failures, silent fallbacks). Two implementations = two semantics =
bugs of divergence. Deleting the fast-path forces ONE semantic route; daemon
and cache become orthogonal transport/optimization concerns that can be
toggled without changing what the command returns.
Net effect:
- Same command always returns same data (post-Phase4 all list commands
align; pre-Phase4 only taskserv/server/cluster are aligned)
- `prvng server list` with daemon on/off returns identical data
- Cold-start impact: minor regression (~200-500ms) while Phase 4 completes —
once mod.nu is emptied and thin handlers fully selective, cold-start drops
to <1s for these commands even without daemon
Refs: ADR-025 Phase 4, workspaces/libre-daoshi/.coder/2026-04-17-server-list-daemon-middleware.info.md
This commit is contained in:
parent
bea0477b25
commit
5d9ce3c591
4 changed files with 4 additions and 465 deletions
|
|
@ -823,49 +823,10 @@ if [ "${1:-}" = "provider" ] || [ "${1:-}" = "providers" ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Taskserv list (fast-path) - avoid full system load
|
# Fast-paths removed (ADR-025 Phase 4 — single-route principle).
|
||||||
if [ "${1:-}" = "taskserv" ] || [ "${1:-}" = "task" ]; then
|
# taskserv/server/cluster `list` now route to their thin handlers which invoke
|
||||||
if [ "${2:-}" = "list" ] || [ -z "${2:-}" ]; then
|
# the full semantic path (middleware + live provider state). Daemon routing
|
||||||
$NU "$PROVISIONING/core/nulib/scripts/query-taskservs.nu" 2>/dev/null
|
# (for server list/ls/l) is preserved further down in the dispatch case.
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Server list: fast-path (filesystem only) unless --infra is given, which needs live provider data
|
|
||||||
if [ "${1:-}" = "server" ] || [ "${1:-}" = "s" ]; then
|
|
||||||
if [ "${PROVISIONING_DAEMON_MODE:-}" != "true" ] && { [ "${2:-}" = "list" ] || [ "${2:-}" = "l" ] || [ -z "${2:-}" ]; }; then
|
|
||||||
# Check for --infra/-i in remaining args
|
|
||||||
_HAS_INFRA=""
|
|
||||||
for _a in "${@}"; do
|
|
||||||
if [ "$_a" = "--infra" ] || [ "$_a" = "-i" ]; then _HAS_INFRA=1; break; fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$_HAS_INFRA" ]; then
|
|
||||||
# No infra filter — use fast-path (no credentials needed)
|
|
||||||
INFRA_FILTER=""
|
|
||||||
shift
|
|
||||||
{ [ "${1:-}" = "list" ] || [ "${1:-}" = "l" ]; } && shift
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "${1:-}" in
|
|
||||||
--infra | -i) INFRA_FILTER="${2:-}"; shift 2 ;;
|
|
||||||
*) shift ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
export INFRA_FILTER
|
|
||||||
$NU -n -c "source '$PROVISIONING/core/nulib/lib_minimal.nu'; source '$PROVISIONING/core/nulib/scripts/query-servers.nu'" 2>/dev/null
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
# --infra given: fall through to full module for live provider status
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Cluster list (lightweight - reads filesystem only)
|
|
||||||
if [ "${1:-}" = "cluster" ] || [ "${1:-}" = "cl" ]; then
|
|
||||||
if [ "${2:-}" = "list" ] || [ -z "${2:-}" ]; then
|
|
||||||
$NU -n -c "source '$PROVISIONING/core/nulib/lib_minimal.nu'; source '$PROVISIONING/core/nulib/scripts/query-clusters.nu'" 2>/dev/null
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Infra list (lightweight - reads filesystem only)
|
# Infra list (lightweight - reads filesystem only)
|
||||||
if [ "${1:-}" = "infra" ] || [ "${1:-}" = "inf" ]; then
|
if [ "${1:-}" = "infra" ] || [ "${1:-}" = "inf" ]; then
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
# List all clusters in active workspace
|
|
||||||
# This file is sourced by bash after lib_minimal.nu is loaded
|
|
||||||
# Not meant to be run standalone
|
|
||||||
|
|
||||||
# Get active workspace
|
|
||||||
let active_ws = (workspace-active)
|
|
||||||
if ($active_ws | is-empty) {
|
|
||||||
print 'No active workspace'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get workspace path from config
|
|
||||||
let user_config_path = (
|
|
||||||
$env.HOME
|
|
||||||
| path join 'Library'
|
|
||||||
| path join 'Application Support'
|
|
||||||
| path join 'provisioning'
|
|
||||||
| path join 'user_config.yaml'
|
|
||||||
)
|
|
||||||
|
|
||||||
if not ($user_config_path | path exists) {
|
|
||||||
print 'Config not found'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = (open $user_config_path)
|
|
||||||
let workspaces = ($config | get --optional workspaces | default [])
|
|
||||||
let ws = ($workspaces | where { $in.name == $active_ws } | first)
|
|
||||||
|
|
||||||
if ($ws | is-empty) {
|
|
||||||
print 'Workspace not found'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let ws_path = $ws.path
|
|
||||||
|
|
||||||
# List all clusters from workspace
|
|
||||||
let clusters = (
|
|
||||||
if (($ws_path | path join '.clusters') | path exists) {
|
|
||||||
let clusters_path = ($ws_path | path join '.clusters')
|
|
||||||
ls $clusters_path
|
|
||||||
| where type == 'dir'
|
|
||||||
| each {|cl|
|
|
||||||
let cl_name = ($cl.name | path basename)
|
|
||||||
{
|
|
||||||
name: $cl_name
|
|
||||||
path: $cl.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($clusters | length) == 0 {
|
|
||||||
print '🗂️ Available Clusters: (none found)'
|
|
||||||
} else {
|
|
||||||
print '🗂️ Available Clusters:'
|
|
||||||
print ''
|
|
||||||
$clusters | each {|cl|
|
|
||||||
print $" • ($cl.name)"
|
|
||||||
} | ignore
|
|
||||||
}
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
||||||
# List all servers in active workspace
|
|
||||||
# This file is sourced by bash after lib_minimal.nu is loaded
|
|
||||||
# Not meant to be run standalone
|
|
||||||
# Usage: Called from bash with optional $INFRA_FILTER environment variable
|
|
||||||
|
|
||||||
# PWD-based workspace detection: if we're inside a workspace root that has
|
|
||||||
# config/provisioning.ncl, use it — takes precedence over the active workspace.
|
|
||||||
let pwd_config_file = ($env.PWD | path join "config" "provisioning.ncl")
|
|
||||||
use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft]
|
|
||||||
|
|
||||||
def extract-config-string [content: string, key: string] {
|
|
||||||
let pattern = ("(?m)^\\s*" + $key + "\\s*=\\s*\"(?<value>[^\"]+)\"")
|
|
||||||
let matches = ($content | parse --regex $pattern)
|
|
||||||
if ($matches | is-not-empty) {
|
|
||||||
$matches | first | get value | default ""
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pwd_config_raw = if ($pwd_config_file | path exists) {
|
|
||||||
open $pwd_config_file --raw
|
|
||||||
} else { "" }
|
|
||||||
|
|
||||||
let pwd_ws_config = if ($pwd_config_file | path exists) {
|
|
||||||
ncl-eval-soft $pwd_config_file [] {}
|
|
||||||
} else { {} }
|
|
||||||
|
|
||||||
let pwd_ws_name = (
|
|
||||||
$pwd_ws_config
|
|
||||||
| get --optional workspace
|
|
||||||
| default (extract-config-string $pwd_config_raw "workspace")
|
|
||||||
)
|
|
||||||
let pwd_current_infra = (
|
|
||||||
$pwd_ws_config
|
|
||||||
| get --optional current_infra
|
|
||||||
| default (extract-config-string $pwd_config_raw "current_infra")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Convention fallback: if config/provisioning.ncl has no current_infra but
|
|
||||||
# infra/<pwd-basename>/settings.ncl exists, that's the default infra.
|
|
||||||
let pwd_convention_infra = if ($pwd_current_infra | is-empty) {
|
|
||||||
let candidate = ($env.PWD | path join "infra" ($env.PWD | path basename) | path join "settings.ncl")
|
|
||||||
if ($candidate | path exists) { $env.PWD | path basename } else { "" }
|
|
||||||
} else { "" }
|
|
||||||
|
|
||||||
let pwd_infra = if ($pwd_current_infra | is-not-empty) { $pwd_current_infra } else { $pwd_convention_infra }
|
|
||||||
|
|
||||||
# Resolve workspace: PWD-inferred takes precedence over session active workspace
|
|
||||||
let ws_path = if ($pwd_config_file | path exists) {
|
|
||||||
# We are inside the workspace root — PWD is the workspace path
|
|
||||||
$env.PWD
|
|
||||||
} else {
|
|
||||||
# Fall back to active workspace from user_config.yaml
|
|
||||||
let ws_result = (workspace-active)
|
|
||||||
let active_ws = if (is-ok $ws_result) { $ws_result.ok } else { "" }
|
|
||||||
if ($active_ws | is-empty) {
|
|
||||||
print 'No active workspace. Run: provisioning workspace activate <name>'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_config_path = (
|
|
||||||
$env.HOME | path join 'Library' | path join 'Application Support'
|
|
||||||
| path join 'provisioning' | path join 'user_config.yaml'
|
|
||||||
)
|
|
||||||
if not ($user_config_path | path exists) {
|
|
||||||
print 'Config not found'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
let config = (open $user_config_path)
|
|
||||||
let workspaces = ($config | get --optional workspaces | default [])
|
|
||||||
let ws = ($workspaces | where { $in.name == $active_ws } | first)
|
|
||||||
if ($ws | is-empty) {
|
|
||||||
print $"Workspace '($active_ws)' not found in user config"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$ws.path
|
|
||||||
}
|
|
||||||
|
|
||||||
let infra_path = ($ws_path | path join 'infra')
|
|
||||||
if not ($infra_path | path exists) {
|
|
||||||
print 'No infrastructures found'
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Resolve filter: explicit INFRA_FILTER > PWD current_infra > convention > workspace default_infra
|
|
||||||
let filter_raw = ($env.INFRA_FILTER? | default "")
|
|
||||||
let filter = if ($filter_raw | is-not-empty) {
|
|
||||||
$filter_raw | path basename
|
|
||||||
} else if ($pwd_infra | is-not-empty) {
|
|
||||||
$pwd_infra
|
|
||||||
} else {
|
|
||||||
# Last resort: registered workspace default_infra from user config
|
|
||||||
let ws_result2 = (workspace-active)
|
|
||||||
let active_ws2 = if (is-ok $ws_result2) { $ws_result2.ok } else { "" }
|
|
||||||
if ($active_ws2 | is-not-empty) {
|
|
||||||
let uc = (
|
|
||||||
$env.HOME | path join 'Library' | path join 'Application Support'
|
|
||||||
| path join 'provisioning' | path join 'user_config.yaml'
|
|
||||||
)
|
|
||||||
if ($uc | path exists) {
|
|
||||||
let wlist = (open $uc | get --optional workspaces | default [])
|
|
||||||
let wentry = ($wlist | where { $in.name == $active_ws2 } | first)
|
|
||||||
$wentry | get --optional default_infra | default ""
|
|
||||||
} else { "" }
|
|
||||||
} else { "" }
|
|
||||||
}
|
|
||||||
|
|
||||||
# List server definitions from infrastructure (filtered if --infra specified)
|
|
||||||
let servers = (
|
|
||||||
ls $infra_path
|
|
||||||
| where type == 'dir'
|
|
||||||
| each {|infra|
|
|
||||||
let infra_name = ($infra.name | path basename)
|
|
||||||
|
|
||||||
# Skip if filter is specified and doesn't match
|
|
||||||
if (($filter | is-not-empty) and ($infra_name != $filter)) {
|
|
||||||
[]
|
|
||||||
} else {
|
|
||||||
# servers.ncl can live directly in infra dir or under defs/
|
|
||||||
let infra_dir = ($infra_path | path join $infra_name)
|
|
||||||
let servers_file_direct = ($infra_dir | path join 'servers.ncl')
|
|
||||||
let servers_file_defs = ($infra_dir | path join 'defs' | path join 'servers.ncl')
|
|
||||||
let servers_file = if ($servers_file_direct | path exists) {
|
|
||||||
$servers_file_direct
|
|
||||||
} else {
|
|
||||||
$servers_file_defs
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($servers_file | path exists) {
|
|
||||||
# Parse servers.ncl: correlate hostname / server_type / private_ip per block.
|
|
||||||
# Strategy: scan lines in order; a new server block begins at each `make_server {`
|
|
||||||
# (or at the first hostname = "..." after the previous block closes).
|
|
||||||
# We accumulate fields until the next block starts.
|
|
||||||
let lines = (open $servers_file --raw | split row "\n")
|
|
||||||
let extract_quoted = {|line|
|
|
||||||
let parts = ($line | split row '"')
|
|
||||||
if ($parts | length) >= 2 { $parts | get 1 } else { "" }
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build one record per server by scanning lines top-to-bottom.
|
|
||||||
# Reset on each `make_server {` boundary.
|
|
||||||
let parsed = (
|
|
||||||
$lines | reduce --fold {blocks: [], cur: {hostname: "", server_type: "", private_ip: ""}} {|line, acc|
|
|
||||||
let trimmed = ($line | str trim)
|
|
||||||
if ($trimmed =~ 'make_server\s*\{') {
|
|
||||||
# flush previous if it had a hostname
|
|
||||||
let blocks = if ($acc.cur.hostname | is-not-empty) {
|
|
||||||
$acc.blocks | append $acc.cur
|
|
||||||
} else {
|
|
||||||
$acc.blocks
|
|
||||||
}
|
|
||||||
{blocks: $blocks, cur: {hostname: "", server_type: "", private_ip: ""}}
|
|
||||||
} else if ($trimmed =~ '^hostname\s*=\s*"') {
|
|
||||||
{blocks: $acc.blocks, cur: ($acc.cur | upsert hostname (do $extract_quoted $trimmed))}
|
|
||||||
} else if ($trimmed =~ '^server_type\s*=\s*"') {
|
|
||||||
{blocks: $acc.blocks, cur: ($acc.cur | upsert server_type (do $extract_quoted $trimmed))}
|
|
||||||
} else if ($trimmed =~ '^private_ip\s*=\s*"') {
|
|
||||||
{blocks: $acc.blocks, cur: ($acc.cur | upsert private_ip (do $extract_quoted $trimmed))}
|
|
||||||
} else {
|
|
||||||
$acc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# flush last block
|
|
||||||
let all_blocks = if ($parsed.cur.hostname | is-not-empty) {
|
|
||||||
$parsed.blocks | append $parsed.cur
|
|
||||||
} else {
|
|
||||||
$parsed.blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
$all_blocks
|
|
||||||
| where {|b| $b.hostname | is-not-empty }
|
|
||||||
| each {|b|
|
|
||||||
{
|
|
||||||
name: $b.hostname
|
|
||||||
infrastructure: $infra_name
|
|
||||||
server_type: $b.server_type
|
|
||||||
private_ip: $b.private_ip
|
|
||||||
path: $servers_file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| flatten
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read persisted server state (written by server_create workflow post-sync)
|
|
||||||
# Key: server name → { provider_id, public_ip, location, status, floating_ip, floating_ip_address }
|
|
||||||
let cached_state_path = ($ws_path | path join "infra" | path join $filter | path join ".servers-state.json")
|
|
||||||
let cached_state = if ($filter | is-not-empty) and ($cached_state_path | path exists) {
|
|
||||||
open $cached_state_path
|
|
||||||
} else { {} }
|
|
||||||
|
|
||||||
# Bootstrap state: FIP name → actual IP (fallback when server not in cached_state)
|
|
||||||
let bs_state_path = ($ws_path | path join ".provisioning-state.json")
|
|
||||||
let bs_fips = if ($bs_state_path | path exists) {
|
|
||||||
open $bs_state_path | get -o bootstrap.floating_ips | default {}
|
|
||||||
} else { {} }
|
|
||||||
|
|
||||||
# Query live status from hcloud for real-time status updates
|
|
||||||
let hcloud_res = (do { ^hcloud server list -o json } | complete)
|
|
||||||
let live_servers_all = if $hcloud_res.exit_code == 0 and ($hcloud_res.stdout | str trim | is-not-empty) {
|
|
||||||
let parsed = ($hcloud_res.stdout | from json)
|
|
||||||
if (($parsed | describe) | str starts-with "list") { $parsed } else { [] }
|
|
||||||
} else { [] }
|
|
||||||
let live_servers = if ($filter | is-not-empty) {
|
|
||||||
$live_servers_all | where {|l| ($servers | any {|s| $s.name == $l.name }) }
|
|
||||||
} else {
|
|
||||||
$live_servers_all
|
|
||||||
}
|
|
||||||
|
|
||||||
def status_icon [s: string] {
|
|
||||||
match $s {
|
|
||||||
"running" => "🟢"
|
|
||||||
"off" => "🔴"
|
|
||||||
"starting" => "🟡"
|
|
||||||
"stopping" => "🟡"
|
|
||||||
"rebuilding" => "🔵"
|
|
||||||
"migrating" => "🔵"
|
|
||||||
_ => "⚪"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($servers | length) == 0 {
|
|
||||||
print '📦 Available Servers: (none configured)'
|
|
||||||
} else {
|
|
||||||
print ''
|
|
||||||
let rows = ($servers | each {|srv|
|
|
||||||
let live = ($live_servers | where {|l| $l.name == $srv.name} | first | default null)
|
|
||||||
let cached = ($cached_state | get -o $srv.name | default null)
|
|
||||||
|
|
||||||
# Status: hcloud live > cached state > unknown
|
|
||||||
let status = if $live != null { $live.status } else if $cached != null { $cached.status } else { "—" }
|
|
||||||
|
|
||||||
# Public IP: hcloud live > cached state
|
|
||||||
let pub_ip = if $live != null {
|
|
||||||
$live.public_net?.ipv4?.ip? | default ""
|
|
||||||
} else if $cached != null {
|
|
||||||
$cached.public_ip? | default ""
|
|
||||||
} else { "" }
|
|
||||||
|
|
||||||
# Private IP: hcloud live (actual) > NCL (desired)
|
|
||||||
let priv_ip = if $live != null {
|
|
||||||
$live.private_net? | default [] | first | default null | get --optional ip | default ""
|
|
||||||
} else {
|
|
||||||
$srv.private_ip? | default ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# Server type: hcloud live > NCL (type is config, not runtime state)
|
|
||||||
let srv_type = if $live != null {
|
|
||||||
$live.server_type?.name? | default ($srv.server_type? | default "")
|
|
||||||
} else {
|
|
||||||
$srv.server_type? | default ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# Location: hcloud live > cached state
|
|
||||||
let location = if $live != null {
|
|
||||||
$live.datacenter?.location?.name? | default ""
|
|
||||||
} else if $cached != null {
|
|
||||||
$cached.location? | default ""
|
|
||||||
} else { "" }
|
|
||||||
|
|
||||||
# Floating IP: cached state (has name+ip) > bootstrap state lookup by FIP name
|
|
||||||
let fip_display = if $cached != null and ($cached.floating_ip? | default "" | is-not-empty) {
|
|
||||||
let fip_ip = ($cached.floating_ip_address? | default "")
|
|
||||||
if ($fip_ip | is-not-empty) {
|
|
||||||
$"($cached.floating_ip) ($fip_ip)"
|
|
||||||
} else {
|
|
||||||
$cached.floating_ip
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
# Fallback: resolve FIP IP from bootstrap state using the FIP name in NCL
|
|
||||||
let fip_name = ($srv | get -o floating_ip | default "")
|
|
||||||
if ($fip_name | is-not-empty) {
|
|
||||||
let fip_key = ($fip_name | str replace --all "librecloud-fip-" "" | str replace --all "-" "_")
|
|
||||||
let fip_rec = ($bs_fips | get -o $fip_key | default null)
|
|
||||||
if $fip_rec != null {
|
|
||||||
$"($fip_name) ($fip_rec.ip? | default "")"
|
|
||||||
} else { $fip_name }
|
|
||||||
} else { "" }
|
|
||||||
}
|
|
||||||
|
|
||||||
# Delete protection: hcloud live > cached state
|
|
||||||
let protected = if $live != null {
|
|
||||||
$live.protection?.delete? | default false
|
|
||||||
} else if $cached != null {
|
|
||||||
$cached.protection_delete? | default false
|
|
||||||
} else { false }
|
|
||||||
let lock_icon = if $protected { "🔒" } else { "" }
|
|
||||||
|
|
||||||
{
|
|
||||||
hostname: $srv.name
|
|
||||||
type: $srv_type
|
|
||||||
location: $location
|
|
||||||
status: $status
|
|
||||||
public_ip: $pub_ip
|
|
||||||
private_ip: $priv_ip
|
|
||||||
floating_ip: $fip_display
|
|
||||||
protected: $lock_icon
|
|
||||||
provider: "hetzner"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print ($rows | table -i false)
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#!/usr/bin/env nu
|
|
||||||
# List all available components/taskservs.
|
|
||||||
# Searches extensions/components/ (flat, primary) then extensions/taskservs/ (grouped, legacy).
|
|
||||||
|
|
||||||
use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft]
|
|
||||||
|
|
||||||
let provisioning = ($env.PROVISIONING? | default '/usr/local/provisioning')
|
|
||||||
|
|
||||||
# Resolve component base path: components/ → taskservs/ (legacy fallback)
|
|
||||||
let components_base = ($provisioning | path join 'extensions' | path join 'components')
|
|
||||||
let taskservs_base = ($provisioning | path join 'extensions' | path join 'taskservs')
|
|
||||||
|
|
||||||
mut all_items = []
|
|
||||||
|
|
||||||
# Primary: flat components/ (post-migration)
|
|
||||||
if ($components_base | path exists) {
|
|
||||||
for item in (ls $components_base | where type == 'dir') {
|
|
||||||
let name = ($item.name | path basename)
|
|
||||||
let meta = ($item.name | path join 'metadata.ncl')
|
|
||||||
let modes = if ($meta | path exists) {
|
|
||||||
let result = (ncl-eval-soft $meta [] null)
|
|
||||||
if ($result | is-not-empty) { $result | get -o modes | default ['taskserv'] } else { ['taskserv'] }
|
|
||||||
} else { ['taskserv'] }
|
|
||||||
$all_items = ($all_items | append { task: $name, mode: ($modes | str join ','), info: 'component' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Legacy: grouped taskservs/ (only if not already found in components/)
|
|
||||||
if ($taskservs_base | path exists) {
|
|
||||||
let known = ($all_items | each {|i| $i.task })
|
|
||||||
for cat in (ls $taskservs_base | where type == 'dir') {
|
|
||||||
let category = ($cat.name | path basename)
|
|
||||||
for ts in (ls $cat.name | where type == 'dir') {
|
|
||||||
let ts_name = ($ts.name | path basename)
|
|
||||||
if $ts_name not-in $known {
|
|
||||||
$all_items = ($all_items | append { task: $ts_name, mode: $category, info: 'taskserv' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($all_items | is-empty) {
|
|
||||||
print '📦 Available Taskservs: (none found)'
|
|
||||||
} else {
|
|
||||||
print '📦 Available Taskservs:'
|
|
||||||
print ''
|
|
||||||
$all_items | sort-by task | each {|ts|
|
|
||||||
print $" • ($ts.task) [($ts.mode)]"
|
|
||||||
} | ignore
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue