- Reject unknown --flags with error instead of silently treating as commit SHA - Declare remote_hash as local to prevent stale values across loop iterations - Use associative array for container names (consistent with DEPLOY_HOST pattern) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
204 lines
5.8 KiB
Bash
Executable File
204 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Deploy Paper Dynasty Database API
|
|
# Usage: ./scripts/deploy.sh <dev|prod> [--sync-templates] [commit]
|
|
#
|
|
# Dev: Force-updates the "dev" git tag → CI builds :dev Docker image
|
|
# Prod: Creates CalVer tag + force-updates "latest" and "production" git tags
|
|
# → CI builds :<calver>, :latest, :production Docker images
|
|
#
|
|
# Options:
|
|
# --sync-templates Upload changed templates to the target server via scp
|
|
#
|
|
# Templates are volume-mounted (not in the Docker image). The script always
|
|
# checks for template drift and warns if local/remote differ. Pass
|
|
# --sync-templates to actually push the changed files.
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
REMOTE="origin"
|
|
SYNC_TEMPLATES=false
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TEMPLATE_DIR="$SCRIPT_DIR/../storage/templates"
|
|
|
|
# Server config
|
|
declare -A DEPLOY_HOST=([dev]="pd-database" [prod]="akamai")
|
|
declare -A TEMPLATE_PATH=(
|
|
[dev]="/home/cal/container-data/dev-pd-database/storage/templates"
|
|
[prod]="/root/container-data/paper-dynasty/storage/templates"
|
|
)
|
|
|
|
usage() {
|
|
echo "Usage: $0 <dev|prod> [--sync-templates] [commit]"
|
|
echo ""
|
|
echo " dev [commit] Force-update 'dev' tag on HEAD or specified commit"
|
|
echo " prod [commit] Create CalVer + 'latest' + 'production' tags on HEAD or specified commit"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --sync-templates Upload changed templates to the target server"
|
|
exit 1
|
|
}
|
|
|
|
[[ $# -lt 1 ]] && usage
|
|
|
|
ENV="$1"
|
|
shift
|
|
|
|
# Parse optional flags
|
|
COMMIT="HEAD"
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--sync-templates)
|
|
SYNC_TEMPLATES=true
|
|
shift
|
|
;;
|
|
--*)
|
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
usage
|
|
;;
|
|
*)
|
|
COMMIT="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
SHA=$(git rev-parse "$COMMIT" 2>/dev/null) || {
|
|
echo -e "${RED}Invalid commit: $COMMIT${NC}"
|
|
exit 1
|
|
}
|
|
SHA_SHORT="${SHA:0:7}"
|
|
|
|
git fetch --tags "$REMOTE"
|
|
|
|
if ! git branch -a --contains "$SHA" 2>/dev/null | grep -qE '(^|\s)(main|remotes/origin/main)$'; then
|
|
echo -e "${RED}Commit $SHA_SHORT is not on main. Aborting.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# --- Template drift check ---
|
|
check_templates() {
|
|
local host="${DEPLOY_HOST[$ENV]}"
|
|
local remote_path="${TEMPLATE_PATH[$ENV]}"
|
|
|
|
echo -e "${CYAN}Checking templates against ${host}:${remote_path}...${NC}"
|
|
|
|
local local_hashes remote_hashes
|
|
local_hashes=$(cd "$TEMPLATE_DIR" && md5sum *.html 2>/dev/null | sort -k2)
|
|
remote_hashes=$(ssh "$host" "cd '$remote_path' && md5sum *.html 2>/dev/null | sort -k2" 2>/dev/null) || {
|
|
echo -e "${YELLOW} Could not reach ${host} — skipping template check.${NC}"
|
|
return 0
|
|
}
|
|
|
|
local changed=()
|
|
local missing_remote=()
|
|
while IFS= read -r line; do
|
|
local hash file remote_hash
|
|
hash=$(echo "$line" | awk '{print $1}')
|
|
file=$(echo "$line" | awk '{print $2}')
|
|
remote_hash=$(echo "$remote_hashes" | awk -v f="$file" '$2 == f {print $1}')
|
|
if [[ -z "$remote_hash" ]]; then
|
|
missing_remote+=("$file")
|
|
elif [[ "$hash" != "$remote_hash" ]]; then
|
|
changed+=("$file")
|
|
fi
|
|
done <<<"$local_hashes"
|
|
|
|
if [[ ${#changed[@]} -eq 0 && ${#missing_remote[@]} -eq 0 ]]; then
|
|
echo -e "${GREEN} Templates in sync.${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${YELLOW} Template drift detected:${NC}"
|
|
for f in "${changed[@]+"${changed[@]}"}"; do
|
|
[[ -n "$f" ]] && echo -e " ${YELLOW}CHANGED${NC} $f"
|
|
done
|
|
for f in "${missing_remote[@]+"${missing_remote[@]}"}"; do
|
|
[[ -n "$f" ]] && echo -e " ${YELLOW}MISSING${NC} $f (not on server)"
|
|
done
|
|
|
|
if [[ "$SYNC_TEMPLATES" == true ]]; then
|
|
echo -e "${CYAN} Syncing templates...${NC}"
|
|
for f in "${changed[@]+"${changed[@]}"}" "${missing_remote[@]+"${missing_remote[@]}"}"; do
|
|
[[ -n "$f" ]] && scp "$TEMPLATE_DIR/$f" "${host}:${remote_path}/$f"
|
|
done
|
|
echo -e "${GREEN} Templates synced to ${host}.${NC}"
|
|
else
|
|
echo -e "${YELLOW} Run with --sync-templates to push changes.${NC}"
|
|
fi
|
|
}
|
|
|
|
check_templates
|
|
|
|
# --- Cached image report ---
|
|
declare -A API_CONTAINER=([dev]="dev_pd_database" [prod]="pd_api")
|
|
|
|
report_cache() {
|
|
local host="${DEPLOY_HOST[$ENV]}"
|
|
local container="${API_CONTAINER[$ENV]}"
|
|
|
|
echo -e "${CYAN}Cached card images on ${host} (${container}):${NC}"
|
|
ssh "$host" "
|
|
png_count=\$(docker exec $container find /app/storage/cards -name '*.png' 2>/dev/null | wc -l)
|
|
apng_count=\$(docker exec $container find /app/storage/cards -name '*.apng' 2>/dev/null | wc -l)
|
|
echo \" PNG: \${png_count} files\"
|
|
echo \" APNG: \${apng_count} files\"
|
|
echo \" Total: \$((\${png_count} + \${apng_count})) files\"
|
|
" 2>/dev/null || echo -e "${YELLOW} Could not reach ${host} — skipping cache report.${NC}"
|
|
}
|
|
|
|
report_cache
|
|
|
|
case "$ENV" in
|
|
dev)
|
|
echo -e "${YELLOW}Deploying to dev...${NC}"
|
|
echo -e " Commit: ${SHA_SHORT}"
|
|
|
|
git tag -f dev "$SHA"
|
|
git push "$REMOTE" dev --force
|
|
|
|
echo -e "${GREEN}Tagged ${SHA_SHORT} as 'dev' and pushed. CI will build :dev image.${NC}"
|
|
;;
|
|
|
|
prod)
|
|
echo -e "${YELLOW}Deploying to prod...${NC}"
|
|
|
|
YEAR=$(date -u +%Y)
|
|
MONTH=$(date -u +%-m)
|
|
PREFIX="${YEAR}.${MONTH}."
|
|
|
|
LAST_BUILD=$(git tag -l "${PREFIX}*" | sed "s/^${PREFIX}//" | sort -n | tail -1)
|
|
BUILD=$((${LAST_BUILD:-0} + 1))
|
|
CALVER="${PREFIX}${BUILD}"
|
|
|
|
echo -e " Commit: ${SHA_SHORT}"
|
|
echo -e " Version: ${CALVER}"
|
|
echo -e " Tags: ${CALVER}, latest, production"
|
|
|
|
read -rp "Proceed? [y/N] " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || {
|
|
echo "Aborted."
|
|
exit 0
|
|
}
|
|
|
|
git tag "$CALVER" "$SHA"
|
|
git tag -f latest "$SHA"
|
|
git tag -f production "$SHA"
|
|
|
|
git push "$REMOTE" "$CALVER"
|
|
git push "$REMOTE" latest --force
|
|
git push "$REMOTE" production --force
|
|
|
|
echo -e "${GREEN}Tagged ${SHA_SHORT} as '${CALVER}', 'latest', 'production' and pushed.${NC}"
|
|
echo -e "${GREEN}CI will build :${CALVER}, :latest, :production images.${NC}"
|
|
;;
|
|
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|