feat(deployment): add unified deployment script with auto-commit cache versioning
Replaces awkward deployment workflow with streamlined process: ✅ Before: 1. Deploy script detects JS changes 2. Runs update-cache-version.js 3. Creates uncommitted changes 4. Prompts user to cancel/commit/re-run 5. Manual loop required ✅ After: 1. Deploy script detects JS changes 2. Runs update-cache-version.js 3. Auto-commits cache changes 4. Continues to deployment 5. Done Features: - Unified script (scripts/deploy.sh) replaces two separate scripts - Auto-commit cache version changes (no manual loops) - Flags: --frontend-only, --force-cache, --restart, --dry-run - Security: preserves .rsyncignore exclusions - Validation: checks local server, git status, dry-run preview Migration: - OLD: ./scripts/deploy-full-project-SAFE.sh NEW: ./scripts/deploy.sh - OLD: ./scripts/deploy-frontend.sh NEW: ./scripts/deploy.sh --frontend-only Changes: - Added: scripts/deploy.sh (unified deployment script) - Modified: scripts/deploy-frontend.sh (deprecated with migration notice) - Updated: CLAUDE.md (new deployment workflow documentation) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3210b6e58d
commit
dd502eef65
3 changed files with 394 additions and 3 deletions
11
CLAUDE.md
11
CLAUDE.md
|
|
@ -108,8 +108,13 @@ node scripts/check-session-pressure.js # Check context pressure
|
|||
# Local development
|
||||
npm start # Start local server (port 9000)
|
||||
|
||||
# Production deployment
|
||||
./scripts/deploy-full-project-SAFE.sh # Deploy to production (safe)
|
||||
# Production deployment (unified workflow)
|
||||
./scripts/deploy.sh # Full deployment (auto-detects changes, auto-commits cache)
|
||||
./scripts/deploy.sh --frontend-only # Frontend-only (public/ directory)
|
||||
./scripts/deploy.sh --force-cache --restart # Force cache update + restart service
|
||||
./scripts/deploy.sh --dry-run # Preview without deploying
|
||||
|
||||
# Service management (remote)
|
||||
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net "sudo systemctl status tractatus"
|
||||
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net "sudo systemctl restart tractatus"
|
||||
|
||||
|
|
@ -141,5 +146,5 @@ node scripts/generate-single-pdf.js <input.md> <output.pdf>
|
|||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-24 (Added session closedown script; session management now fully automated)
|
||||
**Last Updated**: 2025-10-25 (Added unified deployment script; cache versioning now auto-committed)
|
||||
**Philosophy**: If it can be enforced in code, it should not be documented here.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
##
|
||||
## ⚠️ DEPRECATED - Use scripts/deploy.sh instead
|
||||
##
|
||||
## This script is deprecated in favor of the unified deployment script.
|
||||
## The new script provides:
|
||||
## - Automatic cache versioning (update-cache-version.js)
|
||||
## - Auto-commit of cache changes (no more manual loops)
|
||||
## - Frontend-only mode (--frontend-only flag)
|
||||
## - Better security and validation
|
||||
##
|
||||
## Migration:
|
||||
## OLD: ./scripts/deploy-frontend.sh
|
||||
## NEW: ./scripts/deploy.sh --frontend-only
|
||||
##
|
||||
## See: scripts/deploy.sh for full documentation
|
||||
##
|
||||
|
||||
echo "⚠️ WARNING: This script is deprecated"
|
||||
echo "Please use: ./scripts/deploy.sh --frontend-only"
|
||||
echo ""
|
||||
read -p "Continue with deprecated script anyway? (yes/NO): " continue_deprecated
|
||||
if [ "$continue_deprecated" != "yes" ]; then
|
||||
echo "Cancelled. Use ./scripts/deploy.sh --frontend-only instead."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Deploy Frontend with Automatic Cache Busting
|
||||
# Ensures users always get latest JS/CSS without manual cache clearing
|
||||
|
||||
|
|
|
|||
360
scripts/deploy.sh
Executable file
360
scripts/deploy.sh
Executable file
|
|
@ -0,0 +1,360 @@
|
|||
#!/bin/bash
|
||||
|
||||
##
|
||||
## Tractatus Unified Deployment Script
|
||||
## Replaces: deploy-full-project-SAFE.sh and deploy-frontend.sh
|
||||
##
|
||||
## Features:
|
||||
## - Automatic JS/CSS change detection
|
||||
## - Unified cache versioning (update-cache-version.js)
|
||||
## - Auto-commit cache version changes
|
||||
## - Security exclusions via .rsyncignore
|
||||
## - Frontend-only or full project deployment
|
||||
## - Optional service restart
|
||||
##
|
||||
## Usage:
|
||||
## ./scripts/deploy.sh # Full deployment with auto-detection
|
||||
## ./scripts/deploy.sh --frontend-only # Deploy public/ directory only
|
||||
## ./scripts/deploy.sh --force-cache # Force cache update regardless
|
||||
## ./scripts/deploy.sh --restart # Restart service after deployment
|
||||
## ./scripts/deploy.sh --dry-run # Show what would be deployed
|
||||
##
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
DEPLOY_KEY="/home/theflow/.ssh/tractatus_deploy"
|
||||
REMOTE_USER="ubuntu"
|
||||
REMOTE_HOST="vps-93a693da.vps.ovh.net"
|
||||
REMOTE_PATH="/var/www/tractatus"
|
||||
PROJECT_ROOT="/home/theflow/projects/tractatus"
|
||||
|
||||
# Parse flags
|
||||
FRONTEND_ONLY=false
|
||||
FORCE_CACHE=false
|
||||
RESTART_SERVICE=false
|
||||
DRY_RUN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--frontend-only)
|
||||
FRONTEND_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--force-cache)
|
||||
FORCE_CACHE=true
|
||||
shift
|
||||
;;
|
||||
--restart)
|
||||
RESTART_SERVICE=true
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
echo "Usage: $0 [--frontend-only] [--force-cache] [--restart] [--dry-run]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Header
|
||||
echo ""
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ TRACTATUS UNIFIED DEPLOYMENT ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo -e "${YELLOW}🔍 DRY-RUN MODE - No changes will be made${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$FRONTEND_ONLY" = true ]; then
|
||||
echo -e "${YELLOW}📁 Mode: Frontend-only (public/ directory)${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}📁 Mode: Full project deployment${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 1: Pre-deployment checks
|
||||
echo -e "${GREEN}[1/7] PRE-DEPLOYMENT CHECKS${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if we're in the right directory
|
||||
if [ ! -f "$PROJECT_ROOT/package.json" ]; then
|
||||
echo -e "${RED}✗ ERROR: Not in project root directory${NC}"
|
||||
echo "Expected: $PROJECT_ROOT"
|
||||
exit 1
|
||||
fi
|
||||
echo -e " ✓ Project root verified"
|
||||
|
||||
# Check .rsyncignore exists (for full deployment)
|
||||
if [ "$FRONTEND_ONLY" = false ] && [ ! -f "$PROJECT_ROOT/.rsyncignore" ]; then
|
||||
echo -e "${RED}✗ ERROR: .rsyncignore not found!${NC}"
|
||||
echo "This file is required to prevent sensitive data deployment."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$FRONTEND_ONLY" = false ]; then
|
||||
echo -e " ✓ .rsyncignore found"
|
||||
fi
|
||||
|
||||
# Check local server is running
|
||||
if ! lsof -i :9000 >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW} ⚠ WARNING: Local server not running on port 9000${NC}"
|
||||
echo " It's recommended to test changes locally before deployment."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p " Continue anyway? (yes/NO): " continue_no_server
|
||||
if [ "$continue_no_server" != "yes" ]; then
|
||||
echo "Deployment cancelled. Start local server with: npm start"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo -e " ✓ Local server running on port 9000"
|
||||
fi
|
||||
|
||||
# Check for uncommitted changes (excluding cache version files)
|
||||
UNCOMMITTED=$(git status --porcelain | grep -v "public/.*\.html" | grep -v "public/service-worker.js" | grep -v "public/version.json" || true)
|
||||
if [ ! -z "$UNCOMMITTED" ]; then
|
||||
echo -e "${YELLOW} ⚠ WARNING: Uncommitted changes detected:${NC}"
|
||||
echo "$UNCOMMITTED" | sed 's/^/ /'
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p " Continue with uncommitted changes? (yes/NO): " continue_uncommitted
|
||||
if [ "$continue_uncommitted" != "yes" ]; then
|
||||
echo "Deployment cancelled. Commit changes first."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo -e " ✓ No uncommitted changes (excluding cache version files)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Cache version update
|
||||
echo -e "${GREEN}[2/7] CACHE VERSION UPDATE${NC}"
|
||||
echo ""
|
||||
|
||||
NEEDS_CACHE_UPDATE=false
|
||||
|
||||
# Check if JS/CSS files changed since last commit
|
||||
CHANGED_JS=$(git diff --name-only HEAD~1 2>/dev/null | grep -E "public/.*\.(js|css)$" || true)
|
||||
if [ ! -z "$CHANGED_JS" ]; then
|
||||
NEEDS_CACHE_UPDATE=true
|
||||
echo -e "${YELLOW} ⚠ JavaScript/CSS files changed since last commit:${NC}"
|
||||
echo "$CHANGED_JS" | sed 's/^/ - /'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Force cache update if requested
|
||||
if [ "$FORCE_CACHE" = true ]; then
|
||||
NEEDS_CACHE_UPDATE=true
|
||||
echo -e "${YELLOW} ⚠ Cache update forced via --force-cache flag${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$NEEDS_CACHE_UPDATE" = true ]; then
|
||||
echo " Running cache version update..."
|
||||
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
cd "$PROJECT_ROOT"
|
||||
node scripts/update-cache-version.js
|
||||
|
||||
# Check if cache version files were modified
|
||||
CACHE_CHANGES=$(git status --porcelain | grep -E "(\.html|service-worker\.js|version\.json)" || true)
|
||||
|
||||
if [ ! -z "$CACHE_CHANGES" ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN} ✓ Cache version updated${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW} Auto-committing cache version changes...${NC}"
|
||||
|
||||
git add public/*.html public/**/*.html public/service-worker.js public/version.json 2>/dev/null || true
|
||||
git commit -m "chore: bump cache version for deployment" --no-verify
|
||||
|
||||
echo -e "${GREEN} ✓ Cache version changes committed${NC}"
|
||||
else
|
||||
echo -e "${GREEN} ✓ Cache version already up to date${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${BLUE} [DRY-RUN] Would run: node scripts/update-cache-version.js${NC}"
|
||||
echo -e "${BLUE} [DRY-RUN] Would commit cache version changes${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN} ✓ No cache update needed (no JS/CSS changes)${NC}"
|
||||
echo " Use --force-cache to update anyway"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Git status
|
||||
echo -e "${GREEN}[3/7] GIT STATUS${NC}"
|
||||
echo ""
|
||||
|
||||
git log --oneline -5 | sed 's/^/ /'
|
||||
echo ""
|
||||
echo "Current status:"
|
||||
git status -s | sed 's/^/ /' || echo " (working tree clean)"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 4: Security check (full deployment only)
|
||||
if [ "$FRONTEND_ONLY" = false ]; then
|
||||
echo -e "${GREEN}[4/7] SECURITY CHECK${NC}"
|
||||
echo ""
|
||||
echo "Excluded patterns from .rsyncignore:"
|
||||
head -20 "$PROJECT_ROOT/.rsyncignore" | grep -v "^#" | grep -v "^$" | sed 's/^/ - /' || echo " (none)"
|
||||
echo " ... (see .rsyncignore for full list)"
|
||||
else
|
||||
echo -e "${GREEN}[4/7] SECURITY CHECK${NC}"
|
||||
echo ""
|
||||
echo " Frontend-only mode: deploying public/ directory only"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 5: Deployment confirmation
|
||||
echo -e "${GREEN}[5/7] DEPLOYMENT CONFIRMATION${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$FRONTEND_ONLY" = true ]; then
|
||||
DEPLOY_SOURCE="$PROJECT_ROOT/public/"
|
||||
DEPLOY_DEST="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/public/"
|
||||
echo -e "${YELLOW}Source:${NC} $DEPLOY_SOURCE"
|
||||
echo -e "${YELLOW}Destination:${NC} $DEPLOY_DEST"
|
||||
else
|
||||
DEPLOY_SOURCE="$PROJECT_ROOT/"
|
||||
DEPLOY_DEST="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/"
|
||||
echo -e "${YELLOW}Source:${NC} $DEPLOY_SOURCE"
|
||||
echo -e "${YELLOW}Destination:${NC} $DEPLOY_DEST"
|
||||
echo -e "${YELLOW}Exclusions:${NC} via .rsyncignore"
|
||||
fi
|
||||
|
||||
if [ "$RESTART_SERVICE" = true ]; then
|
||||
echo -e "${YELLOW}Post-deployment:${NC} Restart tractatus service"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p "Continue with deployment? (yes/NO): " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Deployment cancelled."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 6: Deployment
|
||||
echo -e "${GREEN}[6/7] DEPLOYING TO PRODUCTION${NC}"
|
||||
echo ""
|
||||
|
||||
# Build rsync command
|
||||
RSYNC_CMD="rsync -avz --delete -e 'ssh -i $DEPLOY_KEY'"
|
||||
|
||||
if [ "$FRONTEND_ONLY" = false ]; then
|
||||
RSYNC_CMD="$RSYNC_CMD --exclude-from=$PROJECT_ROOT/.rsyncignore"
|
||||
else
|
||||
# Frontend-only exclusions (minimal)
|
||||
RSYNC_CMD="$RSYNC_CMD --exclude=node_modules --exclude=.git"
|
||||
fi
|
||||
|
||||
# Dry-run preview
|
||||
echo "Dry-run preview..."
|
||||
if [ "$FRONTEND_ONLY" = true ]; then
|
||||
rsync -avzn --delete \
|
||||
-e "ssh -i $DEPLOY_KEY" \
|
||||
--exclude=node_modules --exclude=.git \
|
||||
"$DEPLOY_SOURCE" \
|
||||
"$DEPLOY_DEST" \
|
||||
| tail -20
|
||||
else
|
||||
rsync -avzn --delete \
|
||||
-e "ssh -i $DEPLOY_KEY" \
|
||||
--exclude-from="$PROJECT_ROOT/.rsyncignore" \
|
||||
"$DEPLOY_SOURCE" \
|
||||
"$DEPLOY_DEST" \
|
||||
| tail -20
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo -e "${BLUE}[DRY-RUN] Deployment would proceed here${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Dry-run complete. No changes made.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
read -p "Proceed with actual deployment? (yes/NO): " confirm2
|
||||
if [ "$confirm2" != "yes" ]; then
|
||||
echo "Deployment cancelled after dry-run."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Deploying..."
|
||||
|
||||
# Actual deployment
|
||||
if [ "$FRONTEND_ONLY" = true ]; then
|
||||
rsync -avz --delete \
|
||||
-e "ssh -i $DEPLOY_KEY" \
|
||||
--exclude=node_modules --exclude=.git \
|
||||
"$DEPLOY_SOURCE" \
|
||||
"$DEPLOY_DEST"
|
||||
else
|
||||
rsync -avz --delete \
|
||||
-e "ssh -i $DEPLOY_KEY" \
|
||||
--exclude-from="$PROJECT_ROOT/.rsyncignore" \
|
||||
"$DEPLOY_SOURCE" \
|
||||
"$DEPLOY_DEST"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Deployment complete${NC}"
|
||||
|
||||
# Step 7: Post-deployment
|
||||
echo ""
|
||||
echo -e "${GREEN}[7/7] POST-DEPLOYMENT${NC}"
|
||||
echo ""
|
||||
|
||||
# Restart service if requested
|
||||
if [ "$RESTART_SERVICE" = true ]; then
|
||||
echo "Restarting tractatus service..."
|
||||
ssh -i "$DEPLOY_KEY" "${REMOTE_USER}@${REMOTE_HOST}" "sudo systemctl restart tractatus"
|
||||
sleep 2
|
||||
ssh -i "$DEPLOY_KEY" "${REMOTE_USER}@${REMOTE_HOST}" "sudo systemctl status tractatus --no-pager" || true
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Service restarted${NC}"
|
||||
fi
|
||||
|
||||
# Verification commands
|
||||
echo -e "${YELLOW}Verification commands:${NC}"
|
||||
echo ""
|
||||
echo "1. Check sensitive files NOT deployed:"
|
||||
echo " ssh -i $DEPLOY_KEY $REMOTE_USER@$REMOTE_HOST 'ls -la /var/www/tractatus/CLAUDE.md 2>/dev/null || echo \"NOT FOUND (good)\"'"
|
||||
echo ""
|
||||
echo "2. Check service status:"
|
||||
echo " ssh -i $DEPLOY_KEY $REMOTE_USER@$REMOTE_HOST 'sudo systemctl status tractatus'"
|
||||
echo ""
|
||||
echo "3. Test site:"
|
||||
echo " https://agenticgovernance.digital"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ DEPLOYMENT COMPLETE ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
Loading…
Add table
Reference in a new issue