Created
October 31, 2023 11:16
-
-
Save chapterjason/4fffc97203fd0913e2918c13f714f719 to your computer and use it in GitHub Desktop.
Zero Deployment Time Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -euo pipefail | |
# Base | |
USER_DIRECTORY="$(cd ~ && pwd)" | |
ROOT_DIRECTORY="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" | |
LOCK_FILE="${ROOT_DIRECTORY}/.deploy.lock" | |
NOW=$(date -u +"%Y%m%d%H%M%S") | |
LOG_FILE="${ROOT_DIRECTORY}/.deploy.${NOW}.log" | |
# Folder Configuration | |
SHARED_DIRECTORY="${ROOT_DIRECTORY}/shared" | |
DEPLOYMENT_DIRECTORY="${ROOT_DIRECTORY}/deployment" | |
RELEASE_DIRECTORY="${ROOT_DIRECTORY}/release" | |
CURRENT_RELEASE_DIRECTORY="${RELEASE_DIRECTORY}/${NOW}" | |
CURRENT_DIRECTORY="${ROOT_DIRECTORY}/current" | |
# Release Configuration | |
SHARED_FILES=".env.local.php" | |
SHARED_DIRECTORIES="var/log | |
etc/daemons" | |
WRITEABLE_DIRECTORIES="var/cache | |
var/log" | |
EXECUTABLE_FILES="bin/console" | |
KEEP_RELEASES=5 | |
# Variables | |
PHP_BIN="${USER_DIRECTORY}/.phpenv/shims/php" | |
COMPOSER_BIN="${USER_DIRECTORY}/.phpenv/shims/composer" | |
CONSOLE_BIN="${CURRENT_RELEASE_DIRECTORY}/bin/console" | |
function after_release() { | |
if [ -f "${LOG_FILE}" ] && [ -d "${CURRENT_RELEASE_DIRECTORY}" ]; then | |
mv "${LOG_FILE}" "${CURRENT_RELEASE_DIRECTORY}" | |
fi | |
} | |
function __error_handler() { | |
EXIT_CODE=$? | |
if [ "${EXIT_CODE}" -ne 0 ]; then | |
echo "ERROR: Deployment failed. See log file '${LOG_FILE}' for details." | |
fi | |
if [ -f "${LOCK_FILE}" ]; then | |
rm "${LOCK_FILE}" | |
fi | |
if [ -d "${CURRENT_RELEASE_DIRECTORY}" ]; then | |
rm -rf "${CURRENT_RELEASE_DIRECTORY}" | |
fi | |
exit "${EXIT_CODE}" | |
} | |
function log() { | |
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") - $1" | tee -a "${LOG_FILE}" | |
} | |
function exec_console() { | |
"$PHP_BIN" "${CONSOLE_BIN}" "$@" | |
} | |
# register trap on error | |
trap __error_handler ERR INT TERM EXIT | |
log "Starting deployment" | |
log "PHP: ${PHP_BIN}" | |
log "Composer: ${COMPOSER_BIN}" | |
log "Console: ${CONSOLE_BIN}" | |
# lock deployment | |
if [ -f "${LOCK_FILE}" ]; then | |
echo "ERROR: Deployment is locked. Please remove the lock file '${LOCK_FILE}' to continue." | |
exit 1 | |
fi | |
touch "${LOCK_FILE}" | |
# create release directory | |
if [ -d "${CURRENT_RELEASE_DIRECTORY}" ]; then | |
log "ERROR: Release directory '${CURRENT_RELEASE_DIRECTORY}' already exists." | |
exit 1 | |
fi | |
if [ ! -d "${RELEASE_DIRECTORY}" ]; then | |
log "Creating release directory '${RELEASE_DIRECTORY}'" | |
mkdir -p "${RELEASE_DIRECTORY}" | |
fi | |
log "Creating release directory '${CURRENT_RELEASE_DIRECTORY}'" | |
mv "${DEPLOYMENT_DIRECTORY}" "${CURRENT_RELEASE_DIRECTORY}" | |
mkdir "${DEPLOYMENT_DIRECTORY}" | |
# add symlinks of shared files/dirs | |
for FILE in ${SHARED_FILES}; do | |
SOURCE_FILE="${SHARED_DIRECTORY}/${FILE}" | |
TARGET_FILE="${CURRENT_RELEASE_DIRECTORY}/${FILE}" | |
SOURCE_DIRECTORY="$(dirname "${SOURCE_FILE}")" | |
TARGET_DIRECTORY="$(dirname "${TARGET_FILE}")" | |
if [ ! -f "${SOURCE_FILE}" ]; then | |
log "WARNING: Shared file '${SOURCE_FILE}' does not exist. Creating empty file." | |
mkdir -p "${SOURCE_DIRECTORY}" | |
touch "${SOURCE_FILE}" | |
fi | |
if [ -f "${TARGET_FILE}" ]; then | |
log "WARNING: Removing existing file '${TARGET_FILE}'" | |
rm "${TARGET_FILE}" | |
fi | |
if [ ! -d "${TARGET_DIRECTORY}" ]; then | |
log "WARNING: Target directory '${TARGET_DIRECTORY}' does not exist. Creating empty directory." | |
mkdir -p "${TARGET_DIRECTORY}" | |
fi | |
log "Symlinking file '${SOURCE_FILE}' to '${TARGET_FILE}'" | |
ln -s "${SOURCE_FILE}" "${TARGET_FILE}" | |
done | |
for DIRECTORY in ${SHARED_DIRECTORIES}; do | |
SOURCE_DIRECTORY="${SHARED_DIRECTORY}/${DIRECTORY}" | |
TARGET_DIRECTORY="${CURRENT_RELEASE_DIRECTORY}/${DIRECTORY}" | |
TARGET_PARENT_DIRECTORY="$(dirname "${TARGET_DIRECTORY}")" | |
if [ ! -d "${SOURCE_DIRECTORY}" ]; then | |
log "WARNING: Shared directory '${SOURCE_DIRECTORY}' does not exist. Creating empty directory." | |
mkdir -p "${SOURCE_DIRECTORY}" | |
fi | |
if [ -d "${TARGET_DIRECTORY}" ]; then | |
log "WARNING: Removing existing directory '${TARGET_DIRECTORY}'" | |
rm -rf "${TARGET_DIRECTORY}" | |
fi | |
if [ ! -d "${TARGET_PARENT_DIRECTORY}" ]; then | |
log "WARNING: Target parent directory '${TARGET_PARENT_DIRECTORY}' does not exist. Creating empty directory." | |
mkdir -p "${TARGET_PARENT_DIRECTORY}" | |
fi | |
log "Symlinking directory '${SOURCE_DIRECTORY}' to '${TARGET_DIRECTORY}'" | |
ln -s "${SOURCE_DIRECTORY}" "${TARGET_DIRECTORY}" | |
done | |
# make dirs writeable | |
for DIRECTORY in ${WRITEABLE_DIRECTORIES}; do | |
TARGET_DIRECTORY="${CURRENT_RELEASE_DIRECTORY}/${DIRECTORY}" | |
if [ ! -d "${TARGET_DIRECTORY}" ]; then | |
log "WARNING: Writeable directory '${TARGET_DIRECTORY}' does not exist. Creating empty directory." | |
mkdir -p "${TARGET_DIRECTORY}" | |
fi | |
log "Making directory '${TARGET_DIRECTORY}' writeable" | |
chmod -R 777 "${TARGET_DIRECTORY}" | |
done | |
# make files executable | |
for FILE in ${EXECUTABLE_FILES}; do | |
TARGET_FILE="${CURRENT_RELEASE_DIRECTORY}/${FILE}" | |
if [ ! -f "${TARGET_FILE}" ]; then | |
log "WARNING: Executable file '${TARGET_FILE}' does not exist." | |
exit 1 | |
fi | |
log "Making file '${TARGET_FILE}' executable" | |
chmod +x "${TARGET_FILE}" | |
done | |
log "Installing vendors" | |
"${COMPOSER_BIN}" install --no-interaction --no-dev --optimize-autoloader | |
log "Clearing cache" | |
exec_console cache:clear --no-interaction --no-warmup --env=prod | |
log "Warming up cache" | |
exec_console cache:warmup --no-interaction --env=prod | |
log "Installing assets" | |
exec_console assets:install --no-interaction --env=prod | |
log "Migrating database" | |
exec_console doctrine:migrations:migrate --allow-no-migration --all-or-nothing --no-interaction --env=prod | |
log "Symlinking release '${CURRENT_RELEASE_DIRECTORY}' to '${CURRENT_DIRECTORY}'" | |
if [ -L "${CURRENT_DIRECTORY}" ] || [ -d "${CURRENT_DIRECTORY}" ]; then | |
log "Removing existing symlink '${CURRENT_DIRECTORY}'" | |
rm "${CURRENT_DIRECTORY}" | |
fi | |
ln -s "${CURRENT_RELEASE_DIRECTORY}" "${CURRENT_DIRECTORY}" | |
log "Reloading workers" | |
exec_console worker:reload --env=prod --no-interaction | |
log "Restarting workers" | |
exec_console messenger:stop-workers --env=prod --no-interaction | |
# unlock deployment | |
log "Removing lock file '${LOCK_FILE}'" | |
rm "${LOCK_FILE}" | |
# cleanup (remove old release (keep settings)) | |
RELEASES="$(ls -dt "${RELEASE_DIRECTORY}/"*)" | |
RELEASES="$(echo "${RELEASES}" | tail -n +$((KEEP_RELEASES + 1)))" | |
for RELEASE in ${RELEASES}; do | |
log "Removing old release '${RELEASE}'" | |
rm -rf "${RELEASE}" | |
done | |
log "Deployment finished" | |
after_release |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just realized I should add a tracking if the current folder is already changed before removing current release in the trap.
I don't think that anything after that will cause an error, but who knows?