Series: Drupal Docker
tl;dr: Key scripts for a Drupal Docker setup includes connecting to and building the database, setting up key files with variables, and running Apache.
This post is one of several in a series detailing my development environment and CI/CD deployment pipeline for a Drupal site. The code for the developer environment can be seen on my GitLab. Other posts in the series can be seen by checking the Drupal Docker series links in the sidebar. I provided an overview in the first post of the series.
In the last post, I created the web Dockerfile. At the end of that file it specifies what script to run when it starts up. This is simply called start.sh in my setup, and it's that script that I will now pick up from for this post.
Start.sh
The start script itself is relatively simple, but it is calling on a few other scripts. The idea here is that it will wait for the database container to be available. Once it is, it will check if the database is already installed. If it's not, the database can be rebuilt. Finally, prepare the phpunit.xml file and then start up Apache.
#!/bin/bash
# Wait for the database to come up
wait_for_db() {
local retries=10
local wait_time=5
local attempt=1
source /opt/drupal/scripts/settings-file.sh
while [ $attempt -le $retries ]; do
if drush sqlq "SELECT 1" > /dev/null 2>&1; then
return 0
else
echo "Database is not ready yet. Attempt $attempt/$retries. Waiting for $wait_time seconds..."
sleep $wait_time
attempt=$((attempt + 1))
wait_time=$((wait_time * 2))
fi
done
return 1
}
if wait_for_db; then
if $(drush sqlq "SHOW TABLES LIKE 'users'" | grep -q users); then
echo Drupal has already been initialized.
else
source /opt/drupal/scripts/rebuild-db.sh
fi
source /opt/drupal/scripts/phpunit-file.sh
apache2-foreground
else
echo "Database never came up."
exit 1
fi
Now, let's get into some of those other files that are referenced by the start file, as well as potentially referenced in other workflows as well.
Settings-file.sh
The settings file script will fill in the Drupal settings file, using variables that are passed through from the GitLab variables (most of them) or from the GitLab build job as an argument (the timestamp that becomes the deployment ID). This is done using the envsubst command to generate a new file with the correct name in the correct location by replacing those variables in a template file. This is necessary to allow that each environment has different variables like database connection information.
#!/bin/bash
# Update template from variables.
if [ ! -f /opt/drupal/web/sites/default/settings.php ]; then
# First 10 digits are just the date.
echo "export DEPLOYMENT_ID=${TIMESTAMP:0:10}" >> ~/.bashrc
source ~/.bashrc
# Create the new file from the template with variable replacement.
envsubst '${DEPLOYMENT_ID} ${MYSQL_DATABASE} ${MYSQL_USER} ${MYSQL_PASSWORD} ${MYSQL_HOST} ${MYSQL_PORT} ${CONFIG_SPLIT_LOCAL} ${CONFIG_SPLIT_DEV} ${CONFIG_SPLIT_STAGING} ${CONFIG_SPLIT_PROD}' < /opt/drupal/settings.php.tmpl > /opt/drupal/web/sites/default/settings.php
fi
# Dev tools
if [ "$DEPLOY_ENV" != "main" ] && [ "$DEPLOY_ENV" != "staging" ]; then
cp -f /opt/drupal/.devcontainer/settings.dev.php /opt/drupal/web/sites/default/settings.dev.php
cp -f /opt/drupal/.devcontainer/local_dev.services.yml /opt/drupal/web/sites/local_dev.services.yml
fi
Rebuild-db.sh
I will get into the bigger picture of this more in a future post about the whole database lifecycle, but for now, here's the script. There is a hierarchy of options for what to do in terms of building the database:
- If it already exists, don't do anything new. This keeps it safe when deploying to environments with databases already existing which should not be wiped out.
- If it doesn't exist, but there is a backup file in the backups folder, start by importing that.
- If it doesn't exist and there is no backup file, build a new site from the configuration. Normally in this setup now, it shouldn't ever need to get this far, but if it does, that's still a usable site, just with a lot less representative content.
#!/bin/bash
# Find the newest .sql backup file
newest_file=$(ls -t "/opt/drupal/backups"/*.sql 2>/dev/null | head -n 1)
if [ -z "$newest_file" ]; then
echo "No .sql backup files found. Starting a new Drupal install."
# Import config
drush site-install --existing-config
# Run again to catch extra configuration in the config_split
drush config-import -y
# Add admin user
drush user:create $DRUPAL_USER_NAME --mail="$DRUPAL_USER_EMAIL" --password="$DRUPAL_USER_PASSWORD"
drush user:role:add "administrator" $DRUPAL_USER_NAME
else
echo "The newest backup .sql file is: $newest_file. Installing now."
source /opt/drupal/scripts/import-db.sh $newest_file
fi
# Rebuild node access caches
drush php-eval 'node_access_rebuild();'
drush cr
Import-db.sh
The rebuild-db.sh file also referenced import-db.sh, which handles the scenario when importing from a database backup file. It then also sets the Drupal user based on the variables, and imports any configuration to incorporate any changes since the backup. This gets used in the initial local dev environment build. It also gets used to update the database on dev and staging servers so they can continue to have relatively accurate content.
#!/bin/bash
source /opt/drupal/scripts/settings-file.sh
BACKUP_FILE="/opt/drupal/backups/$(ls -1r /opt/drupal/backups | head -n 1)"
if [ "${BACKUP_FILE}" == "" ]; then
echo "No backups found"
exit 1
else
echo "Applying SQL from ${BACKUP_FILE}..."
drush sql-cli < "${BACKUP_FILE}"
drush cr
# Activate and set the distinct local password on the admin user.
drush user:unblock $DRUPAL_USER_NAME
drush upwd $DRUPAL_USER_NAME $DRUPAL_USER_PASSWORD
# Update config twice to ensure any config_split differences
drush config-import -y
drush config-import -y
drush cr
fi
Phpunit-file.sh
The phpunit file I will also get into more in a future post, but it is a lot like the settings file, simply substituting variables into the file.
#!/bin/bash
envsubst '${MYSQL_DATABASE} ${MYSQL_USER} ${MYSQL_PASSWORD} ${MYSQL_HOST} ${MYSQL_PORT}' < /opt/drupal/phpunit.xml.tmpl > /opt/drupal/phpunit.xml
Previous: The Primary Web Dockerfile