264 lines
7.0 KiB
Bash
Executable File
264 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# cleanup-sessions.sh - Clean up dead or old tmux sessions
|
|
#
|
|
# Removes dead sessions from the registry or optionally cleans up
|
|
# all sessions or sessions older than a specified threshold.
|
|
#
|
|
# Usage:
|
|
# ./cleanup-sessions.sh [options]
|
|
#
|
|
# Options:
|
|
# --dry-run Show what would be cleaned without doing it
|
|
# --all Remove all sessions (even alive ones)
|
|
# --older-than Remove sessions older than duration (e.g., "1h", "2d")
|
|
# -h, --help Show this help message
|
|
#
|
|
# Exit codes:
|
|
# 0 - Success
|
|
# 1 - Invalid arguments
|
|
|
|
set -euo pipefail
|
|
|
|
# Get script directory to source registry library
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=lib/registry.sh
|
|
source "$SCRIPT_DIR/lib/registry.sh"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Configuration
|
|
#------------------------------------------------------------------------------
|
|
|
|
dry_run=false
|
|
clean_all=false
|
|
older_than=""
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Functions
|
|
#------------------------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat << EOF
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Clean up dead or old tmux sessions from the registry.
|
|
|
|
Options:
|
|
--dry-run Show what would be cleaned without actually removing
|
|
--all Remove all sessions (even alive ones)
|
|
--older-than DUR Remove sessions older than duration
|
|
-h, --help Show this help message
|
|
|
|
Duration Format:
|
|
Supported units: s (seconds), m (minutes), h (hours), d (days)
|
|
Examples: 30m, 2h, 1d, 3600s
|
|
|
|
Cleanup Modes:
|
|
Default: Remove only dead/missing/zombie sessions
|
|
--all: Remove all sessions regardless of health
|
|
--older-than: Remove sessions older than specified duration
|
|
|
|
Examples:
|
|
# Show what dead sessions would be removed (dry-run)
|
|
$(basename "$0") --dry-run
|
|
|
|
# Remove all dead sessions
|
|
$(basename "$0")
|
|
|
|
# Remove sessions inactive for more than 1 hour
|
|
$(basename "$0") --older-than 1h
|
|
|
|
# Remove all sessions (even alive ones)
|
|
$(basename "$0") --all
|
|
|
|
# Dry-run: show sessions older than 2 days
|
|
$(basename "$0") --dry-run --older-than 2d
|
|
|
|
Exit codes:
|
|
0 - Success
|
|
1 - Invalid arguments
|
|
EOF
|
|
}
|
|
|
|
# Parse duration string to seconds
|
|
# Args: duration string (e.g., "1h", "30m", "2d")
|
|
# Returns: seconds as integer
|
|
parse_duration() {
|
|
local dur="$1"
|
|
|
|
if [[ "$dur" =~ ^([0-9]+)([smhd])$ ]]; then
|
|
local value="${BASH_REMATCH[1]}"
|
|
local unit="${BASH_REMATCH[2]}"
|
|
|
|
case "$unit" in
|
|
s) echo "$value" ;;
|
|
m) echo "$((value * 60))" ;;
|
|
h) echo "$((value * 3600))" ;;
|
|
d) echo "$((value * 86400))" ;;
|
|
esac
|
|
else
|
|
echo "Error: Invalid duration format: $dur" >&2
|
|
echo "Use format like: 30m, 2h, 1d" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Check if session is older than threshold
|
|
# Args: created_at timestamp, threshold in seconds
|
|
# Returns: 0 if older, 1 if newer
|
|
is_older_than() {
|
|
local created="$1"
|
|
local threshold_secs="$2"
|
|
|
|
# Convert ISO8601 to epoch (cross-platform)
|
|
local created_epoch
|
|
created_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" "+%s" 2>/dev/null || \
|
|
date -d "$created" "+%s" 2>/dev/null || echo "0")
|
|
|
|
if [[ "$created_epoch" == "0" ]]; then
|
|
# Can't parse date, assume it's old
|
|
return 0
|
|
fi
|
|
|
|
local now_epoch
|
|
now_epoch=$(date "+%s")
|
|
local age=$((now_epoch - created_epoch))
|
|
|
|
[[ $age -gt $threshold_secs ]]
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Argument parsing
|
|
#------------------------------------------------------------------------------
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--dry-run)
|
|
dry_run=true
|
|
shift
|
|
;;
|
|
--all)
|
|
clean_all=true
|
|
shift
|
|
;;
|
|
--older-than)
|
|
older_than="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Error: Unknown option: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Validate arguments
|
|
#------------------------------------------------------------------------------
|
|
|
|
threshold_secs=0
|
|
if [[ -n "$older_than" ]]; then
|
|
threshold_secs=$(parse_duration "$older_than") || exit 1
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Get sessions from registry
|
|
#------------------------------------------------------------------------------
|
|
|
|
registry_data=$(registry_list_sessions)
|
|
session_names=$(echo "$registry_data" | jq -r '.sessions | keys[]' 2>/dev/null || echo "")
|
|
|
|
if [[ -z "$session_names" ]]; then
|
|
echo "No sessions in registry."
|
|
exit 0
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Find sessions to remove
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Path to pane-health tool
|
|
PANE_HEALTH="$SCRIPT_DIR/pane-health.sh"
|
|
|
|
sessions_to_remove=()
|
|
removed_count=0
|
|
|
|
while IFS= read -r name; do
|
|
[[ -z "$name" ]] && continue
|
|
|
|
# Get session data
|
|
socket=$(echo "$registry_data" | jq -r ".sessions[\"$name\"].socket")
|
|
target=$(echo "$registry_data" | jq -r ".sessions[\"$name\"].target")
|
|
created=$(echo "$registry_data" | jq -r ".sessions[\"$name\"].created_at")
|
|
|
|
# Determine if session should be removed
|
|
should_remove=false
|
|
reason=""
|
|
|
|
if [[ "$clean_all" == true ]]; then
|
|
should_remove=true
|
|
reason="all sessions mode"
|
|
else
|
|
# Check health status
|
|
if [[ -x "$PANE_HEALTH" ]]; then
|
|
if ! "$PANE_HEALTH" -S "$socket" -t "$target" --format text >/dev/null 2>&1; then
|
|
should_remove=true
|
|
reason="dead/missing/zombie"
|
|
fi
|
|
fi
|
|
|
|
# Check age if threshold specified
|
|
if [[ -n "$older_than" ]] && [[ "$threshold_secs" -gt 0 ]]; then
|
|
if is_older_than "$created" "$threshold_secs"; then
|
|
should_remove=true
|
|
if [[ -n "$reason" ]]; then
|
|
reason="$reason + older than $older_than"
|
|
else
|
|
reason="older than $older_than"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Add to removal list if needed
|
|
if [[ "$should_remove" == true ]]; then
|
|
sessions_to_remove+=("$name|$reason")
|
|
fi
|
|
done <<< "$session_names"
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Remove sessions
|
|
#------------------------------------------------------------------------------
|
|
|
|
if [[ ${#sessions_to_remove[@]} -eq 0 ]]; then
|
|
echo "No sessions to clean up."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$dry_run" == true ]]; then
|
|
echo "Dry-run mode: Would remove ${#sessions_to_remove[@]} session(s):"
|
|
for session_info in "${sessions_to_remove[@]}"; do
|
|
IFS='|' read -r name reason <<< "$session_info"
|
|
echo " - $name ($reason)"
|
|
done
|
|
else
|
|
echo "Removing ${#sessions_to_remove[@]} session(s):"
|
|
for session_info in "${sessions_to_remove[@]}"; do
|
|
IFS='|' read -r name reason <<< "$session_info"
|
|
echo " - $name ($reason)"
|
|
if registry_remove_session "$name"; then
|
|
removed_count=$((removed_count + 1))
|
|
else
|
|
echo " Warning: Failed to remove $name" >&2
|
|
fi
|
|
done
|
|
echo "Removed $removed_count session(s) successfully."
|
|
fi
|
|
|
|
exit 0
|