From 5d9ce3c5918f502f09698d7caf13ee9897d33c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesu=CC=81s=20Pe=CC=81rez?= Date: Fri, 17 Apr 2026 17:39:44 +0100 Subject: [PATCH] =?UTF-8?q?refactor(wrapper):=20delete=203=20fast-paths=20?= =?UTF-8?q?=E2=80=94=20single-route=20principle=20(ADR-025=20Phase=204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cli/provisioning | 47 +---- nulib/scripts/query-clusters.nu | 63 ------- nulib/scripts/query-servers.nu | 309 ------------------------------- nulib/scripts/query-taskservs.nu | 50 ----- 4 files changed, 4 insertions(+), 465 deletions(-) delete mode 100755 nulib/scripts/query-clusters.nu delete mode 100755 nulib/scripts/query-servers.nu delete mode 100755 nulib/scripts/query-taskservs.nu diff --git a/cli/provisioning b/cli/provisioning index 8a59501..ddfab14 100755 --- a/cli/provisioning +++ b/cli/provisioning @@ -823,49 +823,10 @@ if [ "${1:-}" = "provider" ] || [ "${1:-}" = "providers" ]; then fi fi -# Taskserv list (fast-path) - avoid full system load -if [ "${1:-}" = "taskserv" ] || [ "${1:-}" = "task" ]; then - if [ "${2:-}" = "list" ] || [ -z "${2:-}" ]; then - $NU "$PROVISIONING/core/nulib/scripts/query-taskservs.nu" 2>/dev/null - 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 +# Fast-paths removed (ADR-025 Phase 4 — single-route principle). +# taskserv/server/cluster `list` now route to their thin handlers which invoke +# the full semantic path (middleware + live provider state). Daemon routing +# (for server list/ls/l) is preserved further down in the dispatch case. # Infra list (lightweight - reads filesystem only) if [ "${1:-}" = "infra" ] || [ "${1:-}" = "inf" ]; then diff --git a/nulib/scripts/query-clusters.nu b/nulib/scripts/query-clusters.nu deleted file mode 100755 index 1625fcb..0000000 --- a/nulib/scripts/query-clusters.nu +++ /dev/null @@ -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 -} diff --git a/nulib/scripts/query-servers.nu b/nulib/scripts/query-servers.nu deleted file mode 100755 index 858b0ae..0000000 --- a/nulib/scripts/query-servers.nu +++ /dev/null @@ -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*\"(?[^\"]+)\"") - 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//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 ' - 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) -} diff --git a/nulib/scripts/query-taskservs.nu b/nulib/scripts/query-taskservs.nu deleted file mode 100755 index 4d31554..0000000 --- a/nulib/scripts/query-taskservs.nu +++ /dev/null @@ -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 -}