e969234ca5
gates / consistency-and-conventions (push) Successful in 1m28s
- lib/registry.sh: shared query API over POS_* headers (reg_scan, reg_list, reg_lookup, reg_tools_in, reg_each, reg_config_scopes/keys). Replaces per-consumer sed/grep header parsing. - bin/pos-tree + bin/pos _pos_category_help(): migrated to registry API. Category help now shows [deps: ...] annotations. Tree output preserved. - New optional headers # POS_DEPS: and # POS_EXAMPLES: in tool metadata. Added to pos-network-download (aria2c jq curl), pos-media-sync (lsblk jq), pos-system-backup (tar), pos-docker-ps (docker) as initial adopters. - scripts/gen-docs.sh: extended tools array with deps/examples fields; conditional column rendering in gen_dispatch; deps annotation in gen_tree. Fixed URL-unsafe // joiner (→ middle dot ·) and \x1f caption delimiter collision in config-ui. - bin/pos-ai-alias: rewrote activation from bash aliases (source-time-frozen) to executable wrapper scripts at ~/.local/bin. Staleness eliminated: edits apply on next invocation with no shell reload. _alias_sync() reconciliation on every subcommand, marker-guarded lifecycle, collision refusal, legacy .sh retirement. Fixed dup-table bug (option 4 no-op). - lib/config-ui.sh: @caption/@[KEY=alt] conditional captions, *providers=<tag> tagged wildcards, uniform typography tier (bold/cyan/dim), honest prompt. Active provider keys bold, inactive dimmed with reason. Backward-compatible. - bin/pos-system-uninstall: marker-scan for wrapper script cleanup. - Docs synced: AGENTS.md (new headers + registry), DOC/SCRIPTS.md (registry section + lib list), DOC/POS.md (alias wrapper activation), MAINTENANCE.md (M-024). Lint fixed: pos-ai-alias registered in INTERACTIVE_CMDS. Gates: make gen && make check && make lint = 0 FAIL, 0 WARN
127 lines
4.0 KiB
Bash
Executable File
127 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# POS: docker ps — Enhanced container overview (health, IPs, ports, uptime)
|
|
# POS_DEPS: docker
|
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: pos docker ps
|
|
|
|
Display enhanced Docker container overview with health status,
|
|
uptime, IPs, and port mappings.
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
command -v docker &>/dev/null || err "docker not found — install it with: sudo apt install docker.io"
|
|
|
|
case "${1:-}" in
|
|
-h|--help) usage ;;
|
|
esac
|
|
|
|
if ! container_ids=$(docker ps -q 2>/dev/null) || [[ -z "$container_ids" ]]; then
|
|
echo "No running containers."
|
|
exit 0
|
|
fi
|
|
|
|
healthy_count=0
|
|
unhealthy_count=0
|
|
running_count=0
|
|
total_count=0
|
|
|
|
printf "%-28s %-35s %-22s %-10s %-35s %-20s %s\n" \
|
|
"NAME" "IMAGE" "STATUS(HEALTH)" "UPTIME" "IPS" "PORTS" "CONTAINER ID"
|
|
|
|
for cid in $container_ids; do
|
|
total_count=$((total_count + 1))
|
|
|
|
info=$(docker inspect "$cid" 2>/dev/null) || continue
|
|
|
|
name=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Name'].lstrip('/'))" 2>/dev/null)
|
|
image=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Config']['Image'])" 2>/dev/null)
|
|
status=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['State']['Status'])" 2>/dev/null)
|
|
health=$(echo "$info" | python3 -c "
|
|
import sys,json
|
|
d=json.load(sys.stdin)[0]
|
|
h=d.get('State',{}).get('Health',{})
|
|
print(h.get('Status','') if h else '')
|
|
" 2>/dev/null || true)
|
|
started_at=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['State']['StartedAt'])" 2>/dev/null)
|
|
ip_data=$(echo "$info" | python3 -c "
|
|
import sys,json
|
|
d=json.load(sys.stdin)[0]
|
|
nets=d.get('NetworkSettings',{}).get('Networks',{})
|
|
out=' '.join(f'{k}:{v.get(\"IPAddress\",\"\")}' for k,v in nets.items() if v.get('IPAddress'))
|
|
print(out or '-')
|
|
" 2>/dev/null)
|
|
port_data=$(echo "$info" | python3 -c "
|
|
import sys,json
|
|
d=json.load(sys.stdin)[0]
|
|
ports=d.get('NetworkSettings',{}).get('Ports',{}) or {}
|
|
parts=[]
|
|
for container_port, bindings in ports.items():
|
|
if bindings:
|
|
for b in bindings:
|
|
hp=b.get('HostPort','')
|
|
hi=b.get('HostIp','')
|
|
if hi and hp:
|
|
parts.append(f'{hi}:{hp}->{container_port}')
|
|
elif hp:
|
|
parts.append(f'{hp}->{container_port}')
|
|
else:
|
|
parts.append(container_port)
|
|
else:
|
|
parts.append(container_port)
|
|
print(', '.join(parts) if parts else '-')
|
|
" 2>/dev/null)
|
|
|
|
[ "${#image}" -gt 35 ] && image="${image:0:32}..."
|
|
|
|
if [ "$status" = "running" ]; then
|
|
running_count=$((running_count + 1))
|
|
fi
|
|
|
|
if [ -n "$health" ]; then
|
|
case "$health" in
|
|
healthy) healthy_count=$((healthy_count + 1)); status_display="${GREEN}${status} (${health})${RESET}" ;;
|
|
unhealthy) unhealthy_count=$((unhealthy_count + 1)); status_display="${RED}${status} (${health})${RESET}" ;;
|
|
*) status_display="${YELLOW}${status} (${health})${RESET}" ;;
|
|
esac
|
|
else
|
|
status_display="${YELLOW}${status}${RESET}"
|
|
fi
|
|
|
|
uptime="-"
|
|
if start_ts=$(date -d "$started_at" +%s 2>/dev/null); then
|
|
now_ts=$(date +%s)
|
|
diff=$((now_ts - start_ts))
|
|
days=$((diff / 86400))
|
|
hours=$(((diff % 86400) / 3600))
|
|
mins=$(((diff % 3600) / 60))
|
|
if [ "$days" -gt 0 ]; then
|
|
uptime="${days}d ${hours}h"
|
|
elif [ "$hours" -gt 0 ]; then
|
|
uptime="${hours}h ${mins}m"
|
|
else
|
|
uptime="${mins}m"
|
|
fi
|
|
fi
|
|
|
|
printf "%-28s %-35s %-22b %-10s %-35s %-20s %s\n" \
|
|
"$name" "$image" "$status_display" "$uptime" "$ip_data" "$port_data" "$cid"
|
|
done
|
|
|
|
echo
|
|
printf '%*s\n' 120 '' | tr ' ' '-'
|
|
|
|
echo "Containers : $total_count"
|
|
echo "Healthy : $healthy_count"
|
|
echo "Unhealthy : $unhealthy_count"
|
|
echo "Running : $running_count"
|
|
|
|
if [ "$unhealthy_count" -gt 0 ]; then
|
|
echo
|
|
echo "${RED}Warning: unhealthy containers detected${RESET}"
|
|
fi
|