A Successful One-Click Deployment in Drupal 8

by Juampy NR

When I started building the DrupalCamp Spain 2017 website I was very excited to see how far could I get with Configuration Management. A lot of effort went into Drupal 8 to make the management of configuration seamless without the dependency on contributed modules like Features. Long story short: Configuration Management works wonderfully if you introduce it in your development workflow. In this article, we will walk through how to implement, test, and deploy a release of DrupalCamp Spain’s website, including some issues we ran into and how we resolved them.

The goal: just push the button

Our goal with deployments at DrupalCamp Spain was that there should be nothing left to do on the website after the Jenkins job that performs the deployment has completed. It worked in the following way:

Someone would open the Jenkins production deployment job, select a git tag, and click build:

Production deployment

The above job would, via an Ansible task:

  1. Deploy the source code.
  2. Run composer install in a new directory.
  3. Run database updates: drush update-db -y.
  4. Import configuration: drush config-import -y.
  5. Purge Varnish caches: varnish clean domain all.

Here is the log of a successful deployment. In the following sections, we will see how to setup our project to achieve one-click deployments.

The starting point

The DrupalCamp Spain website has a public repository that you can fork and explore. It uses:

Jenkins jobs

Now, let’s look at one of the past deployments and then dissect it together.

A sample deployment

Even though the DrupalCamp Spain website was a fairly small project, we did many deployments to the production environment. As an example, I have chosen the 2.2.2 release - List session proposals because:

  • It includes a database update.
  • It contains configuration changes.
  • It involves code changes.
  • It adds new dependencies.

Here is the list of changes for the release:

Release changes

If you are wondering how all of the above could be deployed successfully, the answer is twofold. First, we had a Jenkins job in charge of deploying the code. Second, we had a development process which tested deployments that we will look at in the next section.

Working on an issue

Release 2.2.2 was the result of the Call4Papers milestone. This milestone consisted of four issues which were completed via pull requests. We followed this development process:

  1. Install the site locally following the repository’s README.
  2. Pick an open issue like List session proposals.
  3. Create a new branch from master with the issue number such as 113-list-proposals.
  4. Implement the requirements and create a pull request when the branch is ready for review. If there are changes in configuration, export them via drush config-export and include them in the pull request.
  5. Once the pull request is merged, check Jenkins to verify that the automatic deployment to the development environment worked.

Polishing the deployment

In this case, I merged the pull request, and that triggered a Jenkins job that deployed the changes in the master branch to the development environment. It failed, as I made a change in the pull request just before it was merged that introduced a bug. Once I fixed that in a second pull request, I introduced a different bug (slap me with a trout, please), which needed a third pull request. Once it was merged, Jenkins could run the deployment task to the development environment successfully, and there was much rejoicing.

To verify the deployment, I opened the development environment in the web browser. I was puzzled when I found out that even though the database update that installed the new module did complete successfully, the module was not installed. After some debugging, I discovered that I forgot to export my local environment’s configuration into the config directory, which, after doing it with drush config-export, contained a setting to keep the new module installed. Since Jenkins runs drush config-import after drush update-db, it uninstalled the module before the module was present in the site configuration. Therefore, I created a fourth pull request that included this fix. When Jenkins deployed it to the development environment, this time, the module was successfully installed. Phew!

It took us four deployments to get the release working in the development environment. For production, we want to accomplish it in a single deployment. In the next section, we will see how to test that.

We use a tool called Tugboat in our client's projects that helps us to spot and fix implementation and deployment bugs in the early stages of development. You can find out further details at https://tugboat.qa.

Testing the deployment to production

Once we completed all the milestone’s issues for listing sessions, we needed to make sure they could be deployed as a whole without causing errors.

Simulating a production deployment

Here is an example of how we test production deployments:

First, we triggered the Jenkins job that copies the production environment's database and files into the development environment:

Development deployment job

Here is the log. Once we had fresh data at the development environment, we triggered the job that kicks in when someone makes changes to the master branch (which ran the Ansible task to update the database).

Jenkins job to copy database and files

The job completed successfully (Yay!). I also opened the development environment’s website and verified that the new module was installed and that all the new features that we worked on the milestone were working as expected. We were ready to publish this to production!

Hitting the button

The release was complete, and we tested its deployment. Therefore, we were ready to publish it. We used the Jenkins job that takes a git tag as a parameter, deploys the code to the production environment, and finally runs the post-deploy task to update the database. It worked!

When you test deployments beforehand, you get a deep understanding of what happens during such a process. It gets especially interesting when something goes wrong, as it gives you a chance to debug the deployment process locally, fix it, and test it again at the development environment until it works flawlessly.

What happens when a deployment goes wrong in production? Setting up a roll back process is something that deserves its own article. In the meantime, what’s your development and deployment process like? Do you have any suggestions? I am looking forward to hearing your insights below.

Acknowledgements

This article was possible thanks to the following folks:

Footer image by By włodi from London, UK (The Big Red ButtonUploaded by Yarl) [CC BY-SA 2.0], via Wikimedia Commons.

newsletter-bot