D - resources/scripts/sync-copilot-state.sh: rsync-based push/pull for publish-log.tsv and project-ports-registry.json; dry-run by default; reads remote config from env or ~/.copilot-resources-state/sync.conf; excludes quarantine dir and machine-local log files E - install/verify.sh: add checks for session end hook script and all three history maintenance scripts (cleanup, restore, purge)
126 lines
3.6 KiB
Bash
Executable File
126 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Sync persistent Copilot state files between machines using rsync over SSH.
|
|
# Only publish-log.tsv and project-ports-registry.json are synced; machine-
|
|
# local files (hook-events.log, session-events.tsv) and the quarantine
|
|
# directory are always excluded.
|
|
#
|
|
# Usage:
|
|
# sync-copilot-state.sh --mode push|pull --remote user@host [options]
|
|
#
|
|
# Configuration:
|
|
# Set COPILOT_STATE_SYNC_REMOTE in environment or in
|
|
# ~/.copilot-resources-state/sync.conf to avoid passing --remote every time.
|
|
|
|
set -euo pipefail
|
|
|
|
state_dir="${COPILOT_RESOURCES_STATE_DIR:-$HOME/.copilot-resources-state}"
|
|
sync_conf="$state_dir/sync.conf"
|
|
|
|
dry_run="true"
|
|
mode=""
|
|
remote=""
|
|
ssh_port="22"
|
|
|
|
function usage() {
|
|
cat >&2 <<EOF
|
|
Usage: sync-copilot-state.sh --mode <push|pull> [options]
|
|
|
|
Required:
|
|
--mode <push|pull> push: send local state to remote.
|
|
pull: fetch remote state to local.
|
|
|
|
Optional:
|
|
--remote <user@host> Remote SSH target. Overrides COPILOT_STATE_SYNC_REMOTE
|
|
and sync.conf.
|
|
--remote-path <path> Remote state directory. Default: ~/.copilot-resources-state
|
|
--ssh-port <port> SSH port. Default: 22
|
|
--execute Apply the sync. Default is dry-run.
|
|
--help Show this help text.
|
|
|
|
Configuration file:
|
|
$sync_conf
|
|
Supported keys (shell variable syntax):
|
|
COPILOT_STATE_SYNC_REMOTE=user@host
|
|
COPILOT_STATE_SYNC_REMOTE_PATH=/custom/path
|
|
COPILOT_STATE_SYNC_SSH_PORT=22
|
|
EOF
|
|
}
|
|
|
|
# Load optional config file before parsing args so args take precedence.
|
|
if [[ -f "$sync_conf" ]]; then
|
|
# shellcheck source=/dev/null
|
|
source "$sync_conf"
|
|
fi
|
|
|
|
remote="${COPILOT_STATE_SYNC_REMOTE:-}"
|
|
remote_path="${COPILOT_STATE_SYNC_REMOTE_PATH:-.copilot-resources-state}"
|
|
ssh_port="${COPILOT_STATE_SYNC_SSH_PORT:-22}"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--mode)
|
|
mode="$2"; shift 2 ;;
|
|
--remote)
|
|
remote="$2"; shift 2 ;;
|
|
--remote-path)
|
|
remote_path="$2"; shift 2 ;;
|
|
--ssh-port)
|
|
ssh_port="$2"; shift 2 ;;
|
|
--execute)
|
|
dry_run="false"; shift ;;
|
|
--help)
|
|
usage; exit 0 ;;
|
|
*)
|
|
printf 'Unknown argument: %s\n' "$1" >&2; usage; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$mode" ]]; then
|
|
printf '--mode push|pull is required.\n' >&2; usage; exit 1
|
|
fi
|
|
|
|
if [[ "$mode" != "push" && "$mode" != "pull" ]]; then
|
|
printf '--mode must be push or pull.\n' >&2; exit 1
|
|
fi
|
|
|
|
if [[ -z "$remote" ]]; then
|
|
printf 'No remote configured. Pass --remote user@host or set COPILOT_STATE_SYNC_REMOTE.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p -- "$state_dir"
|
|
|
|
# Files to sync: only durable shared state; exclude machine-local and ephemeral.
|
|
sync_files=(
|
|
"publish-log.tsv"
|
|
"project-ports-registry.json"
|
|
)
|
|
|
|
rsync_args=(-avz --checksum -e "ssh -p ${ssh_port}")
|
|
|
|
if [[ "$dry_run" == "true" ]]; then
|
|
rsync_args+=(--dry-run)
|
|
printf 'Dry-run mode — no changes will be made. Pass --execute to apply.\n\n'
|
|
fi
|
|
|
|
if [[ "$mode" == "push" ]]; then
|
|
printf 'Pushing state to %s:%s\n\n' "$remote" "$remote_path"
|
|
for file in "${sync_files[@]}"; do
|
|
local_file="$state_dir/$file"
|
|
if [[ ! -f "$local_file" ]]; then
|
|
printf 'SKIP (not found): %s\n' "$local_file"
|
|
continue
|
|
fi
|
|
printf 'SYNC: %s -> %s:%s/%s\n' "$local_file" "$remote" "$remote_path" "$file"
|
|
rsync "${rsync_args[@]}" "$local_file" "${remote}:${remote_path}/${file}"
|
|
done
|
|
else
|
|
printf 'Pulling state from %s:%s\n\n' "$remote" "$remote_path"
|
|
for file in "${sync_files[@]}"; do
|
|
printf 'SYNC: %s:%s/%s -> %s/%s\n' "$remote" "$remote_path" "$file" "$state_dir" "$file"
|
|
rsync "${rsync_args[@]}" "${remote}:${remote_path}/${file}" "$state_dir/${file}"
|
|
done
|
|
fi
|
|
|
|
printf '\nDone.\n'
|