Ballast
A local development toolset developed with the support of Digital Pulp., (*1)
Key contributors:
- Shawn Duncan
- Nick Maine, (*2)
A Composer template for Drupal and WordPress projects with Docker
This project template automates Docker based local development with using
DDEV as a foundation., (*3)
- Site dependencies managed with Composer and initial Composer plugins are compatible
with Composer 2.
- Automated setup of DDEV.
Getting Started
-
First you need to install composer.
Note: The instructions below refer to the global composer installation.
You might need to replace composer
with php composer.phar
(or similar) for your setup., (*4)
-
Ballast will check your system for needed software. If anything is missing, a list of missing packages will be
provided., (*5)
-
We recommend that you place all your Ballast projects in a single parent directory, such as ~/BallastSites/
., (*6)
-
OS Specific Notes:, (*7)
-
macOS: Your Docker Sites need a home. DDEV uses Docker for Mac which manages file exports to Docker. This is
well known to have performance issues on macOS. Once you have confirmed that the project loads properly then we
recommend that you transition to use NFS
for the mounted filesystem. We have extracted and automated the setup described in the DDEV docs:
ahoy setup-nfs
-
Windows Linux Subsystem: Build and manage your Ballast sites within Linux. DDev recommends this approach for
Windows users.
-
Native Linux: Follow one of the installation methods for DDev.
Upgrading to 3.x
You will see an http-proxy container running in Docker Desktop. This container will create a conflict with ddev.
If you are only testing 3.x then simply stop this container. If you are changing to 3.x completely, you can delete
this container. If you have to run 2.x after deleting, the 2.x version of ahoy harbor
will restore it., (*8)
Managing Theme Tasks
There are ahoy commands for running theme tasks in the front-end container, so you can choose to not install node on
your host. When you first setup a site with an established theme, you will probably need to run
ahoy npm install --no-save
to install the node based theme tools. Additional ahoy commands for front-end task can be
found via ahoy --help
., (*9)
Initial Setup
After the initial setup, you should delete the Initial Setup section of
this README., (*10)
In the folder chosen or created under Getting Started, composer create-project digitalpulp/ballast your_project
., (*11)
Edit composer.json
There is also a project specific value to set here, which is the path to
bower components in the custom theme file. Edit the extra section to
add a path to the theme bower_components
directory. An example entry
is shown as the last line in the snippet below:, (*12)
{
"extra": {
"installer-types": ["bower-asset"],
"installer-paths": {
"docroot/core": ["type:drupal-core"],
"docroot/libraries/{$name}": ["type:drupal-library"],
"docroot/modules/contrib/{$name}": ["type:drupal-module"],
"docroot/profiles/contrib/{$name}": ["type:drupal-profile"],
"docroot/themes/contrib/{$name}": ["type:drupal-theme"],
"drush/contrib/{$name}": ["type:drupal-drush"],
"docroot/themes/custom/theme_name/bower_components/{$name}": ["type:bower-asset"]
}
}
}
This can be changed to npm-asset
if that fits your project better., (*13)
In addition, an additional property is available for the drupal-scaffold
key in the extra
property:, (*14)
{
"file-mapping": {
"[web-root]/.htaccess": false,
"[web-root]/.eslintrc.json": false,
"[web-root]/.ht.router.php": false,
"[web-root]/INSTALL.txt": false,
"[web-root]/README.txt": false,
"[web-root]/autoload.php": false,
"[web-root]/example.gitignore": false,
"[web-root]/index.php": false,
"[web-root]/robots.txt": false,
"[web-root]/update.php": false,
"[web-root]/web.config": false,
"[web-root]/sites/default/settings.php": false
}
}
These will block that file from being changed when core is updated or added to the project., (*15)
WordPress
Replace the original composer.json file with the wordpress-composer.json
and running composer update
., (*16)
Initial Composer Install
You may wish to require an initial line up of contributed modules or WordPress plugins. (See
Updates and Maintenance below). If you are not adding modules at first,
you may run composer update nothing
to generate an initial
composer.lock
file. Either way, committing the result will speed
setup for other members of your team., (*17)
Run composer robo setup:cloned
An interview and automated setup will run and configure much of the project for you.
Note: Testing shows that Composer 1.x does not support interactive scripts. If you are still on Composer 1, use
php scripts/robo/BallastRunner.php setup:clone
instead. Ahoy commands will be setup as part of this process if you
accept the offer to also run setup:project
, which will enable ahoy robo
for running other advanced commands., (*18)
Set a node version in your custom theme.
The front-end container expects a .node-version
file in the theme directory. See the nodenv documentation., (*19)
Prepare for Codeship Pro
We use Codeship Pro to deploy
projects built on this template. A free tier is available. All commits
on the develop
branch will be built and deployed., (*20)
Docker Environment Variables
There are two environment variables and one path which need to be set in
codeship-services.yml
found in the root of the project repo:, (*21)
- front-end : environment : THEME_NAME - The folder/machine name of your custom
theme. This folder should be present in
docroot/themes/custom
. If you set
site_theme_path
in setup/config.yml
then also set THEME_PATH here to the
same value.
- front-end : working_dir - set this value to the full path in the container to
the theme folder. Usually
/var/www/docroot/themes/custom/yourtheme
- deploy : environment : DEPLOY_TARGET - The url of the git remote to
which Codeship will push the build artifact.
Encrypted Environment Variables
Deployment credentials should not be stored in the repo in the clear, but Codeship will decrypt these environment variables on each build. You will need to install the Codeship CLI tool Jet to accomplish these steps., (*22)
- Create a new Codeship Pro project.
- Open Project Settings and browse to the General tab.
- Scroll down and download the AES Key.
- Move this key into the project root and rename it
codeship.aes
- Create a new file named
env
(Both this file and codeship.aes
are
set to be ignored by git).
- Copy or create a private key that matches the public key installed in
your target git remote to the project root or to a
/keys
directory in the project.
- Use the ahoy commands to bring up the local project.
- Use advanced command
ahoy key-prep path/to/private_key
to get your private
key in a one-line format and appended to the env
file.
- Execute
ssh user@git_remote_url
if you have not accessed this git
remote before to add the remote to your ~/.ssh/known_hosts
.
- Define the environment variables in
env
copying the appropriate line from ~/.ssh/known_hosts
, and the
name and email to use when commiting the build like this:
SSH_PRIVATE_KEY=one-line-key copied from the terminal
GIT_KNOWN_HOST=entire line matching the git remote copied from `~/.ssh/known_hosts`
GIT_NAME=Codeship-Deploy
GIT_EMAIL=name@example.com
-
Encrypt the file using Jet:
jet encrypt env env.encrypted
- Remove or move the key created in step 6 - do not commit the private key!
- Commit
env.encrypted
to the repo.
Using a Passphrase protected key in the CI Service
Some projects, such as ecommerce projects, should have all ssh keys
protected with a passphrase. This is not particularly an issue for keys
used by humans, but the key used by a continuous integration service
requires automation. The approach used here assumes that the CI
environment is using our containers and is therefore created for each
build. This example expects the CI service to be Codeship, which you
can adapt if you use a different service., (*23)
Additional Environment Variables
- Add the passphrase into the
env
file described above as
SSHPASS=key
and re-encrypt the file.
- Add an environment variable to
environment
section of the deploy
service in the codeship-services.yml
file:
GIT_SSH_COMMAND: SSH_AUTH_SOCK=/root/.ssh/ssh_auth_sock ssh
Manually Connect Once
If the key in question has never been used to authenticate to the remote
git service, ssh-agent will still prompt for the passphrase as discussed
in this StackExchange comment.
Use ssh -i
to connect once to the git service from the command line on
your local using the CI key. Supply the passphrase at the prompt and
the git service will be primed for use by your CI user., (*24)
Advanced ahoy
commands for Tech Leads
There are some additional commands in the ahoy.yml
file marked "Advanced" which do
not appear in response to ahoy --help
These are intended for tech leads that may need
to shell in the docker container for some purpose., (*25)
Install the Project
composer install
All docker dependencies and Drupal core dependencies along with Drupal
core will be installed., (*26)
You should commit all files not excluded by the .gitignore file., (*27)
What does the template do?
When installing the given composer.json
some tasks are taken care of:, (*28)
- Drupal will be installed in the
docroot
-directory.
- Autoloader is implemented to use the generated composer autoloader in
vendor/autoload.php
, instead of the one provided by Drupal
(docroot/vendor/autoload.php
).
- Modules (packages of type
drupal-module
) will be placed
in docroot/modules/contrib/
- Theme (packages of type
drupal-theme
) will be placed
in docroot/themes/contrib/
- Profiles (packages of type
drupal-profile
) will be placed
in docroot/profiles/contrib/
- Creates default writable versions of
settings.php
and services.yml
.
- Creates
docroot/sites/default/files
-directory.
- Latest version of drush is installed locally for use
at
vendor/bin/drush
.
- Latest version of DrupalConsole is installed locally for use
at
vendor/bin/drupal
.
- The local machine is checked for dependencies to run the docker
development setup. Any missing dependencies are installed
via homebrew. The following are required for Mac:
- DDEV Local
- Docker Desktop
- Docker Compose
- pre-commit by Yelp
- Strongly suggested but not required:
Updates and Maintenance
Updating Drupal Core
This project will attempt to keep all of your Drupal Core files
up-to-date; the project drupal/core-composer-scaffold is used
to ensure that your scaffold files are updated every time drupal/core
is updated. If you customize any of the "scaffolding" files (commonly
.htaccess
), you may need to merge conflicts if any of your modified
files are updated in a new release of Drupal core. See the drupal-scaffold
section above
to block updates to any of these files., (*29)
Follow the steps below to update your core files., (*30)
- Run
composer update drupal/core-recommended drupal/core-composer-scaffold drupal/core-dev --with-all-dependencies
to update
Drupal Core and its dependencies.
- Run
git diff
to determine if any of the scaffolding files have
changed. Review the files for any changes and restore any
customizations to .htaccess
or robots.txt
.
- Commit everything all together in a single commit, so
docroot
will
remain in sync with the core
when checking out branches or running
git bisect
.
- In the event that there are non-trivial conflicts in step 2, you may
wish to perform these steps on a branch, and use
git merge
to combine the updated core files with your customized files. This
facilitates the use of a three-way merge tool such as kdiff3. This
setup is not necessary if your changes are simple;
keeping all of your modifications at the beginning or end of the file
is a good strategy to keep merges easy.
Updating WordPress
Edit the version number and url in the package definition embedded in the
composer.json. See:
- WPackagist
- Webroot Installer, (*31)
Updating and maintaining composer.json
At the Managing Drupal Projects with Composer BOF at DrupalCon
Baltimore, one of the common pain points was merge conflicts in
composer.json
and composer.lock
. It was the strong consensus of
those gathered that for development teams, there should be one
designated maintainer on the team for these files. New modules, updates
and so forth should be requested from the maintainer, who is generally
the project lead., (*32)
With composer require ...
you can download new dependencies, including
Drupal contributed modules to your installation. To install the latest
versions of multiple modules:, (*33)
composer require drupal/block_visibility_groups drupal/config_split drupal/easy_breadcrumb drupal/focal_point drupal/media_entity_image drupal/media_entity_browser drupal/field_formatter drupal/paragraphs drupal/inline_entity_form drupal/pathauto drupal/page_manager drupal/viewsreference
You also can require bower components:, (*34)
composer require bower-asset/formstone
The stability minimum is set to stable. You will need to flag specific packages if you need a lower stability., (*35)
Local Developement Commands
The docker best practice is to work in the host and send commands to a
container when needed. This project uses Ahoy as an abstraction tool to
further simplify this flow for developers. Ahoy commands work anywhere
at or below the root directory of the project., (*36)
ahoy -h
will list all Ahoy commands., (*37)
FAQ
Should I commit the contrib modules I download?
Composer recommends no. They provide argumentation against but also
workrounds if a project decides to do it anyway., (*38)
Should I commit the scaffolding files?
The drupal-scaffold plugin can download the scaffold files (like
index.php, update.php, …) to the docroot/ directory of your project. We
generally commit these. If you have not customized those files you could
choose to not check them into your version control system (e.g. git). If
that is the case for your project it might be convenient to
automatically run the drupal-scaffold plugin after every install or
update of your project. You can achieve that by registering
@drupal-scaffold
as post-install and post-update command in your
composer.json:, (*39)
{
"scripts": {
"drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
"post-install-cmd": [
"@drupal-scaffold",
"..."
],
"post-update-cmd": [
"@drupal-scaffold",
"..."
]
}
}
How can I apply patches to downloaded modules?
If you need to apply patches (depending on the project being modified, a
pull request is often a better solution), you can do so with the
composer-patches plugin., (*40)
To add a patch to drupal module foobar insert the patches section in the
extra section of composer.json:, (*41)
{
"extra": {
"patches": {
"drupal/foobar": {
"Patch description": "URL to patch"
}
}
}
}
Appreciation
We are grateful for the following open source projects that made this project possible!, (*42)