Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-21 08:46:28

0001 #!/usr/bin/env bash
0002 #
0003 # Lightweight swf-monitor deploy for UI and MCP development.
0004 #
0005 # This updates the active release in place. It is intentionally not a full
0006 # production release deploy: no venv copy, dependency install, migrations,
0007 # Apache config sync, current-symlink flip, ops-agent restart, or bot restart.
0008 
0009 set -euo pipefail
0010 
0011 DEPLOY_ROOT="${SWF_DEPLOY_ROOT:-/opt/swf-monitor}"
0012 CURRENT_DIR="$DEPLOY_ROOT/current"
0013 SHARED_STATIC="$DEPLOY_ROOT/shared/static"
0014 MCP_SERVICE="${SWF_MCP_SERVICE:-swf-monitor-mcp-asgi.service}"
0015 
0016 DO_UI=false
0017 DO_MCP=false
0018 DO_STATIC=false
0019 DRY_RUN=false
0020 
0021 usage() {
0022     cat <<'EOF'
0023 Usage: deploy-lightweight-ui-mcp.sh [--ui] [--mcp] [--static] [--dry-run]
0024 
0025 Fast in-place deploy for swf-monitor UI/template/view changes and MCP tool
0026 changes only. Use the full deploy for migrations, requirements/venv changes,
0027 Apache config, systemd units, ops-agent code, bot code, or release changes.
0028 This follows the normal dev-area sync workflow: current checkout to active
0029 /opt/swf-monitor/current release, without creating a release directory or
0030 moving the current symlink.
0031 
0032 Options:
0033   --ui       Sync UI/web code and templates; recycle mod_wsgi by touching wsgi.py
0034   --mcp      Sync MCP code/helpers; restart swf-monitor-mcp-asgi.service
0035   --static   Also collect and publish static assets
0036   --dry-run  Show rsync changes and planned process actions without applying
0037   --help     Show this help
0038 
0039 Examples:
0040   sudo ./deploy-lightweight-ui-mcp.sh --ui
0041   sudo ./deploy-lightweight-ui-mcp.sh --mcp
0042   sudo ./deploy-lightweight-ui-mcp.sh --ui --mcp
0043   sudo ./deploy-lightweight-ui-mcp.sh --ui --static
0044 EOF
0045 }
0046 
0047 log() {
0048     echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
0049 }
0050 
0051 while [[ $# -gt 0 ]]; do
0052     case "$1" in
0053         --ui)
0054             DO_UI=true
0055             ;;
0056         --mcp)
0057             DO_MCP=true
0058             ;;
0059         --static)
0060             DO_STATIC=true
0061             ;;
0062         --dry-run)
0063             DRY_RUN=true
0064             ;;
0065         --help|-h)
0066             usage
0067             exit 0
0068             ;;
0069         *)
0070             echo "Unknown option: $1" >&2
0071             usage >&2
0072             exit 2
0073             ;;
0074     esac
0075     shift
0076 done
0077 
0078 if [[ "$DO_UI" != true && "$DO_MCP" != true ]]; then
0079     echo "ERROR: choose --ui and/or --mcp; --static is an add-on" >&2
0080     usage >&2
0081     exit 2
0082 fi
0083 
0084 if [[ "$DRY_RUN" != true && "$EUID" -ne 0 ]]; then
0085     echo "ERROR: run with sudo so the active release and systemd service can be updated" >&2
0086     exit 1
0087 fi
0088 
0089 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
0090 REPO_ROOT="$SCRIPT_DIR"
0091 SRC_DIR="$REPO_ROOT/src"
0092 TARGET_SRC="$CURRENT_DIR/src"
0093 
0094 if [[ ! -d "$SRC_DIR/monitor_app" || ! -f "$SRC_DIR/manage.py" ]]; then
0095     echo "ERROR: source tree not found at $SRC_DIR" >&2
0096     exit 1
0097 fi
0098 
0099 if [[ ! -d "$TARGET_SRC" || ! -f "$TARGET_SRC/swf_monitor_project/wsgi.py" ]]; then
0100     echo "ERROR: active deployment source tree not found at $TARGET_SRC" >&2
0101     exit 1
0102 fi
0103 
0104 if [[ -d "$REPO_ROOT/.git" ]]; then
0105     OUT_OF_SCOPE=$(git -C "$REPO_ROOT" status --short --untracked-files=all \
0106         | awk '{print $2}' \
0107         | grep -E '(^requirements|^pyproject\.toml$|^apache-swf-monitor\.conf$|\.service$|^config/apache/|/migrations/|^agents/|/panda/|/testbed_bot/)' \
0108         || true)
0109     if [[ -n "$OUT_OF_SCOPE" ]]; then
0110         echo "ERROR: working tree contains changes outside lightweight UI/MCP scope:" >&2
0111         echo "$OUT_OF_SCOPE" >&2
0112         echo "Use the full deploy for these changes, or commit/stash unrelated work before running this script." >&2
0113         exit 1
0114     fi
0115 fi
0116 
0117 RSYNC_ARGS=(-a --delete --exclude '__pycache__/' --exclude '*.pyc')
0118 if [[ "$DRY_RUN" == true ]]; then
0119     RSYNC_ARGS+=(--dry-run --itemize-changes)
0120 fi
0121 
0122 MONITOR_EXCLUDES=(
0123     --exclude 'migrations/'
0124     --exclude 'management/'
0125     --exclude 'panda/'
0126     --exclude 'testbed_bot/'
0127     --exclude 'tests/'
0128 )
0129 PCS_EXCLUDES=(
0130     --exclude 'migrations/'
0131     --exclude 'management/'
0132     --exclude 'tests/'
0133 )
0134 AI_EXCLUDES=(
0135     --exclude 'migrations/'
0136     --exclude 'tests/'
0137 )
0138 
0139 if [[ "$DO_MCP" != true ]]; then
0140     MONITOR_EXCLUDES+=(--exclude 'mcp/')
0141 fi
0142 
0143 if [[ "$DO_UI" != true ]]; then
0144     MONITOR_EXCLUDES+=(--exclude 'templates/')
0145     PCS_EXCLUDES+=(--exclude 'templates/')
0146     AI_EXCLUDES+=(--exclude 'templates/')
0147 fi
0148 
0149 if [[ "$DO_STATIC" != true ]]; then
0150     MONITOR_EXCLUDES+=(--exclude 'static/')
0151     PCS_EXCLUDES+=(--exclude 'static/')
0152 fi
0153 
0154 log "Lightweight deploy source: $REPO_ROOT"
0155 log "Active target: $CURRENT_DIR"
0156 
0157 log "Syncing monitor_app lightweight paths..."
0158 rsync "${RSYNC_ARGS[@]}" "${MONITOR_EXCLUDES[@]}" "$SRC_DIR/monitor_app/" "$TARGET_SRC/monitor_app/"
0159 
0160 # pcs ships from swf-epicprod as an installed package; lightweight-sync the
0161 # swf-epicprod dev tree onto the deployed venv's installed copy (migrations
0162 # and management commands still ride the full deploy only).
0163 EPICPROD_ROOT="/data/wenauseic/github/swf-epicprod"
0164 # Resolve from $CURRENT_DIR: python -c puts cwd first on sys.path, so running
0165 # this script from a tree containing pcs/ would shadow the venv copy.
0166 TARGET_PCS=$(cd "$CURRENT_DIR" && "$CURRENT_DIR/.venv/bin/python" -c "import pcs, os; print(os.path.dirname(pcs.__file__))")
0167 case "$TARGET_PCS" in
0168     "$CURRENT_DIR"/.venv/*) ;;
0169     *)
0170         echo "ERROR: deployed pcs resolves outside the deployed venv: $TARGET_PCS" >&2
0171         echo "Run the full deploy to freeze swf-epicprod, then retry." >&2
0172         exit 1
0173         ;;
0174 esac
0175 log "Syncing pcs lightweight paths (swf-epicprod -> deployed venv)..."
0176 rsync "${RSYNC_ARGS[@]}" "${PCS_EXCLUDES[@]}" "$EPICPROD_ROOT/pcs/" "$TARGET_PCS/"
0177 
0178 log "Syncing ai lightweight paths..."
0179 rsync "${RSYNC_ARGS[@]}" "${AI_EXCLUDES[@]}" "$SRC_DIR/ai/" "$TARGET_SRC/ai/"
0180 
0181 if [[ "$DO_UI" == true ]]; then
0182     log "Syncing project-level templates and URL routing..."
0183     rsync "${RSYNC_ARGS[@]}" "$SRC_DIR/templates/" "$TARGET_SRC/templates/"
0184     rsync "${RSYNC_ARGS[@]}" "$SRC_DIR/swf_monitor_project/urls.py" "$TARGET_SRC/swf_monitor_project/urls.py"
0185 fi
0186 
0187 if [[ "$DO_MCP" == true ]]; then
0188     log "Syncing MCP ASGI entrypoint..."
0189     rsync "${RSYNC_ARGS[@]}" "$SRC_DIR/swf_monitor_project/mcp_asgi.py" "$TARGET_SRC/swf_monitor_project/mcp_asgi.py"
0190 fi
0191 
0192 if [[ "$DO_STATIC" == true ]]; then
0193     if [[ "$DRY_RUN" == true ]]; then
0194         log "Would sync project-level static sources, collect static assets, and sync them to $SHARED_STATIC"
0195     else
0196         # STATICFILES_DIRS points at src/static, which shadows app static at
0197         # collectstatic — it must be synced too or stale copies win.
0198         log "Syncing project-level static sources..."
0199         rsync "${RSYNC_ARGS[@]}" "$SRC_DIR/static/" "$TARGET_SRC/static/"
0200         log "Collecting static assets..."
0201         cd "$TARGET_SRC"
0202         "$CURRENT_DIR/.venv/bin/python" manage.py collectstatic --noinput --settings=swf_monitor_project.settings
0203         log "Syncing static assets to shared Apache static directory..."
0204         rsync -a --delete "$TARGET_SRC/staticfiles/" "$SHARED_STATIC/"
0205     fi
0206 fi
0207 
0208 if [[ "$DO_UI" == true ]]; then
0209     if [[ "$DRY_RUN" == true ]]; then
0210         log "Would touch $TARGET_SRC/swf_monitor_project/wsgi.py to recycle mod_wsgi"
0211     else
0212         log "Recycling mod_wsgi app by touching wsgi.py..."
0213         touch "$TARGET_SRC/swf_monitor_project/wsgi.py"
0214     fi
0215 fi
0216 
0217 if [[ "$DO_MCP" == true ]]; then
0218     if [[ "$DRY_RUN" == true ]]; then
0219         log "Would restart $MCP_SERVICE"
0220     else
0221         log "Restarting MCP ASGI worker ($MCP_SERVICE)..."
0222         systemctl restart "$MCP_SERVICE"
0223     fi
0224 fi
0225 
0226 log "Lightweight UI/MCP deploy complete"