154 lines
3.8 KiB
Bash
Executable File
154 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
BIN_ROOT="/opt/selective-vpn/bin"
|
|
COMPONENTS_RAW=""
|
|
DRY_RUN=0
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
rollback.sh [--bin-root DIR] [--component NAME[,NAME...]] [--dry-run]
|
|
|
|
Description:
|
|
Rolls back transport companion binaries by one history step.
|
|
Uses history files created by update.sh in BIN_ROOT/.packaging/*.history.
|
|
|
|
Examples:
|
|
./scripts/transport-packaging/rollback.sh --component singbox
|
|
./scripts/transport-packaging/rollback.sh --bin-root /tmp/svpn-bin --dry-run
|
|
EOF
|
|
}
|
|
|
|
require_cmd() {
|
|
local cmd="$1"
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "[transport-rollback] missing required command: ${cmd}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
collect_components() {
|
|
local state_dir="$1"
|
|
local out=()
|
|
if [[ -n "$COMPONENTS_RAW" ]]; then
|
|
IFS=',' read -r -a out <<< "$COMPONENTS_RAW"
|
|
else
|
|
if [[ -d "$state_dir" ]]; then
|
|
while IFS= read -r file; do
|
|
local base
|
|
base="$(basename "$file")"
|
|
out+=("${base%.history}")
|
|
done < <(find "$state_dir" -maxdepth 1 -type f -name '*.history' | sort)
|
|
fi
|
|
fi
|
|
printf '%s\n' "${out[@]}"
|
|
}
|
|
|
|
rollback_component() {
|
|
local component="$1"
|
|
local state_dir="$2"
|
|
local history_file="${state_dir}/${component}.history"
|
|
|
|
if [[ ! -f "$history_file" ]]; then
|
|
echo "[transport-rollback] ${component}: history file not found (${history_file})" >&2
|
|
return 1
|
|
fi
|
|
|
|
mapfile -t lines < "$history_file"
|
|
if [[ "${#lines[@]}" -lt 2 ]]; then
|
|
echo "[transport-rollback] ${component}: not enough history entries to rollback" >&2
|
|
return 1
|
|
fi
|
|
|
|
local current_line prev_line
|
|
current_line="${lines[${#lines[@]}-1]}"
|
|
prev_line="${lines[${#lines[@]}-2]}"
|
|
|
|
IFS='|' read -r _ts_curr bin_curr version_curr target_curr <<< "$current_line"
|
|
IFS='|' read -r _ts_prev bin_prev version_prev target_prev <<< "$prev_line"
|
|
local binary_name="${bin_curr:-$bin_prev}"
|
|
local active_link="${BIN_ROOT}/${binary_name}"
|
|
|
|
if [[ -z "$binary_name" || -z "$target_prev" ]]; then
|
|
echo "[transport-rollback] ${component}: invalid history lines" >&2
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -e "$target_prev" ]]; then
|
|
echo "[transport-rollback] ${component}: previous target does not exist: ${target_prev}" >&2
|
|
return 1
|
|
fi
|
|
|
|
echo "[transport-rollback] ${component}: ${binary_name} ${version_curr} -> ${version_prev}"
|
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
echo "[transport-rollback] DRY-RUN ${component}: switch ${active_link} -> ${target_prev}"
|
|
return 0
|
|
fi
|
|
|
|
ln -sfn "$target_prev" "$active_link"
|
|
|
|
if [[ "${#lines[@]}" -eq 2 ]]; then
|
|
printf '%s\n' "${lines[0]}" > "$history_file"
|
|
else
|
|
printf '%s\n' "${lines[@]:0:${#lines[@]}-1}" > "$history_file"
|
|
fi
|
|
echo "[transport-rollback] ${component}: active -> ${target_prev}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--bin-root)
|
|
BIN_ROOT="${2:-}"
|
|
shift 2
|
|
;;
|
|
--component)
|
|
COMPONENTS_RAW="${2:-}"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "[transport-rollback] unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
require_cmd ln
|
|
require_cmd find
|
|
require_cmd basename
|
|
|
|
state_dir="${BIN_ROOT}/.packaging"
|
|
mapfile -t components < <(collect_components "$state_dir")
|
|
|
|
if [[ "${#components[@]}" -eq 0 ]]; then
|
|
echo "[transport-rollback] no components selected/found" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "[transport-rollback] bin_root=${BIN_ROOT}"
|
|
if [[ -n "$COMPONENTS_RAW" ]]; then
|
|
echo "[transport-rollback] components=${COMPONENTS_RAW}"
|
|
fi
|
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
echo "[transport-rollback] mode=dry-run"
|
|
fi
|
|
|
|
for component in "${components[@]}"; do
|
|
component="$(echo "$component" | xargs)"
|
|
if [[ -z "$component" ]]; then
|
|
continue
|
|
fi
|
|
rollback_component "$component" "$state_dir"
|
|
done
|
|
|
|
echo "[transport-rollback] done"
|