515 lines
13 KiB
Bash
515 lines
13 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
# Fast code-only update for BMC Hub (DEV/PROD)
|
||
|
|
#
|
||
|
|
# Purpose:
|
||
|
|
# - Deploy small app code/template/static changes without creating a new tag/package.
|
||
|
|
# - Keep full release workflow in updateto.sh for dependency/runtime/migration changes.
|
||
|
|
#
|
||
|
|
# Safety model:
|
||
|
|
# - Allowed paths: app/* and main.py
|
||
|
|
# - Disallowed: migrations, Docker/compose, requirements, env files, deploy scripts.
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
PODMAN_COMPOSE_FILE="docker-compose.prod.yml"
|
||
|
|
STATE_DIR=".fast-update"
|
||
|
|
STATE_FILE="${STATE_DIR}/current_ref"
|
||
|
|
LAST_BACKUP_FILE="${STATE_DIR}/last_backup"
|
||
|
|
|
||
|
|
TARGET_REF=""
|
||
|
|
DRY_RUN=false
|
||
|
|
ALLOW_PROD=false
|
||
|
|
ROLLBACK_ID=""
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
cat <<'EOF'
|
||
|
|
Usage:
|
||
|
|
./update_fast.sh --ref <git-ref> [--dry-run] [--allow-prod]
|
||
|
|
./update_fast.sh --rollback <backup-id> [--allow-prod]
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
./update_fast.sh --ref main --dry-run
|
||
|
|
./update_fast.sh --ref 08f4097
|
||
|
|
./update_fast.sh --rollback 20260517-142155
|
||
|
|
|
||
|
|
Notes:
|
||
|
|
- Fast mode is ONLY for code/template/static updates in app/* and main.py.
|
||
|
|
- For migrations, dependencies, Docker/compose, or env changes: use ./updateto.sh.
|
||
|
|
- On production hosts, --allow-prod is required.
|
||
|
|
EOF
|
||
|
|
}
|
||
|
|
|
||
|
|
if [ "${EUID:-$(id -u)}" -eq 0 ]; then
|
||
|
|
echo "Error: do not run as root. Use the normal rootless podman user."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
while [ "$#" -gt 0 ]; do
|
||
|
|
case "$1" in
|
||
|
|
--ref)
|
||
|
|
TARGET_REF="${2:-}"
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
--dry-run)
|
||
|
|
DRY_RUN=true
|
||
|
|
shift
|
||
|
|
;;
|
||
|
|
--allow-prod)
|
||
|
|
ALLOW_PROD=true
|
||
|
|
shift
|
||
|
|
;;
|
||
|
|
--rollback)
|
||
|
|
ROLLBACK_ID="${2:-}"
|
||
|
|
shift 2
|
||
|
|
;;
|
||
|
|
-h|--help)
|
||
|
|
usage
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
echo "Error: unknown option: $1"
|
||
|
|
usage
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
if [ -n "$ROLLBACK_ID" ] && [ -n "$TARGET_REF" ]; then
|
||
|
|
echo "Error: use either --ref or --rollback, not both."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "$ROLLBACK_ID" ] && [ -z "$TARGET_REF" ]; then
|
||
|
|
echo "Error: missing required option --ref <git-ref> or --rollback <backup-id>."
|
||
|
|
usage
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ ! -f ".env" ]; then
|
||
|
|
echo "Error: .env not found in $(pwd)"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ ! -f "$PODMAN_COMPOSE_FILE" ]; then
|
||
|
|
echo "Error: $PODMAN_COMPOSE_FILE not found in $(pwd)"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
load_env_file() {
|
||
|
|
local env_file="$1"
|
||
|
|
local line=""
|
||
|
|
local trimmed=""
|
||
|
|
local key=""
|
||
|
|
local value=""
|
||
|
|
local first_char=""
|
||
|
|
local last_char=""
|
||
|
|
|
||
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
||
|
|
line="${line%$'\r'}"
|
||
|
|
trimmed="${line#"${line%%[![:space:]]*}"}"
|
||
|
|
|
||
|
|
if [ -z "$trimmed" ] || [[ "$trimmed" == \#* ]]; then
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ "$line" != *=* ]]; then
|
||
|
|
echo "Error: invalid line in .env: $line"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
key="${line%%=*}"
|
||
|
|
value="${line#*=}"
|
||
|
|
|
||
|
|
key="${key#"${key%%[![:space:]]*}"}"
|
||
|
|
key="${key%"${key##*[![:space:]]}"}"
|
||
|
|
|
||
|
|
if [[ ! "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||
|
|
echo "Error: invalid .env key: $key"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "${#value}" -ge 2 ]; then
|
||
|
|
first_char="${value:0:1}"
|
||
|
|
last_char="${value: -1}"
|
||
|
|
if { [ "$first_char" = '"' ] && [ "$last_char" = '"' ]; } || { [ "$first_char" = "'" ] && [ "$last_char" = "'" ]; }; then
|
||
|
|
value="${value:1:${#value}-2}"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
export "$key=$value"
|
||
|
|
done < "$env_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
load_env_file .env
|
||
|
|
|
||
|
|
get_current_ip() {
|
||
|
|
local ip=""
|
||
|
|
|
||
|
|
if ip=$(hostname -I 2>/dev/null | awk '{print $1}'); then
|
||
|
|
if [ -n "$ip" ]; then
|
||
|
|
echo "$ip"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
if command -v ipconfig >/dev/null 2>&1; then
|
||
|
|
ip=$(ipconfig getifaddr en0 2>/dev/null || true)
|
||
|
|
if [ -z "$ip" ]; then
|
||
|
|
ip=$(ipconfig getifaddr en1 2>/dev/null || true)
|
||
|
|
fi
|
||
|
|
if [ -n "$ip" ]; then
|
||
|
|
echo "$ip"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "unknown"
|
||
|
|
}
|
||
|
|
|
||
|
|
CURRENT_IP="$(get_current_ip)"
|
||
|
|
CURRENT_DIR=$(pwd)
|
||
|
|
IS_PROD=false
|
||
|
|
if [[ "$CURRENT_IP" == "172.16.31.183" ]] || [[ "$CURRENT_DIR" == "/srv/podman/bmc_hub_v2" ]] || [[ "$CURRENT_DIR" == "/srv/podman/bmc_hub_v1.0" ]]; then
|
||
|
|
IS_PROD=true
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "$IS_PROD" = true ] && [ "$ALLOW_PROD" != true ]; then
|
||
|
|
echo "Error: production host detected. Re-run with --allow-prod to continue."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "$IS_PROD" = true ] && [ "$DRY_RUN" != true ]; then
|
||
|
|
echo "Production safety check:"
|
||
|
|
echo " host ip: $CURRENT_IP"
|
||
|
|
echo " cwd: $CURRENT_DIR"
|
||
|
|
read -r -p "Type FAST-UPDATE to continue: " CONFIRM
|
||
|
|
if [ "$CONFIRM" != "FAST-UPDATE" ]; then
|
||
|
|
echo "Aborted."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
mkdir -p "$STATE_DIR/backups"
|
||
|
|
|
||
|
|
GITEA_BASE="${GITEA_URL:-https://g.bmcnetworks.dk}"
|
||
|
|
REPO="${GITHUB_REPO:-ct/bmc_hub}"
|
||
|
|
API_PORT="${API_PORT:-8000}"
|
||
|
|
|
||
|
|
url_encode() {
|
||
|
|
python3 - "$1" <<'PY'
|
||
|
|
import sys
|
||
|
|
from urllib.parse import quote
|
||
|
|
print(quote(sys.argv[1], safe=""))
|
||
|
|
PY
|
||
|
|
}
|
||
|
|
|
||
|
|
fetch_url_to_file() {
|
||
|
|
local url="$1"
|
||
|
|
local out_file="$2"
|
||
|
|
|
||
|
|
if [ -n "${GITHUB_TOKEN:-}" ]; then
|
||
|
|
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" "$url" -o "$out_file"
|
||
|
|
else
|
||
|
|
curl -fsSL "$url" -o "$out_file"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
build_changed_from_compare_json() {
|
||
|
|
local compare_file="$1"
|
||
|
|
local out_file="$2"
|
||
|
|
|
||
|
|
python3 - "$compare_file" > "$out_file" <<'PY'
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
|
||
|
|
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
files = data.get("files") or []
|
||
|
|
for item in files:
|
||
|
|
status = str(item.get("status") or "")
|
||
|
|
filename = str(item.get("filename") or "")
|
||
|
|
prev = str(item.get("previous_filename") or "")
|
||
|
|
print(f"{status}\t{filename}\t{prev}")
|
||
|
|
PY
|
||
|
|
}
|
||
|
|
|
||
|
|
build_changed_from_local_git() {
|
||
|
|
local base_ref="$1"
|
||
|
|
local target_ref="$2"
|
||
|
|
local out_file="$3"
|
||
|
|
|
||
|
|
if ! git rev-parse --verify "$base_ref" >/dev/null 2>&1; then
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
if ! git rev-parse --verify "$target_ref" >/dev/null 2>&1; then
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
git diff --name-status "$base_ref".."$target_ref" \
|
||
|
|
| awk 'BEGIN{OFS="\t"}
|
||
|
|
/^A\t/ {print "added", $2, ""}
|
||
|
|
/^M\t/ {print "modified", $2, ""}
|
||
|
|
/^D\t/ {print "deleted", $2, ""}
|
||
|
|
/^R[0-9]*\t/ {print "renamed", $3, $2}
|
||
|
|
/^C[0-9]*\t/ {print "copied", $3, $2}
|
||
|
|
/^T\t/ {print "type_changed", $2, ""}
|
||
|
|
/^U\t/ {print "unmerged", $2, ""}
|
||
|
|
/^X\t/ {print "unknown", $2, ""}
|
||
|
|
/^B\t/ {print "broken", $2, ""}' > "$out_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
is_disallowed_path() {
|
||
|
|
local path="$1"
|
||
|
|
case "$path" in
|
||
|
|
migrations/*|Dockerfile|requirements.txt|docker-compose.yml|docker-compose.prod.yml|.env|.env.*|updateto.sh|update_fast.sh|scripts/*)
|
||
|
|
return 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
return 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
is_allowed_path() {
|
||
|
|
local path="$1"
|
||
|
|
case "$path" in
|
||
|
|
app/*|main.py)
|
||
|
|
return 0
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
return 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
health_check() {
|
||
|
|
local attempts=30
|
||
|
|
local i=1
|
||
|
|
while [ "$i" -le "$attempts" ]; do
|
||
|
|
if curl -fsS "http://localhost:${API_PORT}/health" >/dev/null 2>&1; then
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
echo "Waiting for API health (${i}/${attempts})"
|
||
|
|
sleep 2
|
||
|
|
i=$((i + 1))
|
||
|
|
done
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
rebuild_api_latest() {
|
||
|
|
RELEASE_VERSION=latest podman-compose -f "$PODMAN_COMPOSE_FILE" build api
|
||
|
|
RELEASE_VERSION=latest podman-compose -f "$PODMAN_COMPOSE_FILE" up -d --no-deps api
|
||
|
|
}
|
||
|
|
|
||
|
|
restore_backup() {
|
||
|
|
local backup_id="$1"
|
||
|
|
local backup_dir="${STATE_DIR}/backups/${backup_id}"
|
||
|
|
local manifest="${backup_dir}/manifest.tsv"
|
||
|
|
|
||
|
|
if [ ! -d "$backup_dir" ]; then
|
||
|
|
echo "Error: backup id not found: $backup_id"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
if [ ! -f "$manifest" ]; then
|
||
|
|
echo "Error: manifest missing: $manifest"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Restoring backup: ${backup_id}"
|
||
|
|
|
||
|
|
while IFS=$'\t' read -r status path existed; do
|
||
|
|
[ -z "$path" ] && continue
|
||
|
|
mkdir -p "$(dirname "$path")"
|
||
|
|
if [ "$existed" = "1" ]; then
|
||
|
|
cp "${backup_dir}/original/${path}" "$path"
|
||
|
|
else
|
||
|
|
rm -f "$path"
|
||
|
|
fi
|
||
|
|
done < "$manifest"
|
||
|
|
|
||
|
|
rebuild_api_latest
|
||
|
|
if ! health_check; then
|
||
|
|
echo "Error: health check failed after rollback."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -f "${backup_dir}/meta.env" ]; then
|
||
|
|
# shellcheck disable=SC1090
|
||
|
|
source "${backup_dir}/meta.env"
|
||
|
|
if [ -n "${BASE_REF:-}" ]; then
|
||
|
|
echo "$BASE_REF" > "$STATE_FILE"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Rollback completed: ${backup_id}"
|
||
|
|
}
|
||
|
|
|
||
|
|
if [ -n "$ROLLBACK_ID" ]; then
|
||
|
|
restore_backup "$ROLLBACK_ID"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
BASE_REF=""
|
||
|
|
if [ -f "$STATE_FILE" ]; then
|
||
|
|
BASE_REF="$(cat "$STATE_FILE")"
|
||
|
|
elif [ -n "${RELEASE_VERSION:-}" ] && [ "${RELEASE_VERSION}" != "latest" ]; then
|
||
|
|
BASE_REF="$RELEASE_VERSION"
|
||
|
|
elif [ -n "${FAST_BASE_REF:-}" ]; then
|
||
|
|
BASE_REF="$FAST_BASE_REF"
|
||
|
|
else
|
||
|
|
echo "Error: cannot determine base ref."
|
||
|
|
echo "Set RELEASE_VERSION in .env, or create ${STATE_FILE}, or provide FAST_BASE_REF env var."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Fast update"
|
||
|
|
echo " base ref: $BASE_REF"
|
||
|
|
echo " target ref: $TARGET_REF"
|
||
|
|
|
||
|
|
BASE_ENC="$(url_encode "$BASE_REF")"
|
||
|
|
TARGET_ENC="$(url_encode "$TARGET_REF")"
|
||
|
|
COMPARE_URL="${GITEA_BASE}/api/v1/repos/${REPO}/compare/${BASE_ENC}...${TARGET_ENC}"
|
||
|
|
|
||
|
|
TMP_COMPARE="$(mktemp)"
|
||
|
|
TMP_CHANGED="$(mktemp)"
|
||
|
|
trap 'rm -f "$TMP_COMPARE" "$TMP_CHANGED"' EXIT
|
||
|
|
|
||
|
|
COMPARE_SOURCE=""
|
||
|
|
if fetch_url_to_file "$COMPARE_URL" "$TMP_COMPARE"; then
|
||
|
|
build_changed_from_compare_json "$TMP_COMPARE" "$TMP_CHANGED"
|
||
|
|
COMPARE_SOURCE="gitea-api"
|
||
|
|
else
|
||
|
|
if [ -d ".git" ] && build_changed_from_local_git "$BASE_REF" "$TARGET_REF" "$TMP_CHANGED"; then
|
||
|
|
COMPARE_SOURCE="local-git"
|
||
|
|
echo "Warning: compare API unavailable, using local git diff fallback."
|
||
|
|
else
|
||
|
|
echo "Error: could not fetch compare data from: $COMPARE_URL"
|
||
|
|
echo "Hint: ensure GITHUB_TOKEN is set, refs exist, or run in a git clone where both refs are available."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Compare source: ${COMPARE_SOURCE}"
|
||
|
|
|
||
|
|
if [ ! -s "$TMP_CHANGED" ]; then
|
||
|
|
echo "No changed files between ${BASE_REF} and ${TARGET_REF}."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
declare -a DISALLOWED_FILES=()
|
||
|
|
declare -a NOT_ALLOWED_FILES=()
|
||
|
|
declare -a UNSUPPORTED_STATUS=()
|
||
|
|
|
||
|
|
while IFS=$'\t' read -r status file_path prev_path; do
|
||
|
|
[ -z "$file_path" ] && continue
|
||
|
|
|
||
|
|
case "$status" in
|
||
|
|
added|modified)
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
UNSUPPORTED_STATUS+=("${status}: ${file_path}")
|
||
|
|
continue
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
|
||
|
|
if is_disallowed_path "$file_path"; then
|
||
|
|
DISALLOWED_FILES+=("$file_path")
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
if ! is_allowed_path "$file_path"; then
|
||
|
|
NOT_ALLOWED_FILES+=("$file_path")
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
done < "$TMP_CHANGED"
|
||
|
|
|
||
|
|
if [ "${#UNSUPPORTED_STATUS[@]}" -gt 0 ]; then
|
||
|
|
echo "Error: unsupported change types in fast mode:"
|
||
|
|
for item in "${UNSUPPORTED_STATUS[@]}"; do
|
||
|
|
echo " - $item"
|
||
|
|
done
|
||
|
|
echo "Use ./updateto.sh for these changes."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "${#DISALLOWED_FILES[@]}" -gt 0 ]; then
|
||
|
|
echo "Error: disallowed files changed for fast mode:"
|
||
|
|
for file_path in "${DISALLOWED_FILES[@]}"; do
|
||
|
|
echo " - $file_path"
|
||
|
|
done
|
||
|
|
echo "Use ./updateto.sh for this update."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "${#NOT_ALLOWED_FILES[@]}" -gt 0 ]; then
|
||
|
|
echo "Error: files outside fast-mode scope changed:"
|
||
|
|
for file_path in "${NOT_ALLOWED_FILES[@]}"; do
|
||
|
|
echo " - $file_path"
|
||
|
|
done
|
||
|
|
echo "Fast mode only allows app/* and main.py. Use ./updateto.sh instead."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "Changed files in fast scope:"
|
||
|
|
while IFS=$'\t' read -r status file_path prev_path; do
|
||
|
|
[ -z "$file_path" ] && continue
|
||
|
|
echo " - ${status}: ${file_path}"
|
||
|
|
done < "$TMP_CHANGED"
|
||
|
|
|
||
|
|
if [ "$DRY_RUN" = true ]; then
|
||
|
|
echo "Dry-run complete. No files updated."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
BACKUP_ID="$(date +%Y%m%d-%H%M%S)"
|
||
|
|
BACKUP_DIR="${STATE_DIR}/backups/${BACKUP_ID}"
|
||
|
|
MANIFEST="${BACKUP_DIR}/manifest.tsv"
|
||
|
|
mkdir -p "${BACKUP_DIR}/original"
|
||
|
|
|
||
|
|
while IFS=$'\t' read -r status file_path prev_path; do
|
||
|
|
[ -z "$file_path" ] && continue
|
||
|
|
|
||
|
|
mkdir -p "$(dirname "$file_path")"
|
||
|
|
|
||
|
|
existed=0
|
||
|
|
if [ -f "$file_path" ]; then
|
||
|
|
mkdir -p "${BACKUP_DIR}/original/$(dirname "$file_path")"
|
||
|
|
cp "$file_path" "${BACKUP_DIR}/original/${file_path}"
|
||
|
|
existed=1
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf "%s\t%s\t%s\n" "$status" "$file_path" "$existed" >> "$MANIFEST"
|
||
|
|
|
||
|
|
file_enc="$(url_encode "$file_path")"
|
||
|
|
raw_url="${GITEA_BASE}/api/v1/repos/${REPO}/raw/${file_enc}?ref=${TARGET_ENC}"
|
||
|
|
tmp_file="${file_path}.tmp.fast"
|
||
|
|
|
||
|
|
fetch_url_to_file "$raw_url" "$tmp_file"
|
||
|
|
mv "$tmp_file" "$file_path"
|
||
|
|
done < "$TMP_CHANGED"
|
||
|
|
|
||
|
|
cat > "${BACKUP_DIR}/meta.env" <<EOF
|
||
|
|
BASE_REF=${BASE_REF}
|
||
|
|
TARGET_REF=${TARGET_REF}
|
||
|
|
BACKUP_ID=${BACKUP_ID}
|
||
|
|
EOF
|
||
|
|
|
||
|
|
echo "Building and restarting API container only"
|
||
|
|
rebuild_api_latest
|
||
|
|
|
||
|
|
if ! health_check; then
|
||
|
|
echo "Error: health check failed after fast update. Starting automatic rollback."
|
||
|
|
restore_backup "$BACKUP_ID"
|
||
|
|
echo "Rollback completed. Fast update aborted."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "$TARGET_REF" > "$STATE_FILE"
|
||
|
|
echo "$BACKUP_ID" > "$LAST_BACKUP_FILE"
|
||
|
|
|
||
|
|
echo "Fast update complete"
|
||
|
|
echo " backup id: $BACKUP_ID"
|
||
|
|
echo " deployed: $TARGET_REF"
|
||
|
|
echo " state file: $STATE_FILE"
|