prvng_core/nulib/main_provisioning/extensions.nu
Jesús Pérez 894046ef5a
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
    config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
    Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
    WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
  - Unified Component Architecture: components/mod.nu, main_provisioning/
    {components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
    topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
  - Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
    JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
    change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
    ADDING_COMMANDS.md documents new-command workflow.
  - Platform service manager: service-manager.nu (+573), startup.nu (+611),
    service-check.nu (+255); autostart/bootstrap/health/target refactored.
  - Nushell 0.112.2 migration: removed all try/catch and bash redirections;
    external commands prefixed with ^; type signatures enforced. Driven by
    scripts/refactor-try-catch{,-simplified}.nu.
  - TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
    tty-filter.sh, tty-commands.conf.
  - New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
    main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
    build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
  - Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
    refactored (-454), removed legacy loaders/file_loader.nu (-330).
  - Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
  - Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
    test_services.
  - README + CHANGELOG updated.
2026-04-17 04:27:33 +01:00

224 lines
6.8 KiB
Text

# Extensions Management Commands
use ../lib_provisioning/extensions *
use ../lib_provisioning/config/accessor.nu *
use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft, default-ncl-paths]
# Resolve the taskservs directory: PROVISIONING_TASKSERVS_PATH → config → $PROVISIONING/extensions/taskservs.
def resolve-taskservs-dir [] : nothing -> string {
let from_env = ($env.PROVISIONING_TASKSERVS_PATH? | default "")
if ($from_env | is-not-empty) { return $from_env }
let from_config = (get-taskservs-path)
if ($from_config | is-not-empty) { return $from_config }
($env.PROVISIONING? | default "/usr/local/provisioning") | path join "extensions" "taskservs"
}
# Load metadata.ncl for each taskserv via nickel export and aggregate provides/requires/conflicts_with.
def load-taskserv-capabilities [] : nothing -> list<record> {
let ts_dir = (resolve-taskservs-dir)
if not ($ts_dir | path exists) { return [] }
glob ($ts_dir | path join "*")
| where ($it | path type) == "dir"
| each { |ts_path|
let meta_path = ($ts_path | path join "metadata.ncl")
if not ($meta_path | path exists) {
null
} else {
let prov = ($env.PROVISIONING? | default "/usr/local/provisioning")
let m = (ncl-eval-soft $meta_path (default-ncl-paths "") null)
if ($m | is-not-empty) {
{
name: $m.name,
version: $m.version,
description: $m.description,
provides: ($m.provides? | default []),
requires: ($m.requires? | default []),
conflicts_with: ($m.conflicts_with? | default []),
}
} else { null }
}
}
| where ($it != null)
}
# List available extensions
export def "main extensions list" [
--type: string = "" # Filter by type: provider, taskserv, or all
--helpinfo (-h) # Show help
] {
if $helpinfo {
print "List available extensions"
return
}
match $type {
"provider" => {
print "Available Provider Extensions:"
list-providers
}
"taskserv" => {
print "Available TaskServ Extensions:"
list-taskservs
}
_ => {
print "Available Extensions:"
print "\nProviders:"
list-providers
print "\nTaskServs:"
list-taskservs
}
}
}
# Show extension details
export def "main extensions show" [
name: string # Extension name
--helpinfo (-h) # Show help
] {
if $helpinfo {
print "Show details for a specific extension"
return
}
let provider = (get-provider $name)
let taskserv = (get-taskserv $name)
if ($provider | is-not-empty) {
print $"Provider Extension: ($name)"
$provider
} else if ($taskserv | is-not-empty) {
print $"TaskServ Extension: ($name)"
$taskserv
} else {
print $"Extension '($name)' not found"
}
}
# Initialize extensions
export def "main extensions init" [
--helpinfo (-h) # Show help
] {
if $helpinfo {
print "Initialize extension registry"
return
}
init-registry
print "Extension registry initialized"
}
# Show current profile
export def "main profile show" [
--helpinfo (-h) # Show help
] {
if $helpinfo {
print "Show current access profile"
return
}
show-profile | table
}
# Create example profiles
export def "main profile create-examples" [
--helpinfo (-h) # Show help
] {
if $helpinfo {
print "Create example profile files"
return
}
create-example-profiles
}
# List capability declarations across all taskservs (provides + requires).
export def "main extensions capabilities" [
--type (-t): string = "all" # Filter: "provides", "requires", or "all"
--helpinfo (-h) # Show help
] : nothing -> any {
if $helpinfo {
print "List capability declarations across all taskservs"
print " --type: provides | requires | all (default: all)"
return
}
let caps = (load-taskserv-capabilities)
if ($caps | is-empty) {
print "No taskservs found or metadata.ncl missing."
return
}
match $type {
"provides" => {
$caps | each { |ts|
$ts.provides | each { |p| { taskserv: $ts.name, provides_id: $p.id, version: $p.version, interface: $p.interface } }
} | flatten | table
}
"requires" => {
$caps | each { |ts|
$ts.requires | each { |r| { taskserv: $ts.name, capability: $r.capability, kind: $r.kind } }
} | flatten | table
}
_ => {
$caps | each { |ts|
{
taskserv: $ts.name,
provides: ($ts.provides | each { |p| $p.id } | str join ", "),
requires: ($ts.requires | each { |r| $"($r.capability)[($r.kind)]" } | str join ", "),
conflicts_with: ($ts.conflicts_with | str join ", "),
}
} | table
}
}
}
# Show inter-extension dependency graph derived from provides/requires metadata.
export def "main extensions graph" [
--format (-f): string = "table" # Output format: table, dot
--helpinfo (-h) # Show help
] : nothing -> any {
if $helpinfo {
print "Show inter-extension dependency graph from provides/requires metadata"
print " --format: table | dot (default: table)"
return
}
let caps = (load-taskserv-capabilities)
if ($caps | is-empty) {
print "No taskservs found."
return
}
# Build provides index: capability_id -> taskserv name
let provides_index = ($caps | each { |ts|
$ts.provides | each { |p| { cap: $p.id, provider: $ts.name } }
} | flatten)
# Build edges: (requirer, capability, provider, kind)
let edges = ($caps | each { |ts|
$ts.requires | each { |r|
let provider = ($provides_index | where cap == $r.capability | get provider?.0 | default "unresolved")
{ from: $ts.name, capability: $r.capability, to: $provider, kind: $r.kind }
}
} | flatten)
match $format {
"table" => {
$edges | table
}
"dot" => {
print "digraph extensions {"
print " rankdir=LR;"
for edge in $edges {
let style = if $edge.kind == "Required" { "" } else { " style=dashed" }
print $" \"($edge.from)\" -> \"($edge.to)\" [label=\"($edge.capability)\"($style)];"
}
print "}"
}
_ => {
error make { msg: $"Unknown format '($format)'. Valid: table, dot" }
}
}
}