feat: add template drift check and cache management to deploy tooling
deploy.sh now checks local vs remote templates via md5sum on every deploy and warns about drift. Pass --sync-templates to push changed files. Also reports cached card image counts on the target server. New clear-card-cache.sh script inspects or clears cached PNG/APNG card images inside the API container, with --apng-only and --all modes. Added scripts/README.md documenting all operational scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
19003215a3
commit
91a57454f2
55
scripts/README.md
Normal file
55
scripts/README.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Scripts
|
||||
|
||||
Operational scripts for the Paper Dynasty Database API.
|
||||
|
||||
## deploy.sh
|
||||
|
||||
Deploy the API by tagging a commit and triggering CI/CD.
|
||||
|
||||
```bash
|
||||
./scripts/deploy.sh dev # Tag HEAD as 'dev', CI builds :dev image
|
||||
./scripts/deploy.sh prod # Create CalVer tag + 'latest' + 'production'
|
||||
./scripts/deploy.sh dev abc1234 # Tag a specific commit
|
||||
./scripts/deploy.sh dev --sync-templates # Deploy + push changed templates to server
|
||||
```
|
||||
|
||||
**Template drift check** runs automatically on every deploy. Compares local `storage/templates/*.html` against the target server via md5sum and warns if any files differ. Templates are volume-mounted (not baked into the Docker image), so code deploys alone won't update them.
|
||||
|
||||
**Cached image report** also runs automatically, showing PNG and APNG counts on the target server.
|
||||
|
||||
| Environment | SSH Host | Template Path |
|
||||
|---|---|---|
|
||||
| dev | `pd-database` | `/home/cal/container-data/dev-pd-database/storage/templates` |
|
||||
| prod | `akamai` | `/root/container-data/paper-dynasty/storage/templates` |
|
||||
|
||||
## clear-card-cache.sh
|
||||
|
||||
Inspect or clear cached rendered card images inside the API container.
|
||||
|
||||
```bash
|
||||
./scripts/clear-card-cache.sh dev # Report cache size (dry run)
|
||||
./scripts/clear-card-cache.sh dev --apng-only # Delete animated card cache only
|
||||
./scripts/clear-card-cache.sh dev --all # Delete all cached card images
|
||||
```
|
||||
|
||||
Cached images regenerate on demand when next requested. APNG files (T3/T4 animated cards) are the most likely to go stale after template CSS changes. Both destructive modes prompt for confirmation before deleting.
|
||||
|
||||
| Environment | SSH Host | Container | Cache Path |
|
||||
|---|---|---|---|
|
||||
| dev | `pd-database` | `dev_pd_database` | `/app/storage/cards/` |
|
||||
| prod | `akamai` | `pd_api` | `/app/storage/cards/` |
|
||||
|
||||
## Migration Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `migrate_to_postgres.py` | One-time SQLite to PostgreSQL migration |
|
||||
| `migrate_missing_data.py` | Backfill missing data after migration |
|
||||
| `db_migrations.py` (in repo root) | Schema migrations |
|
||||
|
||||
## Utility Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `wipe_gauntlet_team.py` | Reset a gauntlet team's state |
|
||||
| `audit_sqlite.py` | Audit legacy SQLite database |
|
||||
89
scripts/clear-card-cache.sh
Executable file
89
scripts/clear-card-cache.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Clear cached card images from the API container
|
||||
# Usage: ./scripts/clear-card-cache.sh <dev|prod> [--apng-only|--all]
|
||||
#
|
||||
# With no flags: reports cache size only (dry run)
|
||||
# --apng-only: delete only .apng files (animated cards)
|
||||
# --all: delete all cached card images (.png + .apng)
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
declare -A DEPLOY_HOST=([dev]="pd-database" [prod]="akamai")
|
||||
declare -A CONTAINER=([dev]="dev_pd_database" [prod]="pd_api")
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <dev|prod> [--apng-only|--all]"
|
||||
echo ""
|
||||
echo " No flag Report cache size (dry run)"
|
||||
echo " --apng-only Delete only .apng files (animated cards)"
|
||||
echo " --all Delete all cached card images"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ $# -lt 1 ]] && usage
|
||||
|
||||
ENV="$1"
|
||||
ACTION="${2:-report}"
|
||||
|
||||
if [[ "$ENV" != "dev" && "$ENV" != "prod" ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
HOST="${DEPLOY_HOST[$ENV]}"
|
||||
CTR="${CONTAINER[$ENV]}"
|
||||
CACHE_PATH="/app/storage/cards"
|
||||
|
||||
report() {
|
||||
echo -e "${CYAN}Card image cache on ${HOST} (${CTR}):${NC}"
|
||||
ssh "$HOST" "
|
||||
png_count=\$(docker exec $CTR find $CACHE_PATH -name '*.png' 2>/dev/null | wc -l)
|
||||
apng_count=\$(docker exec $CTR find $CACHE_PATH -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 "${RED}Could not reach ${HOST}.${NC}"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
report
|
||||
|
||||
case "$ACTION" in
|
||||
report)
|
||||
echo -e "${GREEN}Dry run — no files deleted. Pass --apng-only or --all to clear.${NC}"
|
||||
;;
|
||||
|
||||
--apng-only)
|
||||
echo -e "${YELLOW}Deleting all .apng files from ${CTR}...${NC}"
|
||||
read -rp "Proceed? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || {
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
}
|
||||
|
||||
deleted=$(ssh "$HOST" "docker exec $CTR find $CACHE_PATH -name '*.apng' -delete -print 2>/dev/null | wc -l")
|
||||
echo -e "${GREEN}Deleted ${deleted} .apng files.${NC}"
|
||||
;;
|
||||
|
||||
--all)
|
||||
echo -e "${RED}Deleting ALL cached card images from ${CTR}...${NC}"
|
||||
read -rp "This will clear PNG and APNG caches. Proceed? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || {
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
}
|
||||
|
||||
deleted=$(ssh "$HOST" "docker exec $CTR find $CACHE_PATH -type f \( -name '*.png' -o -name '*.apng' \) -delete -print 2>/dev/null | wc -l")
|
||||
echo -e "${GREEN}Deleted ${deleted} cached card images.${NC}"
|
||||
;;
|
||||
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@ -1,31 +1,67 @@
|
||||
#!/bin/bash
|
||||
# Deploy Paper Dynasty Database API
|
||||
# Usage: ./scripts/deploy.sh <dev|prod> [commit]
|
||||
# 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> [commit]"
|
||||
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"
|
||||
COMMIT="${2:-HEAD}"
|
||||
shift
|
||||
|
||||
# Parse optional flags
|
||||
COMMIT="HEAD"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--sync-templates)
|
||||
SYNC_TEMPLATES=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
COMMIT="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SHA=$(git rev-parse "$COMMIT" 2>/dev/null) || {
|
||||
echo -e "${RED}Invalid commit: $COMMIT${NC}"
|
||||
@ -40,6 +76,82 @@ if ! git branch -a --contains "$SHA" 2>/dev/null | grep -qE '(^|\s)(main|remotes
|
||||
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
|
||||
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 ---
|
||||
report_cache() {
|
||||
local host="${DEPLOY_HOST[$ENV]}"
|
||||
local container
|
||||
if [[ "$ENV" == "dev" ]]; then
|
||||
container="dev_pd_database"
|
||||
else
|
||||
container="pd_api"
|
||||
fi
|
||||
|
||||
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}"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user