Swap in richer progress UI; fix fzf preview to use docker label filter

docker-update (Python):
- Add compose_binary detection (docker compose v2 plugin or legacy docker-compose)
- is_git_repo() now uses `git -C dir rev-parse` instead of .git dir check
- Rich runner: Panel header showing project count / git / registry split,
  per-project sub-task with step labels (git pull, down, pull, up --build…),
  BarColumn(30) + TimeElapsedColumn, bold summary line
- Plain runner: same header line, passes compose_cmd through
- Summary uses bold green/yellow/red instead of plain colours
- All update functions accept compose_cmd parameter

docker-update-fzf (Bash):
- project_status_by_name(): check running state via
  `docker ps --filter label=com.docker.compose.project=NAME` — works from
  any directory, no workdir required
- Preview script rebuilt: shows container table + per-container logs via
  docker ps/logs --filter label=…, includes stopped containers fallback
- show_action_menu() now uses project_status_by_name() for live dot / actions
- Trap extended to INT/TERM for cleaner SSH exit

https://claude.ai/code/session_01LtPxA1zDET2JQn6NYDDxKn
This commit is contained in:
Claude
2026-04-11 10:51:20 +00:00
parent 97378f0af9
commit 61b472bbe8
2 changed files with 182 additions and 72 deletions
+45 -25
View File
@@ -15,7 +15,7 @@ _DU_TMPDIR=""
_setup_tmpdir() {
_DU_TMPDIR=$(mktemp -d /tmp/du-fzf-XXXXXX)
trap '_cleanup' EXIT
trap '_cleanup' EXIT INT TERM
}
_cleanup() {
@@ -27,6 +27,19 @@ die() {
exit 1
}
# ── project status (by name, no workdir needed) ────────────────────────────
# Returns "running" or "stopped" for a compose project by name.
# Uses docker ps --filter so it works from any directory.
project_status_by_name() {
local name="$1"
local running
running=$(docker ps -q \
--filter "label=com.docker.compose.project=${name}" \
2>/dev/null | wc -l)
[[ "$running" -gt 0 ]] && echo "running" || echo "stopped"
}
# ── build_log_list ────────────────────────────────────────────────────────
# Outputs tab-delimited lines:
# DISPLAY_TEXT \t FULL_LOG_PATH
@@ -138,29 +151,44 @@ PYEOF
}
# ── write_preview_script ──────────────────────────────────────────────────
# Writes a small bash script to $_DU_TMPDIR/preview.sh that fzf can call.
# The script receives the full tab-delimited line as $1 and streams docker logs.
# Writes a bash script to $_DU_TMPDIR/preview.sh that fzf calls on hover.
# Uses docker ps --filter label=... so it works from any directory.
write_preview_script() {
local script="$_DU_TMPDIR/preview.sh"
cat > "$script" << 'PREVIEW_EOF'
#!/bin/bash
line="$1"
name=$(printf '%s' "$line" | cut -f2)
workdir=$(printf '%s' "$line" | cut -f3)
name=$(printf '%s' "$line" | cut -f2)
if [[ "$name" == "HEADER" || -z "$workdir" ]]; then
if [[ "$name" == "HEADER" || -z "$name" ]]; then
printf '\n (hover over a project to see its live container logs)\n'
exit 0
fi
if [[ ! -d "$workdir" ]]; then
printf 'Workdir not found: %s\n' "$workdir"
exit 1
printf '\033[1m── containers: %s ──\033[0m\n' "$name"
docker ps \
--filter "label=com.docker.compose.project=${name}" \
--format ' {{.Names}} {{.Status}}' 2>/dev/null
printf '\n\033[1m── logs (last 40 lines) ──\033[0m\n'
# Grab up to 3 container IDs for this project and tail their logs
containers=$(docker ps -q \
--filter "label=com.docker.compose.project=${name}" \
2>/dev/null | head -3)
if [[ -z "$containers" ]]; then
# Also check stopped containers
containers=$(docker ps -aq \
--filter "label=com.docker.compose.project=${name}" \
2>/dev/null | head -3)
[[ -z "$containers" ]] && { printf ' (no containers found)\n'; exit 0; }
fi
printf '\033[1m── live logs: %s ──\033[0m\n\n' "$name"
cd "$workdir" && docker compose logs --tail=80 --no-color 2>&1 \
|| printf 'No logs available\n'
printf '%s\n' "$containers" | while IFS= read -r cid; do
cname=$(docker inspect --format '{{.Name}}' "$cid" 2>/dev/null | sed 's|^/||')
printf '\n\033[2m--- %s ---\033[0m\n' "$cname"
docker logs --tail=40 --timestamps "$cid" 2>&1
done
PREVIEW_EOF
chmod +x "$script"
echo "$script"
@@ -189,22 +217,14 @@ LOGPREVIEW_EOF
show_action_menu() {
local name="$1" workdir="$2" is_git="$3" is_debug="$4"
# Live status check
local running_count stopped_count
running_count=$(cd "$workdir" 2>/dev/null \
&& docker compose ps -q --status running 2>/dev/null | wc -l \
|| echo 0)
stopped_count=$(cd "$workdir" 2>/dev/null \
&& docker compose ps -q 2>/dev/null | wc -l \
|| echo 0)
local dot="○"
[[ "$running_count" -gt 0 ]] && dot="●"
local status dot
status=$(project_status_by_name "$name")
[[ "$status" == "running" ]] && dot="●" || dot="○"
local actions=""
[[ "$running_count" -eq 0 ]] && actions+=" ▶ Start\n"
[[ "$status" != "running" ]] && actions+=" ▶ Start\n"
actions+=" ↺ Update\n"
[[ "$running_count" -gt 0 ]] && actions+=" ■ Stop\n"
[[ "$status" == "running" ]] && actions+=" ■ Stop\n"
local debug_note=""
[[ "$is_debug" == "true" ]] && debug_note=" \033[33m[DEBUG — no real changes]\033[0m"