#!/usr/bin/env bash set -euo pipefail if [[ "$#" -lt 1 || "$#" -gt 2 ]]; then echo "usage: $0 <40-char-git-sha> [web-root]" >&2 exit 2 fi release_sha="$1" web_root="${2:-/srv/www/quant-os}" if [[ ! "${release_sha}" =~ ^[0-9a-f]{40}$ ]]; then echo "release SHA must be a 40-character lowercase Git SHA" >&2 exit 2 fi if [[ "${web_root}" != /* || "${web_root}" == "/" ]]; then echo "web root must be an explicit absolute non-root path" >&2 exit 2 fi if [[ ! -d "${web_root}" ]]; then echo "web root does not exist: ${web_root}" >&2 exit 1 fi lock_dir="${web_root}/.status-deploy.lock" lock_acquired=false next_link="" next_link_created=false cleanup() { exit_status="$?" if [[ "${next_link_created}" == "true" && -L "${next_link}" ]]; then rm -f "${next_link}" fi if [[ "${lock_acquired}" == "true" ]]; then rmdir "${lock_dir}" 2>/dev/null || true fi exit "${exit_status}" } trap cleanup EXIT HUP INT TERM if [[ "${QUANT_OS_STATUS_DEPLOY_LOCK_HELD:-0}" == "1" ]]; then if [[ ! -d "${lock_dir}" || -L "${lock_dir}" ]]; then echo "caller asserted a deployment lock that is not held" >&2 exit 1 fi else if ! mkdir "${lock_dir}" 2>/dev/null; then echo "another status deployment or activation holds ${lock_dir}" >&2 exit 1 fi lock_acquired=true fi release_dir="${web_root}/releases/${release_sha}" if [[ ! -d "${release_dir}" || -L "${release_dir}" ]]; then echo "release must be a regular directory: ${release_dir}" >&2 exit 1 fi release_entries=() while IFS= read -r -d '' entry; do release_entries+=("${entry}") done < <(find "${release_dir}" -mindepth 1 -maxdepth 1 -print0 2>/dev/null) if [[ "${#release_entries[@]}" != "2" ]]; then echo "release must contain exactly index.html and status-data.js" >&2 exit 1 fi for required in index.html status-data.js; do if [[ ! -f "${release_dir}/${required}" || -L "${release_dir}/${required}" ]]; then echo "release must contain a regular non-symlink ${required}" >&2 exit 1 fi done for entry in "${release_entries[@]}"; do case "$(basename "${entry}")" in index.html|status-data.js) ;; *) echo "unexpected release entry: $(basename "${entry}")" >&2 exit 1 ;; esac done next_link="${web_root}/.status-next-${release_sha}" if [[ -e "${next_link}" || -L "${next_link}" ]]; then echo "temporary activation link already exists: ${next_link}" >&2 exit 1 fi ln -s "releases/${release_sha}" "${next_link}" next_link_created=true if [[ -e "${web_root}/status" && ! -L "${web_root}/status" ]]; then echo "active status path exists and is not a symlink" >&2 exit 1 fi if mv --version >/dev/null 2>&1; then mv -Tf "${next_link}" "${web_root}/status" else mv -fh "${next_link}" "${web_root}/status" fi next_link_created=false active_target="$(readlink "${web_root}/status")" if [[ "${active_target}" != "releases/${release_sha}" ]]; then echo "active release verification failed" >&2 exit 1 fi echo "activated ${release_sha}"