#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" ENV_FILE="${REPO_ROOT}/credentials/.env" PLUGIN_SLUG="${PLUGIN_SLUG:-feca_mailshots_plugin}" SOURCE_DIR="${SOURCE_DIR:-${REPO_ROOT}/${PLUGIN_SLUG}}" DRY_RUN=0 usage() { cat <<'USAGE' Usage: scripts/deploy_remote.sh [--dry-run] [--source /path/to/plugin] Deploys plugin files to DEPLOYMENT_DIRECTORY on the remote host using REMOTE_SSH_* values from credentials/.env. USAGE } while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=1 shift ;; --source) SOURCE_DIR="${2:-}" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 1 ;; esac done if [[ ! -f "${ENV_FILE}" ]]; then echo "Error: credentials file not found: ${ENV_FILE}" >&2 exit 1 fi if [[ ! -d "${SOURCE_DIR}" ]]; then echo "Error: source directory not found: ${SOURCE_DIR}" >&2 exit 1 fi PLUGIN_BASENAME="$(basename "${SOURCE_DIR}")" PLUGIN_MAIN_FILE="${SOURCE_DIR}/${PLUGIN_BASENAME}.php" if [[ ! -f "${PLUGIN_MAIN_FILE}" ]]; then echo "Error: plugin main file not found: ${PLUGIN_MAIN_FILE}" >&2 exit 1 fi COMPOSER_JSON="${REPO_ROOT}/composer.json" COMPOSER_LOCK="${REPO_ROOT}/composer.lock" LOCAL_VENDOR_DIR="${REPO_ROOT}/vendor" if [[ ! -f "${COMPOSER_JSON}" || ! -f "${COMPOSER_LOCK}" ]]; then echo "Error: missing composer.json/composer.lock in ${REPO_ROOT}" >&2 exit 1 fi load_env_value() { local key="$1" local line line="$(grep -E "^${key}=" "${ENV_FILE}" | tail -n 1 || true)" line="${line#*=}" line="${line%\"}" line="${line#\"}" line="${line%\'}" line="${line#\'}" printf '%s' "${line}" } SSH_HOST="$(load_env_value REMOTE_SSH_HOST)" SSH_PORT="$(load_env_value REMOTE_SSH_PORT)" SSH_USER="$(load_env_value REMOTE_SSH_USER)" SSH_KEY_RAW="$(load_env_value REMOTE_SSH_KEY)" REMOTE_DIR_RAW="$(load_env_value DEPLOYMENT_DIRECTORY)" if [[ -z "${SSH_HOST}" || -z "${SSH_USER}" || -z "${SSH_KEY_RAW}" ]]; then echo "Error: missing REMOTE_SSH_* values in ${ENV_FILE}" >&2 exit 1 fi if [[ -z "${REMOTE_DIR_RAW}" ]]; then echo "Error: missing DEPLOYMENT_DIRECTORY in ${ENV_FILE}" >&2 exit 1 fi if [[ -z "${SSH_PORT}" ]]; then SSH_PORT=22 fi if [[ "${SSH_KEY_RAW}" = /* ]]; then SSH_KEY_PATH="${SSH_KEY_RAW}" else SSH_KEY_PATH="${REPO_ROOT}/${SSH_KEY_RAW}" fi if [[ ! -f "${SSH_KEY_PATH}" ]]; then echo "Error: SSH key not found: ${SSH_KEY_PATH}" >&2 exit 1 fi SSH_TARGET="${SSH_USER}@${SSH_HOST}" if [[ "${REMOTE_DIR_RAW}" = /* ]]; then REMOTE_DIR="${REMOTE_DIR_RAW}" else REMOTE_DIR="/home/${SSH_USER}/${REMOTE_DIR_RAW}" fi REMOTE_PLUGIN_VENDOR_DIR="${REMOTE_DIR}/vendor" CURRENT_VERSION="$(awk ' /Version:/ { line = $0 sub(/^.*Version:[[:space:]]*/, "", line) if (match(line, /^[0-9]+\.[0-9]+\.[0-9]+/)) { print substr(line, RSTART, RLENGTH) exit } } ' "${PLUGIN_MAIN_FILE}")" if [[ -z "${CURRENT_VERSION}" ]]; then echo "Error: could not parse Version: x.y.z from ${PLUGIN_MAIN_FILE}" >&2 exit 1 fi IFS='.' read -r VER_MAJOR VER_MINOR VER_PATCH <<< "${CURRENT_VERSION}" NEW_PATCH=$((VER_PATCH + 1)) NEW_VERSION="${VER_MAJOR}.${VER_MINOR}.${NEW_PATCH}" if [[ "${DRY_RUN}" -eq 1 ]]; then echo "Version bump (dry-run): ${CURRENT_VERSION} -> ${NEW_VERSION}" else TMP_FILE="${PLUGIN_MAIN_FILE}.tmp.$$" awk -v new_version="${NEW_VERSION}" ' BEGIN { done = 0 } { if (!done && $0 ~ /^[[:space:]]*\*[[:space:]]*Version:[[:space:]]*[0-9]+\.[0-9]+\.[0-9]+[[:space:]]*$/) { sub(/[0-9]+\.[0-9]+\.[0-9]+/, new_version) done = 1 } print } END { if (!done) { exit 2 } } ' "${PLUGIN_MAIN_FILE}" > "${TMP_FILE}" mv "${TMP_FILE}" "${PLUGIN_MAIN_FILE}" echo "Version bumped: ${CURRENT_VERSION} -> ${NEW_VERSION}" fi echo "Remote host: ${SSH_TARGET}:${SSH_PORT}" echo "Source dir: ${SOURCE_DIR}" echo "Deploy target: ${REMOTE_DIR}" echo "Vendor target: ${REMOTE_PLUGIN_VENDOR_DIR}" if [[ "${DRY_RUN}" -eq 1 ]]; then if [[ ! -f "${LOCAL_VENDOR_DIR}/autoload.php" ]]; then echo "Warning: ${LOCAL_VENDOR_DIR}/autoload.php not found. Run scripts/install_dependencies.sh before real deploy." fi else "${REPO_ROOT}/scripts/install_dependencies.sh" --skip-node if [[ ! -f "${LOCAL_VENDOR_DIR}/autoload.php" ]]; then echo "Error: ${LOCAL_VENDOR_DIR}/autoload.php is missing. Install dependencies first (scripts/install_dependencies.sh)." >&2 exit 1 fi fi ssh -p "${SSH_PORT}" \ -i "${SSH_KEY_PATH}" \ -o BatchMode=yes \ -o StrictHostKeyChecking=accept-new \ "${SSH_TARGET}" \ "mkdir -p '${REMOTE_DIR}' '${REMOTE_PLUGIN_VENDOR_DIR}'" RSYNC_SSH="ssh -p ${SSH_PORT} -i ${SSH_KEY_PATH} -o BatchMode=yes -o StrictHostKeyChecking=accept-new" RSYNC_ARGS=( -avz --delete --exclude '.git/' --exclude '.codex/' --exclude '.DS_Store' ) if [[ "${DRY_RUN}" -eq 1 ]]; then RSYNC_ARGS+=(-n) fi rsync "${RSYNC_ARGS[@]}" \ -e "${RSYNC_SSH}" \ "${SOURCE_DIR}/" \ "${SSH_TARGET}:${REMOTE_DIR}/" VENDOR_RSYNC_ARGS=( -avz --delete ) if [[ "${DRY_RUN}" -eq 1 ]]; then VENDOR_RSYNC_ARGS+=(-n) fi rsync "${VENDOR_RSYNC_ARGS[@]}" \ -e "${RSYNC_SSH}" \ "${LOCAL_VENDOR_DIR}/" \ "${SSH_TARGET}:${REMOTE_PLUGIN_VENDOR_DIR}/" if [[ "${DRY_RUN}" -eq 1 ]]; then echo "Dry-run complete. No remote files were changed." else echo "Deploy complete." fi