166 lines
5.7 KiB
Bash
166 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
SCRIPT="$ROOT_DIR/scripts/release-backend.sh"
|
|
DEV_SETUP="$ROOT_DIR/docs/DEV_SETUP.md"
|
|
|
|
bash -n "$SCRIPT"
|
|
|
|
for remote_block in \
|
|
REMOTE_INSPECT \
|
|
REMOTE_BACKUP \
|
|
REMOTE_ACTIVATE \
|
|
REMOTE_RESTORE \
|
|
REMOTE_ROLLBACK_INSPECT \
|
|
REMOTE_ROLLBACK_SAFETY; do
|
|
awk -v marker="$remote_block" '
|
|
index($0, "<<" SQ marker SQ) { capture = 1; next }
|
|
capture && $0 == marker { exit }
|
|
capture { print }
|
|
' SQ="'" "$SCRIPT" | bash -n
|
|
done
|
|
|
|
extract_remote_block() {
|
|
local marker="$1"
|
|
awk -v marker="$marker" '
|
|
index($0, "<<" SQ marker SQ) { capture = 1; next }
|
|
capture && $0 == marker { exit }
|
|
capture { print }
|
|
' SQ="'" "$SCRIPT"
|
|
}
|
|
|
|
run_remote_block_locally() {
|
|
local marker="$1"
|
|
shift
|
|
{
|
|
cat <<'FAKE_SYSTEMD'
|
|
systemctl() {
|
|
case "$1" in
|
|
restart) return 0 ;;
|
|
is-active) printf 'active\n'; return 0 ;;
|
|
status) return 0 ;;
|
|
*) return 0 ;;
|
|
esac
|
|
}
|
|
FAKE_SYSTEMD
|
|
extract_remote_block "$marker"
|
|
} | bash -s -- "$@"
|
|
}
|
|
|
|
test_tmp_base="${TMPDIR:-/tmp}"
|
|
test_tmp="$(mktemp -d "$test_tmp_base/release-backend-test.XXXXXX")"
|
|
test_tmp="$(cd "$test_tmp" && pwd -P)"
|
|
case "$test_tmp" in
|
|
"$test_tmp_base"/release-backend-test.*) ;;
|
|
*)
|
|
echo "FAIL: unsafe test temp directory: $test_tmp" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
trap 'rm -rf -- "$test_tmp"' EXIT
|
|
|
|
mkdir -p "$test_tmp/app" "$test_tmp/backups"
|
|
target="$test_tmp/app/ruoyi-admin.jar"
|
|
printf 'old-backend\n' > "$target"
|
|
old_sha="$(sha256sum "$target" | awk '{print $1}')"
|
|
candidate="$test_tmp/app/candidate.jar"
|
|
printf 'candidate-backend\n' > "$candidate"
|
|
candidate_sha="$(sha256sum "$candidate" | awk '{print $1}')"
|
|
|
|
backup_output="$(
|
|
run_remote_block_locally REMOTE_BACKUP \
|
|
"$target" "$test_tmp/backups" "wygj-aihr.service" \
|
|
"$old_sha" "$candidate_sha" \
|
|
"0000000000000000000000000000000000000000"
|
|
)"
|
|
backup_dir="$(sed -n 's/^backup_dir=//p' <<<"$backup_output")"
|
|
[[ "$backup_dir" == "$test_tmp"/backups/backend-* ]]
|
|
[[ "$(sha256sum "$backup_dir/ruoyi-admin.jar" | awk '{print $1}')" == "$old_sha" ]]
|
|
[[ -f "$backup_dir/release-manifest.txt" ]]
|
|
|
|
run_remote_block_locally REMOTE_ACTIVATE \
|
|
"$target" "$candidate" "wygj-aihr.service" "$old_sha" "$candidate_sha"
|
|
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$candidate_sha" ]]
|
|
[[ ! -e "$candidate" ]]
|
|
|
|
run_remote_block_locally REMOTE_RESTORE \
|
|
"$backup_dir/ruoyi-admin.jar" "$target" "wygj-aihr.service" \
|
|
"$old_sha" "$candidate_sha"
|
|
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$old_sha" ]]
|
|
|
|
printf 'concurrent-backend\n' > "$target"
|
|
concurrent_sha="$(sha256sum "$target" | awk '{print $1}')"
|
|
if run_remote_block_locally REMOTE_RESTORE \
|
|
"$backup_dir/ruoyi-admin.jar" "$target" "wygj-aihr.service" \
|
|
"$old_sha" "$candidate_sha"; then
|
|
echo 'FAIL: restore accepted a concurrently changed target' >&2
|
|
exit 1
|
|
fi
|
|
[[ "$(sha256sum "$target" | awk '{print $1}')" == "$concurrent_sha" ]]
|
|
|
|
safety_output="$(
|
|
run_remote_block_locally REMOTE_ROLLBACK_SAFETY \
|
|
"$target" "$test_tmp/backups" "wygj-aihr.service" \
|
|
"$concurrent_sha" "$old_sha"
|
|
)"
|
|
safety_dir="$(sed -n 's/^safety_dir=//p' <<<"$safety_output")"
|
|
[[ "$safety_dir" == "$test_tmp"/backups/backend-rollback-safety-* ]]
|
|
[[ "$(sha256sum "$safety_dir/ruoyi-admin.jar" | awk '{print $1}')" == "$concurrent_sha" ]]
|
|
[[ -f "$safety_dir/rollback-manifest.txt" ]]
|
|
|
|
grep -Fq 'REMOTE_SSH="YCWY"' "$SCRIPT"
|
|
grep -Fq 'REMOTE_PATH="/opt/wygj/app/ruoyi-admin.jar"' "$SCRIPT"
|
|
grep -Fq 'REMOTE_SERVICE="wygj-aihr.service"' "$SCRIPT"
|
|
grep -Fq 'mode="${1:-plan}"' "$SCRIPT"
|
|
grep -Fq 'plan and rollback-plan are read-only' "$SCRIPT"
|
|
grep -Fq 'require_approval "DEPLOY_BACKEND:$expected_sha256"' "$SCRIPT"
|
|
grep -Fq 'require_approval "ROLLBACK_BACKEND:$expected_sha256"' "$SCRIPT"
|
|
grep -Fq 'deploy and rollback require a clean Git worktree' "$SCRIPT"
|
|
grep -Fq 'run_post_deploy_preflight' "$SCRIPT"
|
|
grep -Fq 'post-deploy preflight failed; restoring' "$SCRIPT"
|
|
grep -Fq 'rollback health failed; restoring safety copy' "$SCRIPT"
|
|
grep -Fq 'expected_target_sha="$5"' "$SCRIPT"
|
|
grep -Fq 'concurrent target change' "$SCRIPT"
|
|
grep -Fq 'remote backend release requires the fixed root deployment account' "$SCRIPT"
|
|
grep -Fq 'cp -a "$target" "$backup_dir/ruoyi-admin.jar"' "$SCRIPT"
|
|
grep -Fq 'mv -T "$staging" "$target"' "$SCRIPT"
|
|
grep -Fq 'RELEASE_VERIFY_REMOTE_BACKEND=true' "$SCRIPT"
|
|
grep -Fq 'RELEASE_VERIFY_REMOTE_SCHEMA=true' "$SCRIPT"
|
|
|
|
if grep -Eq '(^|[[:space:]])rm([[:space:]]|$)' "$SCRIPT"; then
|
|
echo 'FAIL: backend release script must not delete release or backup files' >&2
|
|
exit 1
|
|
fi
|
|
|
|
deploy_without_approval="$(
|
|
bash "$SCRIPT" deploy \
|
|
--artifact missing.jar \
|
|
--artifact-commit HEAD \
|
|
--expected-sha256 0000000000000000000000000000000000000000000000000000000000000000 \
|
|
2>&1 || true
|
|
)"
|
|
grep -Fq 'explicit approval required' <<<"$deploy_without_approval"
|
|
if grep -Fq 'release JAR not found' <<<"$deploy_without_approval"; then
|
|
echo 'FAIL: deploy touched artifact or remote planning before approval' >&2
|
|
exit 1
|
|
fi
|
|
|
|
rollback_without_approval="$(
|
|
bash "$SCRIPT" rollback \
|
|
--backup-dir /opt/wygj/backups/backend-20260729000000-000000000000 \
|
|
--expected-sha256 0000000000000000000000000000000000000000000000000000000000000000 \
|
|
2>&1 || true
|
|
)"
|
|
grep -Fq 'explicit approval required' <<<"$rollback_without_approval"
|
|
if grep -Fq 'read-only rollback inspection failed' <<<"$rollback_without_approval"; then
|
|
echo 'FAIL: rollback contacted the remote before approval' >&2
|
|
exit 1
|
|
fi
|
|
|
|
grep -Fq './scripts/release-backend.sh plan' "$DEV_SETUP"
|
|
grep -Fq './scripts/release-backend.sh deploy' "$DEV_SETUP"
|
|
grep -Fq './scripts/release-backend.sh rollback-plan' "$DEV_SETUP"
|
|
|
|
echo 'PASS: backend release and rollback require explicit approval and preserve recoverable backups'
|