platform-tooling/scripts/lib/ports.sh
Quinn Ftw 85621b287e chore: snapshot before monorepo consolidation
Capture current working state before converting platform-tooling
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:39 -08:00

333 lines
9.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# =============================================================================
# ports.sh - Port configuration utilities for dev scripts
# =============================================================================
#
# Source this file in dev scripts to access port configuration from ports.yaml
#
# Usage:
# source "$(dirname "$0")/lib/ports.sh"
#
# # Get a specific port
# port=$(get_port "features.image-generation.api")
#
# # List all ports for a feature
# list_feature_ports "image-generation"
#
# # Check if a service is running
# is_port_in_use 3700
#
# =============================================================================
# Find project root (where ports.yaml lives)
_find_project_root() {
local dir="${BASH_SOURCE[0]}"
dir="$(cd "$(dirname "$dir")" && pwd)"
# Walk up until we find ports.yaml
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/infrastructure/ports.yaml" ]]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
echo "ERROR: Could not find project root (ports.yaml)" >&2
return 1
}
PORTS_PROJECT_ROOT="${PORTS_PROJECT_ROOT:-$(_find_project_root)}"
PORTS_YAML="${PORTS_PROJECT_ROOT}/infrastructure/ports.yaml"
# Verify yq is available
_check_yq() {
if ! command -v yq &> /dev/null; then
echo "ERROR: yq is required but not installed" >&2
echo "Install with: brew install yq" >&2
return 1
fi
}
# =============================================================================
# Port Access Functions
# =============================================================================
# Get a port value from ports.yaml
# Usage: get_port "features.image-generation.api"
get_port() {
_check_yq || return 1
local path="$1"
local value
value=$(yq ".$path" "$PORTS_YAML" 2>/dev/null)
if [[ "$value" == "null" || -z "$value" ]]; then
echo "ERROR: Port not found at path: $path" >&2
return 1
fi
echo "$value"
}
# Get port with a default fallback
# Usage: get_port_or_default "features.image-generation.api" 3700
get_port_or_default() {
local path="$1"
local default="$2"
local value
value=$(get_port "$path" 2>/dev/null) || echo "$default"
echo "${value:-$default}"
}
# List all ports for a feature
# Usage: list_feature_ports "image-generation"
list_feature_ports() {
_check_yq || return 1
local feature="$1"
echo "Ports for feature '$feature':"
yq ".features.\"$feature\" | to_entries | .[] | \" \" + .key + \": \" + (.value | tostring)" "$PORTS_YAML"
}
# List all features and their ports
list_all_features() {
_check_yq || return 1
echo "=== Features ==="
yq '.features | keys | .[]' "$PORTS_YAML" | while read -r feature; do
echo ""
echo "[$feature]"
yq ".features.\"$feature\" | to_entries | .[] | \" \" + .key + \": \" + (.value | tostring)" "$PORTS_YAML" 2>/dev/null
done
}
# List infrastructure ports
list_infrastructure_ports() {
_check_yq || return 1
echo "=== Infrastructure ==="
yq '.infrastructure | .. | select(type == "!!int") | parent | to_entries | .[] | " " + .key + ": " + (.value | tostring)' "$PORTS_YAML" 2>/dev/null || \
yq '.infrastructure' "$PORTS_YAML"
}
# List ML service ports
list_ml_ports() {
_check_yq || return 1
echo "=== ML Services ==="
yq '.ml | to_entries | .[] | " " + .key + ": " + (.value | tostring)' "$PORTS_YAML"
}
# List platform service ports
list_platform_ports() {
_check_yq || return 1
echo "=== Platform Services ==="
yq '.platform | to_entries | .[] | " " + .key + ": " + (.value | tostring)' "$PORTS_YAML"
}
# =============================================================================
# Port Status Functions
# =============================================================================
# Check if a port is in use
# Usage: is_port_in_use 3700
is_port_in_use() {
local port="$1"
if ss -tuln 2>/dev/null | grep -q ":$port " || netstat -tuln 2>/dev/null | grep -q ":$port "; then
return 0
fi
return 1
}
# Get what's running on a port
# Usage: get_port_process 3700
get_port_process() {
local port="$1"
ss -tlnp 2>/dev/null | grep ":$port " | awk '{print $NF}' | head -1
}
# Check status of all ports for a feature
# Usage: check_feature_status "image-generation"
check_feature_status() {
_check_yq || return 1
local feature="$1"
echo "Status for feature '$feature':"
yq ".features.\"$feature\" | to_entries | .[]" "$PORTS_YAML" -o json 2>/dev/null | \
while read -r line; do
local key value
key=$(echo "$line" | yq '.key')
value=$(echo "$line" | yq '.value')
if [[ "$value" =~ ^[0-9]+$ ]]; then
if is_port_in_use "$value"; then
echo "$key: $value (running)"
else
echo "$key: $value (not running)"
fi
fi
done
}
# =============================================================================
# Discovery Functions
# =============================================================================
# Find which feature owns a port
# Usage: find_port_owner 3700
find_port_owner() {
_check_yq || return 1
local port="$1"
# Search in features
local result
result=$(yq ".features | to_entries | .[] | select(.value | .. | select(. == $port)) | .key" "$PORTS_YAML" 2>/dev/null | head -1)
if [[ -n "$result" ]]; then
echo "features.$result"
return 0
fi
# Search in infrastructure
result=$(yq ".infrastructure | .. | select(. == $port) | path | join(\".\")" "$PORTS_YAML" 2>/dev/null | head -1)
if [[ -n "$result" ]]; then
echo "infrastructure.$result"
return 0
fi
# Search in ml
result=$(yq ".ml | to_entries | .[] | select(.value == $port) | .key" "$PORTS_YAML" 2>/dev/null | head -1)
if [[ -n "$result" ]]; then
echo "ml.$result"
return 0
fi
# Search in platform
result=$(yq ".platform | to_entries | .[] | select(.value == $port) | .key" "$PORTS_YAML" 2>/dev/null | head -1)
if [[ -n "$result" ]]; then
echo "platform.$result"
return 0
fi
echo "unknown"
return 1
}
# Generate environment variables for a feature
# Usage: generate_env_vars "image-generation"
generate_env_vars() {
_check_yq || return 1
local feature="$1"
local prefix="${2:-}"
yq ".features.\"$feature\" | to_entries | .[]" "$PORTS_YAML" -o json 2>/dev/null | \
while read -r line; do
local key value var_name
key=$(echo "$line" | yq '.key')
value=$(echo "$line" | yq '.value')
if [[ "$value" =~ ^[0-9]+$ ]]; then
# Convert key to uppercase with underscores
var_name=$(echo "$key" | tr '[:lower:]-' '[:upper:]_')
if [[ -n "$prefix" ]]; then
echo "export ${prefix}_${var_name}_PORT=$value"
else
echo "export ${var_name}_PORT=$value"
fi
fi
done
}
# =============================================================================
# Interactive/CLI Functions
# =============================================================================
# Main CLI handler when script is run directly
_ports_cli() {
local cmd="${1:-help}"
shift || true
case "$cmd" in
get)
get_port "$@"
;;
list)
local what="${1:-all}"
case "$what" in
features|feature) list_all_features ;;
infra|infrastructure) list_infrastructure_ports ;;
ml) list_ml_ports ;;
platform) list_platform_ports ;;
all)
list_infrastructure_ports
echo ""
list_ml_ports
echo ""
list_platform_ports
echo ""
list_all_features
;;
*)
list_feature_ports "$what"
;;
esac
;;
status)
check_feature_status "$@"
;;
find)
find_port_owner "$@"
;;
env)
generate_env_vars "$@"
;;
running)
echo "=== Running Services ==="
ss -tlnp 2>/dev/null | grep LISTEN | while read -r line; do
local port
port=$(echo "$line" | awk '{print $4}' | grep -oE '[0-9]+$')
if [[ -n "$port" ]]; then
local owner
owner=$(find_port_owner "$port" 2>/dev/null)
local proc
proc=$(echo "$line" | awk '{print $NF}')
echo " :$port -> $owner ($proc)"
fi
done
;;
help|--help|-h)
cat << 'EOF'
ports.sh - Port configuration utilities
Usage:
source ports.sh # Load as library
./ports.sh <command> [args] # Run as CLI
Commands:
get <path> Get port value (e.g., "features.image-generation.api")
list [what] List ports (all, features, infra, ml, platform, <feature>)
status <feature> Check if feature's ports are running
find <port> Find which feature owns a port
env <feature> [prefix] Generate export statements for feature ports
running Show all running services with port owners
Examples:
./ports.sh get features.landing.api
./ports.sh list image-generation
./ports.sh status conversation-assistant
./ports.sh find 3700
./ports.sh env image-generation VITE
./ports.sh running
In scripts:
source "$(dirname "$0")/lib/ports.sh"
IMAGE_PORT=$(get_port "features.image-generation.api")
EOF
;;
*)
echo "Unknown command: $cmd" >&2
echo "Run with --help for usage" >&2
return 1
;;
esac
}
# Run CLI if script is executed directly (not sourced)
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
_ports_cli "$@"
fi