view hg-web/deploy.sh @ 265:056790c4fb0d

add role-aware Epi assistant prompts Add verified June knowledge, guest/member/admin Copilot profiles, profile-isolated session recovery, animated Epi greetings, and a single authoritative runtime config workflow for inference. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 10:50:30 -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"