experienceolympic-com/scripts/deploy.sh

221 lines
9.3 KiB
Bash
Raw Normal View History

2023-12-05 22:07:08 +00:00
#!/bin/bash
set -euo pipefail
# ~SCRIPT~ deploy.sh
# * Copies a build to the specified target and performs updates.
# * The build should already be prepared elsewhere.
# * Run from project root folder.
# * Will create database dumps (using drush)
# * By default, runs all related drush commands.
#######################################
# Using deploy.sh
#######################################
# ~REQUIRED~
# SSH_TARGET:
# Server to push code to/run drush commands on (host aliases work if configured in $HOME/.ssh/config)
# DEPLOY_ENV:
# A label for the environment, used to name backup files, and select settings file
# Defaults to ${CI_ENVIRONMENT_NAME}, if available, which is automatically set by gitlab-ci
# DEPLOY_ROOT:
# Directory name for composer file, containing vendor+config sub-folders
# ~OPTIONAL~
# DRUPAL_DIR:
# Defaults to web
# BACKUP_DIR:
# Defaults to ~/backups
# BUILD_ROOT:
# Defaults to ${PWD}
# DRUSH_DISABLE:
# Set to "1" to disable ALL drush commands.
# May be needed if an install failed or is not already present.
# REMOTE_DRUSH:
# Defaults to ${DEPLOY_ROOT}/vendor/bin/drush
# UPDATE_SCRIPT:
# The post-deploy update routine, specified from perspective of the project root.
# This should know how to find drush or can use $1 for it (see update.sh).
# Defaults to scripts/update.sh
# UPDATE_DISABLE:
# Set to "1" to disable only update commands (e.g. drush updb)
# Note: system will be left in maintenance mode!
# drush state:set system.maintenance_mode FALSE
# DEBUG:
# Set to "1" to call show-vars.sh
# WEB_EXCLUDES:
# --exclude .well-known --exclude sites/default/files --exclude sites/default/settings.*.php"
#######################################
# Load env vars, if supplied
#######################################
[ $# -eq 1 ] && source "env-${1}"
#######################################
# DEFAULTS
#######################################
_backup_dir="~/backups"
_config_dir="config" # Folder under deploy root with config
_remote_drush="${DEPLOY_ROOT:-}/vendor/bin/drush" # Full remote path to drush used by ssh
_update_script="scripts/update.sh"
_web_dir="web" # Under deploy root, the Drupal public web directory
_web_excludes="--exclude .well-known --exclude sites/default/files --exclude sites/default/settings.*.php"
# Note: `_php_settings_dir` cannot be included here; Default directly in optionals block
#######################################
# Check for required variables:
#######################################
[ -z "${DEPLOY_ENV:-}" ] && DEPLOY_ENV="${CI_ENVIRONMENT_NAME:-}"
[ -z "${DEPLOY_ENV:-}" ] && { echo 'Missing ENV var: CI_ENVIRONMENT_NAME/DEPLOY_ENV'; exit 1; }
[ -z "${DEPLOY_ROOT:-}" ] && { echo "Missing ENV var: DEPLOY_ROOT"; exit 1; }
[ -z "${SSH_TARGET:-}" ] && { echo "Missing ENV var: SSH_TARGET"; exit 1; }
#######################################
# Optional variable setup from defaults:
#######################################
[ -z "${BACKUP_DIR:-}" ] && BACKUP_DIR="${_backup_dir}"
[ -z "${BUILD_ROOT:-}" ] && BUILD_ROOT="./"
[ -z "${CONFIG_DIR:-}" ] && CONFIG_DIR="${_config_dir}" # NO trailing slash!
[ -z "${DRUPAL_DIR:-}" ] && DRUPAL_DIR="${_web_dir}" # Name of Drupal public sub-folder
[ -z "${REMOTE_DRUSH:-}" ] && REMOTE_DRUSH="${_remote_drush}" # Full path to remote drush
[ -z "${UPDATE_SCRIPT:-}" ] && UPDATE_SCRIPT="${_update_script}"
[ -z "${WEB_EXCLUDES:-}" ] && WEB_EXCLUDES="${_web_excludes}"
[ -z "${DRUSH_DISABLE:-}" ] && DRUSH_DISABLE="0"
[ -z "${UPDATE_DISABLE:-}" ] && UPDATE_DISABLE="0"
[ -z "${PHP_SETTINGS_DIR:-}" ] && PHP_SETTINGS_DIR="${DRUPAL_DIR}/sites/default"
# Be sure to not overwrite any server-only .user.ini
if [ ! -f ${BUILD_ROOT}/${DRUPAL_DIR}/.user.ini ]; then
WEB_EXCLUDES="--exclude .user.ini ${WEB_EXCLUDES}"
fi
if [ "${DEBUG:-0}" == "1" ]; then
#./scripts/show-vars.sh # per-env
echo "[DEBUG] IN-USE SETTINGS:"
echo "SSH_TARGET=$SSH_TARGET"
echo "DEPLOY_ENV=${DEPLOY_ENV}"
echo "DEPLOY_ROOT=${DEPLOY_ROOT}"
echo "BUILD_ROOT=${BUILD_ROOT}"
echo "CONFIG_DIR=${CONFIG_DIR}"
echo "DRUPAL_DIR=${DRUPAL_DIR}"
echo "REMOTE_DRUSH=${REMOTE_DRUSH}"
echo "BACKUP_DIR=${BACKUP_DIR}"
echo "DRUSH_DISABLE=${DRUSH_DISABLE}"
echo "UPDATE_SCRIPT=${UPDATE_SCRIPT}"
echo "UPDATE_DISABLE=${UPDATE_DISABLE}"
echo "WEB_EXCLUDES=${WEB_EXCLUDES}"
exit 0
fi
#######################################
# Functions
#######################################
function drush_enabled() {
if [ "${DRUSH_DISABLE}" == "1" ]
then
return 1 #1 is false (so, disabled)
else
return 0 #0 is true (enabled) because...bash
fi
}
#######################################
# Check drush/site status
#######################################
echo "Starting deployment to ${DEPLOY_ENV}"
drush_enabled || echo "Note: Drush commands are disabled. Commands marked [D] will not execute."
echo "Verify connectivity (print user & working directory)"
ssh ${SSH_TARGET} "whoami; pwd"
echo "[D] Verify drush functionality (cr, status bootstrap)"
drush_enabled && ssh ${SSH_TARGET} "${REMOTE_DRUSH} cr"
# Expected good result: "Drupal bootstrap : Successful", otherwise empty (but drush returns 0)
ssh_cmd="${REMOTE_DRUSH} status --fields=bootstrap"
drush_enabled && { [[ $(ssh ${SSH_TARGET} "${ssh_cmd}") = *Successful* ]] || { echo ">> Drush failed bootstrap!"; exit 1; } ; }
#######################################
# Enable maintenance mode
#######################################
# echo "[D] Set maintenance mode ON"
# drush_enabled && ssh ${SSH_TARGET} "${REMOTE_DRUSH} state:set system.maintenance_mode TRUE"
#######################################
# Perform a database backup
#######################################
backup_file="${DEPLOY_ENV}_$(date +%Y%m%dT%H%M%S).sql.gz"
ssh_cmd="[ ! -d ${BACKUP_DIR} ] && mkdir -p ${BACKUP_DIR} || true"
echo "Using backup dir '${BACKUP_DIR}' (created as needed)"
ssh "${SSH_TARGET}" "$ssh_cmd"
ssh_cmd="${REMOTE_DRUSH} sql-dump | gzip > ${BACKUP_DIR}/$backup_file"
echo "[D] Creating backup file ${backup_file}"
drush_enabled && ssh "${SSH_TARGET}" "${ssh_cmd}"
#######################################
# DEPLOY CODE:
#######################################
# Check for sub-dirs...
ssh_cmd="[ ! -d ${DEPLOY_ROOT}/${CONFIG_DIR} ] && mkdir -p ${DEPLOY_ROOT}/${CONFIG_DIR} || true"
echo "Ensure '${CONFIG_DIR}' exists"
ssh "${SSH_TARGET}" "$ssh_cmd"
ssh_cmd="[ ! -d ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default ] && mkdir -p ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default || true"
echo "Ensure '${DRUPAL_DIR}/sites/default exists'"
ssh "${SSH_TARGET}" "$ssh_cmd"
# Disable write protect...
echo "Disabling write protection on settings folder and files."
ssh_cmd="chmod ug+w ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default/"
ssh ${SSH_TARGET} ${ssh_cmd}
ssh_cmd="find ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default/ -maxdepth 1 -type f -name 'settings*php' -exec chmod ug+w '{}' \;"
ssh ${SSH_TARGET} ${ssh_cmd}
# Rsync's
printf "Pushing latest codebase: config…"
rsync -rz --delete ${BUILD_ROOT}/${CONFIG_DIR}/ ${SSH_TARGET}:${DEPLOY_ROOT}/config
printf "\b ✓, ${DRUPAL_DIR}"
rsync -rz --delete ${WEB_EXCLUDES} ${BUILD_ROOT}/${DRUPAL_DIR}/ ${SSH_TARGET}:${DEPLOY_ROOT}/${DRUPAL_DIR}
printf "\b ✓, vendor…"
rsync -rz --delete --links ${BUILD_ROOT}/vendor ${SSH_TARGET}:${DEPLOY_ROOT}/
printf "\b ✓, scripts…"
[ -d ./scripts ] && rsync -rz --delete ${BUILD_ROOT}/scripts ${SSH_TARGET}:${DEPLOY_ROOT}/
printf "\b ✓, composer…"
scp -q composer.json ${SSH_TARGET}:${DEPLOY_ROOT}/composer.json
printf "\b ✓, settings…"
scp -q ${BUILD_ROOT}/${PHP_SETTINGS_DIR}/settings.${DEPLOY_ENV}.php ${SSH_TARGET}:${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default/settings.local.php
printf "\b ✓\nFile sync complete!\n"
# Re-protect settings
echo "Enabling write protection on settings folder and files."
ssh_cmd="[ -d ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default ] && chmod ug-w ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default/"
ssh ${SSH_TARGET} ${ssh_cmd}
ssh_cmd="find ${DEPLOY_ROOT}/${DRUPAL_DIR}/sites/default/ -maxdepth 1 -type f -name 'settings*php' -exec chmod ug-w '{}' \;"
ssh ${SSH_TARGET} ${ssh_cmd}
#######################################
# [P|re]load database
#######################################
if [ ! -z "${PRELOAD_DB_FILE:-}" -a "${DEPLOY_ENV}" != "live" -a "${DEPLOY_ENV}" != "prod" ]; then
ssh_cmd="${REMOTE_DRUSH} sql-drop -y"
echo "[D] Dropping existing database"
drush_enabled && ssh "${SSH_TARGET}" "${ssh_cmd}"
if drush_enabled; then
ssh_cmd="${REMOTE_DRUSH} sqlc < ${PRELOAD_DB_FILE}"
echo "[D] Loading database from ${PRELOAD_DB_FILE}"
ssh "${SSH_TARGET}" "${ssh_cmd}"
else
echo "Fallback: Load database file using mysql (requires .my.cnf)"
ssh_cmd="mysql < ${PRELOAD_DB_FILE}"
echo "Loading database from ${PRELOAD_DB_FILE}"
ssh "${SSH_TARGET}" "${ssh_cmd}"
fi
fi
#######################################
# RUN DRUPAL TASKS (via UPDATE_SCRIPT):
#######################################
if drush_enabled && [ "${UPDATE_DISABLE}" == "0" -a -f "${BUILD_ROOT}/${UPDATE_SCRIPT}" ]; then
echo ">> Calling ${UPDATE_SCRIPT} to perform updates..."
ssh ${SSH_TARGET} "${DEPLOY_ROOT}/${UPDATE_SCRIPT} ${REMOTE_DRUSH}"
# echo ">> [D] Set maintenance mode OFF"
# ssh ${SSH_TARGET} ${REMOTE_DRUSH} state:set system.maintenance_mode FALSE
else
echo ">> [D] Skipping follow-up drush (cr, updb, cim, etc)."
# echo ">> Maintenence mode on. To disable: ${drush} state:set system.maintenance_mode FALSE"
fi
echo "Deployment to ${DEPLOY_ENV} completed!"