#!/usr/bin/env bash set -euo pipefail # Deploy frontend to S3 and invalidate CloudFront cache. usage() { cat <<'EOF' Usage: ./deploy.sh --total-bundles N [OPTIONS] Required: --total-bundles N Number of bundle files (from bundle_gen output) Optional: --site-bucket NAME S3 bucket (default: everytab-site) --distribution-id ID CloudFront distribution ID (default: auto-detect) --skip-invalidation Don't invalidate CloudFront cache --help Show this help message Example: ./deploy.sh --total-bundles 779 ./deploy.sh --total-bundles 779 --distribution-id E14S2BLD6PG2XZ EOF exit 0 } TOTAL_BUNDLES="" SITE_BUCKET="everytab-site" DIST_ID="" SKIP_INVALIDATION=false if [ $# -eq 0 ]; then usage; fi while [ $# -gt 0 ]; do case "$1" in --help) usage ;; --total-bundles) TOTAL_BUNDLES="$2"; shift 2 ;; --site-bucket) SITE_BUCKET="$2"; shift 2 ;; --distribution-id) DIST_ID="$2"; shift 2 ;; --skip-invalidation) SKIP_INVALIDATION=true; shift ;; *) echo "Unknown option: $1"; usage ;; esac done if [ -z "$TOTAL_BUNDLES" ]; then echo "ERROR: --total-bundles is required" exit 1 fi # Find frontend directory (relative to this script) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" FRONTEND_DIR="$SCRIPT_DIR/../../frontend" if [ ! -f "$FRONTEND_DIR/index.html" ]; then echo "ERROR: frontend/index.html not found at $FRONTEND_DIR" exit 1 fi echo "=== Frontend Deploy ===" echo "Total bundles: $TOTAL_BUNDLES" echo "S3 bucket: $SITE_BUCKET" echo "" # Build into temp directory TMPDIR=$(mktemp -d) cp "$FRONTEND_DIR/index.html" "$TMPDIR/index.html" cp "$FRONTEND_DIR/site.js" "$TMPDIR/site.js" cp "$FRONTEND_DIR/bot.html" "$TMPDIR/bot.html" cp "$FRONTEND_DIR/about.html" "$TMPDIR/about.html" # Inject TOTAL_BUNDLES sed -i "s/const TOTAL_BUNDLES = [0-9]*/const TOTAL_BUNDLES = ${TOTAL_BUNDLES}/" "$TMPDIR/index.html" echo "Injected TOTAL_BUNDLES = $TOTAL_BUNDLES" # Minify JS (strip comments + whitespace, keep variable names) if command -v esbuild &>/dev/null; then esbuild "$TMPDIR/site.js" --minify --outfile="$TMPDIR/site.js" --allow-overwrite echo "Minified site.js" else echo "Warning: esbuild not found, deploying unminified JS" fi # Upload echo "Uploading to s3://$SITE_BUCKET/..." aws s3 cp "$TMPDIR/index.html" "s3://$SITE_BUCKET/" --content-type "text/html" aws s3 cp "$TMPDIR/site.js" "s3://$SITE_BUCKET/" --content-type "application/javascript" aws s3 cp "$TMPDIR/bot.html" "s3://$SITE_BUCKET/" --content-type "text/html" aws s3 cp "$TMPDIR/about.html" "s3://$SITE_BUCKET/" --content-type "text/html" echo "Uploaded 4 files" rm -rf "$TMPDIR" # Clean up stale bundles from previous runs echo "Cleaning stale bundles above $TOTAL_BUNDLES..." STALE=$(aws s3api list-objects-v2 --bucket "$SITE_BUCKET" --prefix "tabs/" --query "Contents[].Key" --output text \ | tr '\t' '\n' \ | while read -r key; do num=$(echo "$key" | grep -oP '\d+' || true) if [ -n "$num" ] && [ "$((10#$num))" -ge "$TOTAL_BUNDLES" ]; then echo "$key" fi done) if [ -n "$STALE" ]; then STALE_COUNT=$(echo "$STALE" | wc -l) echo "Deleting $STALE_COUNT stale bundles..." echo "$STALE" | while read -r key; do aws s3 rm "s3://$SITE_BUCKET/$key" --quiet done echo "Deleted $STALE_COUNT stale bundles" else echo "No stale bundles found" fi # Invalidate CloudFront if ! $SKIP_INVALIDATION; then if [ -z "$DIST_ID" ]; then DIST_ID=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[?contains(@,'everytab')]].Id" --output text 2>/dev/null || true) fi if [ -n "$DIST_ID" ]; then echo "Invalidating CloudFront $DIST_ID..." aws cloudfront create-invalidation --distribution-id "$DIST_ID" --paths "/index.html" "/site.js" "/bot.html" "/about.html" > /dev/null echo "Invalidation submitted" else echo "Warning: no CloudFront distribution found, skipping invalidation" fi fi echo "" echo "=== Deploy Complete ==="