Serg
Posted on April 18, 2024
Deploying web applications can be a repetitive and error-prone task. In this blog post, we'll introduce a Bash script designed to automate the deployment process for a web application hosted on a server. Let's dive into the details of the script and how it streamlines the deployment workflow.
Setup
Before diving into the deployment process, let's set up some initial configurations:
DOMAIN=example.com
PROJECT_REPO="your_github_repo_name"
AMOUNT_KEEP_RELEASES=5
RELEASE_NAME=$(date +%s--%Y_%m_%d--%H_%M_%S)
RELEASES_DIRECTORY=~/$DOMAIN/releases
DEPLOYMENT_DIRECTORY=$RELEASES_DIRECTORY/$RELEASE_NAME
These variables define crucial aspects such as the domain name, project repository, and the number of releases to keep.
Deployment Process
Cloning Repository and Setup
The script starts by creating a unique release directory and clones the project repository into it:
cd /home/forge/$DOMAIN
mkdir -p "$RELEASES_DIRECTORY" && cd "$RELEASES_DIRECTORY"
git clone "$PROJECT_REPO" "$RELEASE_NAME"
cd "$RELEASE_NAME"
git checkout "$FORGE_SITE_BRANCH"
git fetch origin "$FORGE_SITE_BRANCH"
git reset --hard FETCH_HEAD
Environment Setup
Next, the script copies the .env
file from the project directory:
printf '\nℹ️ Copy ./.env file\n'
ENV_FILE=~/"$DOMAIN"/.env
if [ -f "$ENV_FILE" ]; then
cp $ENV_FILE ./.env
else
printf '\nError: .env file is missing at %s.' "$ENV_FILE" && exit 1
fi
Running Laravel Commands
$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader
( flock -w 10 9 || exit 1
echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
if [ -f artisan ]; then
$FORGE_PHP artisan migrate --force
fi
Dependency Installation and Build
The script installs NPM dependencies and generates necessary files:
printf '\nℹ️ Installing NPM dependencies based on \"./package-lock.json\"\n'
npm install
printf '\nℹ️ Generating JS App files\n'
npm run build
Linking Deployment Directory
The script links the deployment directory to the current directory:
printf '\nℹ️ !!! Link Deployment Directory !!!\n'
echo "$RELEASE_NAME" >> $RELEASES_DIRECTORY/.successes
if [ -d ~/$DOMAIN/current ] && [ ! -L ~/$DOMAIN/current ]; then
rm -rf ~/$DOMAIN/current
fi
ln -s -n -f -T "$DEPLOYMENT_DIRECTORY/public" ~/$DOMAIN/current
Clean Up
Lastly, the script performs clean-up tasks:
printf '\nℹ️ Delete failed releases:\n'
# Code for deleting failed releases
printf '\nℹ️ Delete old successful releases:\n'
# Code for deleting old successful releases
printf '\nℹ️ Status - stored releases:\n'
# Code for displaying stored releases
printf '\n✅ Deployment DONE: %s\n' "$DEPLOYMENT_DIRECTORY"
Full Script
# SETUP #
DOMAIN=yourdomain.com
PROJECT_REPO="git@github.com:your-team/repo.git"
AMOUNT_KEEP_RELEASES=5
RELEASE_NAME=$(date +%s--%Y_%m_%d--%H_%M_%S)
RELEASES_DIRECTORY=~/$DOMAIN/releases
DEPLOYMENT_DIRECTORY=$RELEASES_DIRECTORY/$RELEASE_NAME
# stop script on error signal (-e) and undefined variables (-u)
set -eu
printf '\nℹ️ Starting deployment %s\n' "$RELEASE_NAME"
cd /home/forge/$DOMAIN
mkdir -p "$RELEASES_DIRECTORY" && cd "$RELEASES_DIRECTORY"
printf '\nℹ️ Clone GIT project from %s and checkout branch %s\n' "$PROJECT_REPO" "$FORGE_SITE_BRANCH"
git clone "$PROJECT_REPO" "$RELEASE_NAME"
cd "$RELEASE_NAME"
git checkout "$FORGE_SITE_BRANCH"
git fetch origin "$FORGE_SITE_BRANCH"
git reset --hard FETCH_HEAD
printf '\nℹ️ Copy ./.env file\n'
ENV_FILE=~/"$DOMAIN"/.env
if [ -f "$ENV_FILE" ]; then
cp $ENV_FILE ./.env
else
printf '\nError: .env file is missing at %s.' "$ENV_FILE" && exit 1
fi
$FORGE_COMPOSER install --no-dev --no-interaction --prefer-dist --optimize-autoloader
( flock -w 10 9 || exit 1
echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
if [ -f artisan ]; then
$FORGE_PHP artisan migrate --force
fi
printf '\nℹ️ Installing NPM dependencies based on \"./package-lock.json\"\n'
npm install
printf '\nℹ️ Generating JS App files\n'
npm run build
printf '\nℹ️ !!! Link Deployment Directory !!!\n'
echo "$RELEASE_NAME" >> $RELEASES_DIRECTORY/.successes
if [ -d ~/$DOMAIN/current ] && [ ! -L ~/$DOMAIN/current ]; then
rm -rf ~/$DOMAIN/current
fi
ln -s -n -f -T "$DEPLOYMENT_DIRECTORY/public" ~/$DOMAIN/current
# Clean Up
cd $RELEASES_DIRECTORY
printf '\nℹ️ Delete failed releases:\n'
if grep -qvf .successes <(ls -1)
then
grep -vf .successes <(ls -1)
grep -vf .successes <(ls -1) | xargs rm -rf
else
echo "No failed releases found."
fi
printf '\nℹ️ Delete old successful releases:\n'
AMOUNT_KEEP_RELEASES=$((AMOUNT_KEEP_RELEASES-1))
LINES_STORED_RELEASES_TO_DELETE=$(find . -maxdepth 1 -mindepth 1 -type d ! -name "$RELEASE_NAME" -printf '%T@\t%f\n' | head -n -"$AMOUNT_KEEP_RELEASES" | wc -l)
if [ "$LINES_STORED_RELEASES_TO_DELETE" != 0 ]; then
find . -maxdepth 1 -mindepth 1 -type d ! -name "$RELEASE_NAME" -printf '%T@\t%f\n' | sort -t $'\t' -g | head -n -"$AMOUNT_KEEP_RELEASES" | cut -d $'\t' -f 2-
find . -maxdepth 1 -mindepth 1 -type d ! -name "$RELEASE_NAME" -printf '%T@\t%f\n' | sort -t $'\t' -g | head -n -"$AMOUNT_KEEP_RELEASES" | cut -d $'\t' -f 2- | xargs -I {} sed -i -e '/{}/d' .successes
find . -maxdepth 1 -mindepth 1 -type d ! -name "$RELEASE_NAME" -printf '%T@\t%f\n' | sort -t $'\t' -g | head -n -"$AMOUNT_KEEP_RELEASES" | cut -d $'\t' -f 2- | xargs rm -rf
else
AMOUNT_KEEP_RELEASES=$((AMOUNT_KEEP_RELEASES+1))
LINES_STORED_RELEASES_TOTAL=$(find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%f\n' | wc -l)
printf 'There are only %s successfully stored releases, which is less than or equal to your\ndefined %s releases to keep, so none of them got deleted.' "$LINES_STORED_RELEASES_TOTAL" "$AMOUNT_KEEP_RELEASES"
fi
printf '\nℹ️ Status - stored releases:\n'
find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%f\n' | sort -nr | cut -f 2-
printf '\n✅ Deployment DONE: %s\n' "$DEPLOYMENT_DIRECTORY"
Conclusion
Automation is key to improving deployment efficiency and reducing errors. By using this Bash script, you can streamline the deployment process for your web applications, saving time and ensuring consistency.
Posted on April 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.