#!/usr/bin/env bash # Evomedia.net Token Savers — https://github.com/kellymichels/zscripts-token-savers # Created by Kelly Michels · dev@evomedia.net # Licensed under the MIT License. See LICENSE. # # zdeploy — deploy any project defined in zconfig.json to the server. # Each project runs its own docker compose stack; the handler is picked by the # project's "kind": python | vite | nextjs | edge | docker. # # Usage: # zdeploy [ ...] [--note "message"] # zdeploy all # every project (edge kinds first), stop at first failure # # Flow (python/vite/nextjs): zip source -> free server disk space -> scp up -> # unzip into remote.path (preserving server-side .env) -> docker compose build + up # -> verify the live site reports the new build version. Zips are always deleted. set -uo pipefail source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh" z_track_start "$@" z_need_config z_require ssh scp zip curl jq projects=(); NOTE="Build deployed" while [ $# -gt 0 ]; do case "$1" in --note) NOTE="${2:-Build deployed}"; shift 2 ;; *) projects+=("${1#-}"); shift ;; # tolerate switch-style keys (zdeploy -myproject) esac done if [ "${#projects[@]}" -eq 0 ]; then printf '\n'; warn "Usage: zdeploy [ ...] | all [--note \"message\"]" dim " Projects in zconfig.json: $(zproj_csv)" dim " 'all' deploys everything (edge kinds first) and stops at the first failure." exit 1 fi EC2_IP="$(zec2_ip)" STACK_ROOT="$(zq '.ec2.stackRoot')" REMOTE_HOME="/home/$(zec2_user)" EC2_USER="$(zec2_user)" TEMP_ROOT="$(zq '.paths.temp')" { [ -z "$TEMP_ROOT" ] || [ "$TEMP_ROOT" = "null" ]; } && TEMP_ROOT="${TMPDIR:-/tmp}" mkdir -p "$TEMP_ROOT" # 'all' -> every project, edge kinds first (the proxy must route before apps ship). if printf '%s\n' "${projects[@]}" | grep -qx 'all'; then mapfile -t edge_keys < <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind == "edge") | .key' "$ZCONFIG") mapfile -t rest_keys < <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind != "edge") | .key' "$ZCONFIG") projects=("${edge_keys[@]+"${edge_keys[@]}"}" "${rest_keys[@]+"${rest_keys[@]}"}") info "Deploying all projects: $(printf '%s ' "${projects[@]}")" fi deploy_zip_name() { # local zn; zn="$(zproj "$1" .deploy.zipName)" [ -n "$zn" ] && printf '%s' "$zn" || printf '%sDeploy.zip' "$1" } # Pre-upload cleanup: remove stale deploy zips, prune docker, truncate big logs, # fail if under 1.5 GB free. preflight_cleanup() { # printf '\n'; info "--- [Preflight] Freeing disk space on the server ---" local rm_clause="true" [ $# -gt 0 ] && rm_clause="rm -f $*" local cmd cmd="echo '--- df / before cleanup ---'; df -h /" cmd+="; echo '--- removing stale deploy artifacts ---'; $rm_clause" cmd+="; echo '--- pruning docker build cache + dangling images + stopped containers ---'" cmd+="; sudo docker container prune -f >/dev/null 2>&1 || true" cmd+="; sudo docker builder prune -f >/dev/null 2>&1 || true" cmd+="; sudo docker image prune -af >/dev/null 2>&1 || true" cmd+="; echo '--- truncating large container logs ---'" cmd+="; sudo find /var/lib/docker/containers/ -name '*-json.log' -size +50M -exec truncate -s 0 {} + 2>/dev/null || true" cmd+="; echo '--- df / after cleanup ---'; df -h /" cmd+='; avail_mb=$(df --output=avail -BM / | tail -n 1 | tr -dc 0-9)' cmd+='; [ -z "$avail_mb" ] && avail_mb=0' cmd+='; echo available_mb=$avail_mb' cmd+='; if [ "$avail_mb" -lt 1500 ]; then echo "ERROR: less than 1.5 GB free on /. Grow the root volume or run: sudo docker system prune -af" >&2; exit 11; fi' zssh "$cmd" || { err "Server pre-flight cleanup failed. Root volume too full (need ~1.5 GB free, ideally 3+)."; return 1; } } # Post-deploy cleanup: prune what this deploy created; running stacks untouched. post_cleanup() { #