Mercurial
view hg-web/deploy.sh @ 259:667156fcd3e3
Add dev-only cyberpunk JRPG page
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Wed, 05 Aug 2026 09:19:41 -0700 |
| parents | c5129452493e |
| children |
line wrap: on
line source
#!/usr/bin/env bash set -Eeuo pipefail SERVICE_NAME="${SERVICE_NAME:-hg_web_server.service}" SERVICE_USER="${SERVICE_USER:-hg_web_server}" SERVICE_GROUP="${SERVICE_GROUP:-zenbu_team}" DEPLOY_ROOT="${DEPLOY_ROOT:-/opt/hg_web_server}" ACTIVE_PATH="${ACTIVE_PATH:-${DEPLOY_ROOT}/active}" NEW_PATH="${NEW_PATH:-${DEPLOY_ROOT}/new}" BACKUP_PATH="${BACKUP_PATH:-${DEPLOY_ROOT}/previous}" REPOSITORY_PATH="${REPOSITORY_PATH:-${DEPLOY_ROOT}/repository}" REPOSITORY_NEW_PATH="${REPOSITORY_NEW_PATH:-${DEPLOY_ROOT}/repository-new}" HEALTH_URL="${HEALTH_URL:-http://127.0.0.1:6970/}" HG_ALLOW_PUSH="${HG_ALLOW_PUSH:-false}" MANAGE_NGINX="${MANAGE_NGINX:-auto}" NGINX_SERVER_NAME="${NGINX_SERVER_NAME:-zenbu.babocoder.com}" NGINX_SITE_NAME="${NGINX_SITE_NAME:-hg-web}" NGINX_SITE_AVAILABLE="${NGINX_SITE_AVAILABLE:-/etc/nginx/sites-available/${NGINX_SITE_NAME}}" NGINX_SITE_ENABLED="${NGINX_SITE_ENABLED:-/etc/nginx/sites-enabled/${NGINX_SITE_NAME}}" workspace="${BUILD_WORKSPACE_DIRECTORY:-$(cd "$(dirname "$0")/.." && pwd)}" cd "$workspace" if [[ ! -d "$workspace/.hg" ]]; then echo "Run deployment from a Zenbu Mercurial workspace." >&2 exit 1 fi DEPLOY_ROOT="$(realpath -m "$DEPLOY_ROOT")" ACTIVE_PATH="$(realpath -m "$ACTIVE_PATH")" NEW_PATH="$(realpath -m "$NEW_PATH")" BACKUP_PATH="$(realpath -m "$BACKUP_PATH")" REPOSITORY_PATH="$(realpath -m "$REPOSITORY_PATH")" REPOSITORY_NEW_PATH="$(realpath -m "$REPOSITORY_NEW_PATH")" if [[ ! "$DEPLOY_ROOT" =~ ^/opt/[A-Za-z0-9._/-]+$ ]]; then echo "DEPLOY_ROOT must be a dedicated directory below /opt." >&2 exit 1 fi declare -A deployment_paths=() for path in \ "$ACTIVE_PATH" \ "$NEW_PATH" \ "$BACKUP_PATH" \ "$REPOSITORY_PATH" \ "$REPOSITORY_NEW_PATH"; do if [[ "$path" != "$DEPLOY_ROOT/"* ]]; then echo "Deployment path escapes DEPLOY_ROOT: $path" >&2 exit 1 fi if [[ -n "${deployment_paths[$path]:-}" ]]; then echo "Deployment paths must be distinct: $path" >&2 exit 1 fi deployment_paths["$path"]=1 done if [[ ! "$SERVICE_USER" =~ ^[A-Za-z0-9._-]+$ || ! "$SERVICE_GROUP" =~ ^[A-Za-z0-9._-]+$ || ! "$SERVICE_NAME" =~ ^[A-Za-z0-9._@-]+$ || ! "$HG_ALLOW_PUSH" =~ ^(true|false)$ ]]; then echo "Invalid systemd deployment setting." >&2 exit 1 fi if [[ -n "${BUILD_WORKSPACE_DIRECTORY:-}" ]]; then if [[ -n "${RUNFILES_DIR:-}" ]]; then source "$RUNFILES_DIR/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -n "${RUNFILES_MANIFEST_FILE:-}" ]]; then runfiles_library="$( grep -sm1 '^bazel_tools/tools/bash/runfiles/runfiles.bash ' \ "$RUNFILES_MANIFEST_FILE" | cut -d' ' -f2- )" source "$runfiles_library" elif [[ -d "$0.runfiles" ]]; then RUNFILES_DIR="$0.runfiles" export RUNFILES_DIR source "$RUNFILES_DIR/bazel_tools/tools/bash/runfiles/runfiles.bash" elif [[ -f "$0.runfiles_manifest" ]]; then RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" export RUNFILES_MANIFEST_FILE runfiles_library="$( grep -sm1 '^bazel_tools/tools/bash/runfiles/runfiles.bash ' \ "$RUNFILES_MANIFEST_FILE" | cut -d' ' -f2- )" source "$runfiles_library" else echo "Bazel runfiles are unavailable." >&2 exit 1 fi bundle_dir="$(rlocation _main/hg-web/hg_web_server_bundle)" nginx_template="$(rlocation _main/hg-web/nginx/hg-web.conf.template)" service_template="$(rlocation _main/hg-web/systemd/hg-web.service.template)" else bazel build -c opt //hg-web:hg_web_server_bundle bundle_dir="bazel-bin/hg-web/hg_web_server_bundle" nginx_template="hg-web/nginx/hg-web.conf.template" service_template="hg-web/systemd/hg-web.service.template" fi NGINX_SITE_TEMPLATE="${NGINX_SITE_TEMPLATE:-$nginx_template}" rendered_nginx="$(mktemp)" rendered_service="$(mktemp)" service_backup="$(mktemp)" repository_bundle="$(mktemp)" release_id="$(date -u +%Y%m%dT%H%M%SZ)-$$" promotion_prepared=0 service_changed=0 service_had_previous=0 service_was_enabled=0 nginx_site_created=0 nginx_link_created=0 lock_acquired=0 cleanup() { rm -f \ "$rendered_nginx" \ "$rendered_service" \ "$service_backup" \ "$repository_bundle" } health_check() { for _ in $(seq 1 20); do if curl --fail --silent --max-time 3 "$HEALTH_URL" >/dev/null; then return 0 fi sleep 1 done return 1 } restore_host_configuration() { local restore_failed=0 if [[ "$nginx_link_created" -eq 1 ]]; then sudo rm -f "$NGINX_SITE_ENABLED" || restore_failed=1 fi if [[ "$nginx_site_created" -eq 1 ]]; then sudo rm -f "$NGINX_SITE_AVAILABLE" || restore_failed=1 fi if [[ "$nginx_link_created" -eq 1 || "$nginx_site_created" -eq 1 ]]; then if sudo nginx -t; then sudo systemctl reload nginx || restore_failed=1 else restore_failed=1 fi fi if [[ "$service_changed" -eq 1 ]]; then if [[ "$service_had_previous" -eq 1 ]]; then sudo install -o root -g root -m 0644 \ "$service_backup" "/etc/systemd/system/$SERVICE_NAME" || restore_failed=1 else sudo rm -f "/etc/systemd/system/$SERVICE_NAME" || restore_failed=1 fi sudo systemctl daemon-reload || restore_failed=1 if [[ "$service_was_enabled" -eq 1 ]]; then sudo systemctl enable "$SERVICE_NAME" >/dev/null || restore_failed=1 else sudo systemctl disable "$SERVICE_NAME" >/dev/null 2>&1 || true fi fi if [[ "$restore_failed" -ne 0 ]]; then echo "Host configuration rollback needs manual attention." >&2 fi return 0 } rollback() { trap - ERR INT TERM EXIT if [[ "$lock_acquired" -eq 1 ]]; then restore_host_configuration if [[ "$promotion_prepared" -eq 1 ]]; then if [[ -d "$BACKUP_PATH" ]]; then sudo rm -rf "$ACTIVE_PATH" sudo mv "$BACKUP_PATH" "$ACTIVE_PATH" sudo systemctl restart "$SERVICE_NAME" || true elif sudo test -f "$ACTIVE_PATH/.hg-web-release" && [[ "$(sudo cat "$ACTIVE_PATH/.hg-web-release")" == "$release_id" ]]; then sudo rm -rf "$ACTIVE_PATH" sudo systemctl stop "$SERVICE_NAME" || true fi fi sudo rm -rf "$NEW_PATH" sudo rm -rf "$REPOSITORY_NEW_PATH" fi cleanup exit 1 } trap rollback ERR INT TERM trap cleanup EXIT deploy_user="$(id -un)" sudo install -d -o "$deploy_user" -g "$SERVICE_GROUP" -m 0755 "$DEPLOY_ROOT" exec 9>"$DEPLOY_ROOT/deploy.lock" if ! flock -n 9; then echo "Another hg-web deployment is already running." >&2 exit 1 fi lock_acquired=1 sudo rm -rf "$NEW_PATH" sudo cp -a "$bundle_dir" "$NEW_PATH" sudo chown -R "$deploy_user:$SERVICE_GROUP" "$NEW_PATH" printf '%s\n' "$release_id" > "$NEW_PATH/.hg-web-release" bundled_hg="$NEW_PATH/third_party/mercurial/runtime/bin/hg" test -x "$NEW_PATH/hg_web_server" test -x "$NEW_PATH/hg-web/run_hg_web" test -x "$bundled_hg" if [[ -e "$REPOSITORY_PATH" && ! -d "$REPOSITORY_PATH/.hg" ]]; then echo "REPOSITORY_PATH exists but is not a Mercurial repository." >&2 exit 1 fi if [[ ! -d "$REPOSITORY_PATH/.hg" ]]; then sudo rm -rf "$REPOSITORY_NEW_PATH" HGRCPATH= "$bundled_hg" clone \ --noupdate --pull "$workspace" "$REPOSITORY_NEW_PATH" sudo chown -R "$SERVICE_USER:$SERVICE_GROUP" "$REPOSITORY_NEW_PATH" sudo mv "$REPOSITORY_NEW_PATH" "$REPOSITORY_PATH" else HGRCPATH= "$bundled_hg" --repository "$workspace" \ bundle --all "$repository_bundle" sudo chown "$deploy_user:$SERVICE_GROUP" "$repository_bundle" chmod 0640 "$repository_bundle" sudo -u "$SERVICE_USER" env HGRCPATH= \ "$bundled_hg" --repository "$REPOSITORY_PATH" \ pull "$repository_bundle" fi sudo -u "$SERVICE_USER" env HGRCPATH= \ "$bundled_hg" --repository "$REPOSITORY_PATH" verify sudo chown -R "$SERVICE_USER:$SERVICE_GROUP" "$NEW_PATH" sed \ -e "s|__SERVICE_USER__|${SERVICE_USER}|g" \ -e "s|__SERVICE_GROUP__|${SERVICE_GROUP}|g" \ -e "s|__ACTIVE_PATH__|${ACTIVE_PATH}|g" \ -e "s|__HG_ALLOW_PUSH__|${HG_ALLOW_PUSH}|g" \ -e "s|__REPOSITORY_PATH__|${REPOSITORY_PATH}|g" \ "$service_template" > "$rendered_service" if sudo test -f "/etc/systemd/system/$SERVICE_NAME"; then sudo cat "/etc/systemd/system/$SERVICE_NAME" > "$service_backup" chmod 0600 "$service_backup" service_had_previous=1 fi if sudo systemctl is-enabled --quiet "$SERVICE_NAME"; then service_was_enabled=1 fi service_changed=1 sudo install -o root -g root -m 0644 \ "$rendered_service" "/etc/systemd/system/$SERVICE_NAME" sudo systemctl daemon-reload manage_nginx=0 case "$MANAGE_NGINX" in 1|true|yes) manage_nginx=1 ;; 0|false|no) ;; auto) if command -v nginx >/dev/null 2>&1 && [[ -d /etc/nginx/sites-available && -d /etc/nginx/sites-enabled ]]; then manage_nginx=1 fi ;; *) echo "MANAGE_NGINX must be auto, true, or false." >&2 exit 1 ;; esac if [[ "$manage_nginx" -eq 1 && ! -e "$NGINX_SITE_AVAILABLE" && ! -L "$NGINX_SITE_AVAILABLE" ]]; then if [[ ! "$NGINX_SERVER_NAME" =~ ^[A-Za-z0-9.-]+$ ]]; then echo "Invalid NGINX_SERVER_NAME: $NGINX_SERVER_NAME" >&2 exit 1 fi if [[ ! -f "$NGINX_SITE_TEMPLATE" ]]; then echo "Nginx template not found: $NGINX_SITE_TEMPLATE" >&2 exit 1 fi sed "s/__SERVER_NAME__/${NGINX_SERVER_NAME}/g" \ "$NGINX_SITE_TEMPLATE" > "$rendered_nginx" sudo install -D -o root -g root -m 0644 \ "$rendered_nginx" "$NGINX_SITE_AVAILABLE" nginx_site_created=1 sudo ln -s "$NGINX_SITE_AVAILABLE" "$NGINX_SITE_ENABLED" nginx_link_created=1 sudo nginx -t sudo systemctl reload nginx elif [[ "$manage_nginx" -eq 1 && ! -e "$NGINX_SITE_ENABLED" && ! -L "$NGINX_SITE_ENABLED" ]]; then sudo ln -s "$NGINX_SITE_AVAILABLE" "$NGINX_SITE_ENABLED" nginx_link_created=1 sudo nginx -t sudo systemctl reload nginx elif [[ "$manage_nginx" -eq 1 ]]; then echo "Nginx site already exists; leaving it unchanged." fi sudo rm -rf "$BACKUP_PATH" promotion_prepared=1 if [[ -e "$ACTIVE_PATH" || -L "$ACTIVE_PATH" ]]; then sudo mv "$ACTIVE_PATH" "$BACKUP_PATH" fi sudo mv "$NEW_PATH" "$ACTIVE_PATH" sudo systemctl enable "$SERVICE_NAME" >/dev/null sudo systemctl restart "$SERVICE_NAME" if ! health_check; then echo "hg-web health check failed." >&2 rollback fi sudo rm -rf "$BACKUP_PATH" trap - ERR INT TERM EXIT cleanup echo "Deployment complete: $ACTIVE_PATH"