#!/usr/bin/env bash # # Deploy static files using release directories and an atomic symlink switch. # # This pattern reduces partial deployments because files are copied to a new # release directory first. The live path changes only when the copy succeeds. # # Usage: # bash deploy_static_site.sh --source ./dist --releases /var/www/releases --current /var/www/current # bash deploy_static_site.sh --source ./dist --releases /var/www/releases --current /var/www/current --keep 10 # # Options: # --source DIR Directory containing built static files. # --releases DIR Directory that stores timestamped releases. # --current PATH Symlink path used by the web server. # --keep N Number of releases to keep. Default: 5. # --help Show this help message. set -Eeuo pipefail source_dir="" releases_dir="" current_link="" keep_releases="5" usage() { sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//' } log() { printf '[%s] %s\n' "$(date -Is)" "$*" } while [[ "$#" -gt 0 ]]; do case "$1" in --source) source_dir="${2:-}" shift 2 ;; --releases) releases_dir="${2:-}" shift 2 ;; --current) current_link="${2:-}" shift 2 ;; --keep) keep_releases="${2:-}" [[ "$keep_releases" =~ ^[0-9]+$ ]] || { printf 'ERROR: --keep must be an integer.\n' >&2; exit 1; } shift 2 ;; --help|-h) usage exit 0 ;; *) printf 'ERROR: unknown option: %s\n' "$1" >&2 usage >&2 exit 1 ;; esac done [[ -d "$source_dir" ]] || { printf 'ERROR: source directory does not exist: %s\n' "$source_dir" >&2; exit 1; } [[ -n "$releases_dir" ]] || { printf 'ERROR: --releases is required.\n' >&2; exit 1; } [[ -n "$current_link" ]] || { printf 'ERROR: --current is required.\n' >&2; exit 1; } mkdir -p "$releases_dir" release_id="$(date +%Y%m%d-%H%M%S)" release_path="${releases_dir}/${release_id}" log "Creating release directory: ${release_path}" mkdir -p "$release_path" log "Copying files from ${source_dir}" if command -v rsync >/dev/null 2>&1; then rsync -a --delete "${source_dir}/" "${release_path}/" else cp -a "${source_dir}/." "$release_path/" fi log "Switching current symlink to ${release_path}" ln -sfn "$release_path" "$current_link" log "Removing old releases, keeping the newest ${keep_releases}." find "$releases_dir" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' \ | sort -nr \ | awk -v keep="$keep_releases" 'NR > keep {print $2}' \ | xargs -r rm -rf log "Deployment completed successfully."