view hg-web/deploy.sh @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -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"