#!/usr/bin/env bash set -euo pipefail if [[ "$#" -lt 2 || "$#" -gt 3 ]]; then echo "usage: $0 <40-char-git-sha> [web-root]" >&2 exit 2 fi release_sha="$1" source_dir="$2" web_root="${3:-/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 [[ ! -d "${source_dir}" ]]; then echo "source directory does not exist" >&2 exit 2 fi if [[ -L "${source_dir}" ]]; then echo "source directory must not be a symlink" >&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 source_entries=() while IFS= read -r -d '' entry; do source_entries+=("${entry}") done < <(find "${source_dir}" -mindepth 1 -maxdepth 1 -print0) if [[ "${#source_entries[@]}" != "2" ]]; then echo "source must contain exactly index.html and status-data.js" >&2 exit 1 fi for required in index.html status-data.js; do if [[ ! -f "${source_dir}/${required}" || -L "${source_dir}/${required}" ]]; then echo "source must contain a regular non-symlink ${required}" >&2 exit 1 fi done for entry in "${source_entries[@]}"; do case "$(basename "${entry}")" in index.html|status-data.js) ;; *) echo "unexpected source entry: $(basename "${entry}")" >&2 exit 1 ;; esac if [[ ! -f "${entry}" || -L "${entry}" ]]; then echo "source entries must be regular non-symlink files" >&2 exit 1 fi done release_dir="${web_root}/releases/${release_sha}" incoming_dir="${web_root}/.incoming-${release_sha}" lock_dir="${web_root}/.status-deploy.lock" incoming_created=false lock_acquired=false cleanup() { exit_status="$?" if [[ "${incoming_created}" == "true" ]]; then chmod 0755 "${incoming_dir}" 2>/dev/null || true rm -f \ "${incoming_dir}/index.html" \ "${incoming_dir}/status-data.js" rmdir "${incoming_dir}" 2>/dev/null || true fi if [[ "${lock_acquired}" == "true" ]]; then rmdir "${lock_dir}" 2>/dev/null || true fi exit "${exit_status}" } trap cleanup EXIT HUP INT TERM install -d -m 0755 "${web_root}" if ! mkdir "${lock_dir}" 2>/dev/null; then echo "another status deployment or activation holds ${lock_dir}" >&2 exit 1 fi lock_acquired=true if [[ -L "${web_root}/releases" ]]; then echo "releases directory must not be a symlink" >&2 exit 1 fi install -d -m 0755 "${web_root}/releases" if [[ -L "${web_root}/releases" ]]; then echo "releases directory must not be a symlink" >&2 exit 1 fi if [[ -e "${release_dir}" || -L "${release_dir}" ]]; then echo "refusing to overwrite existing release: ${release_dir}" >&2 exit 1 fi if [[ -e "${incoming_dir}" || -L "${incoming_dir}" ]]; then echo "stale incoming path must be inspected and removed: ${incoming_dir}" >&2 exit 1 fi install -d -m 0755 "${incoming_dir}" incoming_created=true install -m 0644 "${source_dir}/index.html" "${incoming_dir}/index.html" install -m 0644 \ "${source_dir}/status-data.js" \ "${incoming_dir}/status-data.js" hash_command=() if command -v sha256sum >/dev/null 2>&1; then hash_command=(sha256sum) elif command -v shasum >/dev/null 2>&1; then hash_command=(shasum -a 256) else echo "sha256sum or shasum is required" >&2 exit 1 fi for required in index.html status-data.js; do source_hash="$("${hash_command[@]}" "${source_dir}/${required}" | awk '{print $1}')" incoming_hash="$("${hash_command[@]}" "${incoming_dir}/${required}" | awk '{print $1}')" if [[ "${source_hash}" != "${incoming_hash}" ]]; then echo "release hash mismatch: ${required}" >&2 exit 1 fi echo "${required} ${incoming_hash}" done incoming_entries=() while IFS= read -r -d '' entry; do incoming_entries+=("${entry}") done < <(find "${incoming_dir}" -mindepth 1 -maxdepth 1 -print0) if [[ "${#incoming_entries[@]}" != "2" ]]; then echo "incoming release must contain exactly two files" >&2 exit 1 fi for required in index.html status-data.js; do if [[ ! -f "${incoming_dir}/${required}" || -L "${incoming_dir}/${required}" ]]; then echo "incoming release is not a regular immutable payload: ${required}" >&2 exit 1 fi done chmod 0444 \ "${incoming_dir}/index.html" \ "${incoming_dir}/status-data.js" if mv --version >/dev/null 2>&1; then mv -T "${incoming_dir}" "${release_dir}" else if [[ -e "${release_dir}" || -L "${release_dir}" ]]; then echo "refusing to overwrite concurrently created release" >&2 exit 1 fi mv "${incoming_dir}" "${release_dir}" fi incoming_created=false chmod 0555 "${release_dir}" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export QUANT_OS_STATUS_DEPLOY_LOCK_HELD=1 "${script_dir}/activate_static_status.sh" "${release_sha}" "${web_root}"