Mar 09 2020
Mar 09

This is Part 2 of our article on how to tweak BLT so that it can work with Pantheon. If you want more information about BLT, Pantheon, and our setup, go to How to Set Up BLT and Gitlab CI to Work with Pantheon Hosting (Part 1). In this part of the article, we'll go into the details of setting up the CI/CD system in a Drupal project with BLT and Gitlab CI.  

Part 2: Use BLT with Pantheon

We'll set up a development workflow with:

  • BLT: a suite of tools that wraps around your Drupal project
  • Gitlab CI: the pipeline to validate and build artifacts
  • Pantheon: hosts the Drupal site

In this tutorial, we'll set up a Drupal website with the name drupal-books-api.

The Setup 

0. Create the project on Pantheon 

Create the Drupal project on Pantheon, then switch into Git and collect the Git URL. 

1. Create project with BLT 

On your computer, create the project by running

composer create-project --no-interaction acquia/blt-project drupal-books-api

Open the file blt/blt.yml and update git.remotes with the Git URL

   default_branch: master
      - ssh://[email protected]

2. Add Gitlab CI 

Gitlab CI, by its name, is a service from Gitlab that integrates CI/CD pipelines to help build, test and deploy applications. 

We'll start by adding the file name .gitlab-ci.yml into the project's root. 

## 1. use docker image composer
image: composer
## 2. define custom variables
    BLT: ./vendor/bin/blt
## 3. cache, reused packages fetched from previous job
        - $HOME/.npm
        - $HOME/.nvm
        - vendor
        - docroot/core
        - docroot/modules/contrib
        - docroot/themes/contrib
        - docroot/profiles/contrib
        - docroot/libraries
## 4. additional setup, prior to running the main tasks
    # Setup SSH key to push artifact to deploy to server
    - mkdir -p ~/.ssh
    - eval $(ssh-agent -s)
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
    - echo "$DEPLOY_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 0600 ~/.ssh/id_rsa
    - ssh-add ~/.ssh/id_rsa
    # Install rsync as needed by BLT
    - apk add rsync --no-cache
## 5. define stages
    - validate
    - build_deploy
## 6. stage validate
    stage: validate
        # Install required dependencies using composer
        - composer install --ignore-platform-reqs
        # Validate composer, phpcs ...
        - $BLT validate
## 7. state build and deploy
    stage: build_deploy
        - master
        - $BLT artifact:deploy --commit-msg "$CI_COMMIT_TITLE" --branch "master" --ignore-dirty --ignore-platform-reqs --no-interaction --verbose


1. Use Docker image composer: This defines the Docker image that the executor will run to perform CI tasks. This setup uses the composer image. If your CI/CD pipeline has specific requirements, e.g. a specific PHP version, you can always find another prebuilt Docker image from DockerHub. Or if none suits your purpose, you can create and push a custom Docker image for your team. 

2. Define custom variables: These variables will later be used during the run of CI tasks. In this example, we defined a variable named BLT which equals to ./vendor/bin/blt. Then in step 6, $BLT validate simply means running script ./vendor/bin/blt validate.

3. Cache: Cache the downloaded dependencies so that in the next stage, it doesn't have to download them again from the Internet. This helps speed up the running time of the jobs. 

4. Before script: the additional tasks to run prior to the main tasks. At the end of the build process, we want Gitlab CI to push the latest version to Pantheon. As in the first step, we defined Pantheon to accept Git push via SSH protocol, so we need the container where Gitlab CI is running our tasks to be able to identify itself with Pantheon. In this step, we simply create a SSH key ~/.ssh/id_rsa with content from $DEPLOY_PRIVATE_KEY. $DEPLOY_PRIVATE_KEY is a predefined environment variable that we set up in our Gitlab project by going to Gitlab > Your Project > Settings > CI/CD > Variables. 

The value of DEPLOY_PRIVATE_KEY is the private key, which can be retrieved by 

pbcopy < ~/.ssh/id_rsa
// Then paste it into CI/CD variables

Or if you don't have pbcopy, simply copy the contents of ~/.ssh/id_rsa


Note 1: Make sure that the SSH key is added to your Pantheon account, otherwise the Pantheon server has no idea who is pushing code. 

Note 2: It's a good practice to set up a separate SSH key for deployment for each team. 

5. Define stages: Define how many stages you should have in your pipeline. In our simple project, we have two stages: validate code and build the artifact, then deploy it. 

Steps 6 and 7 include tasks to run in each of the stages. 

6. Install dependencies and perform validation: Although BLT will run composer install during the process of creating an artifact, this step is required to make sure BLT and its dependencies are present and up-to-date before running any BLT commands afterward. $BLT validate runs a group of commands below: 

blt tests:composer:validate
blt tests:php:lint
blt tests:phpcs:sniff:all
blt tests:yaml:lint:all
blt tests:twig:lint:all

7. Build the artifact and deploy to Pantheon's server: If all is good, Gitlab CI jumps to the second stage by running the command 

blt artifact:deploy 
  --commit-msg "$CI_COMMIT_TITLE" 
  --branch "master" 
  --no-interaction --verbose

This will create an artifact, then push it to the branch master of the remotes defined in file blt.yml mentioned in step 1. At the end of the process, you'll see the commit passed through to Pantheon's dashboard.

Notice that in step 7, there is a declaration of only which accepts master. This means the step build_deploy triggers only when an action is made on the branch master

Let's say your team follows Gitflow workflow. When a developer pushes the feature branch (feature/00000-change-header-color), Gitlab CI should run the validate stage to verify and sniff code, but should not deploy it right away to Pantheon. Instead, the developer would create a Merge Request against master. Once approved, trigger CI to validate again, then build and deploy to Pantheon. 

So, with the addition of the file .gitlab-ci.yml, we are able to orchestrate a Gitlab CI instance to validate code and push it to Pantheon when things are right. 

3. Update settings.php 

At this point, we have the CI set up and configured, but that's not enough for our small project to run on Pantheon. We need the database connection.

Drupal projects created by Pantheon come with a modified version of settings.php and an additional file settings.pantheon.php. These files allow your project, when run on Pantheon, to be able to read the database connection from a JSON file located at $_SERVER['PRESSFLOW_SETTINGS'], and use it to connect your Drupal site to the correct database. 

A project created with BLT doesn't come with this setup by default, so we need to update settings.php and settings.pantheon.php from the Pantheon repo. 

Note: Make sure to have hash_salt in your settings.php 

$settings['hash_salt'] = '41kFdvIe95v0tbqQWoxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-JJwoEW4iQeHer-wkMB3rgAXkVFQ';

4. Add the pantheon.yml file 

While Acquia prefers to put Drupal code in the docroot directory, Pantheon nests docroot in a directory called web. So we need to add the final tweak—add the file pantheon.yml to the project root: 

# Put overrides to your pantheon.upstream.yml file here.
# For more information, see:
api_version: 1

# PHP Version:
# Set site's PHP version to 7.2
php_version: 7.2

# Nested Docroot
web_docroot: true

If your project doesn't have this file, Pantheon will create one. Since we want to specify web_docroot: true, creating a pantheon.yml tells Pantheon not to overwrite our custom setup. 

And lastly, we need a symlink 

ln -s docroot/ web

Now, commit the changes to Gitlab and wait for the green check marks in Gitlab CI/CD > Pipelines. 

Where to Go From Here 

Now that everything is in place, development workflows in teams are simpler and more manageable: 

  • Developers can just worry about their code and dependencies, not the deployment. With BLT and Gitlab CI, only SASS files make their way to the Git repo, so there's no more resolving of CSS conflicts, no more commits of type "Recompile CSS" 
  • Vendor/, core/, modules/contrib/, libraries/ can be excluded from the Git repo 
  • Deployment is more consistent as the build process runs through the same list of predefined tasks 

A more complex setup can include: 

  • Custom Docker image to run Gitlab CI tasks 
  • More code validating 
  • Automated testing 
  • Complex approval process 
  • Post deployment notifications

This is one way to make BLT work with Pantheon. Let us know if you have comments or questions!

Mar 09 2020
Mar 09

As developers, we all want to maximize the use of the tools we've become familiar with. For me, this is BLT by Acquia. Since BLT fits with Acquia's workflows and Acquia Cloud, it's not designed to work with Pantheon, which has a different setup and development workflow.

This article will help you make tweaks so that BLT can work with Pantheon. The setup in this article is deliberately simple in order to explain the approach and concept. The real world setup is usually much more complex and depends on the needs of each project.

Also in this article, we will implement a simple CI/CD pipeline to conduct a seamless flow of continuous integration from the developer's code to the phase where code is delivered to the deployment server.

To be as thorough as possible, we will break the article into two parts:

  • Part 1: We'll explain what parts make up the system and why they're important.
  • Part 2: We'll show you how to implement the setup.

TL;DR: if you're already familiar with BLT and Pantheon, go to How to Set Up BLT and Gitlab CI to Work with Pantheon Hosting (Part 2). Otherwise, read on.

Part 1: What and Why

The CI/CD Pipelines

A typical Drupal project includes a Drupal code base, version control software (Git), dependencies management tools (Composer, NPM, Bower), and development and production environments.

The pipelines connect all parts of the projects together and orchestrate them to do their jobs at the right moment, thus creating a flow of updates from developers to the deployment server.

In our setup, the workflow looks like this:

  1. Developers push code to Gitlab.
  2. Gitlab CI gets triggered and spins up a Gitlab runner, which is a build instance. Gitlab runners run each and every task as defined in the .gitlab-ci.yml. Tasks are: 
    • Code sniffing 
    • Installing composer dependencies 
    • Run npm or gulp tasks to build front-end assets 
    • Run tests if needed 
    • Sanitize and build artifacts
  3. Push the artifact above to Pantheon.

Pipelines done right can increase product quality thanks to the layers of validation. It can also increase the speed of deployment, which means that new features and bug fixes will reach deployment faster.


Pantheon is one of the biggest names in Drupal hosting. Pantheon's main workflow includes the three main environments Dev-Test-Live and Multidev, which creates extra development environments that development teams can use to test feature branches before merging back to Dev.

Acquia BLT

In some big Drupal projects where many developers are involved, automation is required and managing configurations across many Dev-Stage-Preprod-Prod environments becomes complicated. Things can easily become chaotic due to the different ways each member of the team approaches problems.

Acquia BLT is a suite of tools that wraps around your Drupal project, which helps you manage team projects in a more standardized way. It also adds an automation layer on top of Drupal, making the implementation of continuous integration and deployment easier.

Some notable features of BLT:

  • Local Git hooks: Sniffing coding standard on each commit, or even setting a standard pattern on git commits. This is very useful, for example, when we have the git commit pattern set to 00000 - description of commit, which helps track down the commit and the task in PM tools such as Jira or Redmine.
  • Automation tasks: Tasks such as compiling front-end assets and composer install.
  • Artifact generation: Creating production-only artifacts. Generating a safe artifact to deploy to another git repo involves a lot of steps. During the process of generating artifacts, BLT goes into directory ${project.root}/deploy, checks out the main code base, installs dependencies, builds front-end assets, sanitizes the code base, and creates an artifact ready to deploy. This process is complex given the amount of tasks to run and the amount of files to include or exclude from the artifact. BLT is built on a set of best practices in Drupal development and does the heavy lifting in sanitizing and preparing the code.
  • CI/CD: Works with Acquia Cloud pipelines and Travis CI or Gitlab CI.

An Example

In some projects, we had to integrate Drupal with Okta authentication using module simplesamlphp_auth. Okta configuration needed to be declared in vendor/simplesamlphp/simplesamlphp/config/config.php, and this could be erased when running composer install. Then we needed to add the extra commands post-install-cmd or post-update-cmd to remember to patch the SimpleSAMLphp package with our configuration.

It's manageable, but when a new developer comes onboard, you'll need to explain all these details again and again.

With BLT, it's simple to set up simplesamlphp_auth: add a SimpleSAMLphp to blt/blt.yml, then BLT will remember to include your config from ${project.root}/simplesamlphp/config during the deployment.

This is a simple use case that we like from BLT. Even if you're experienced and can handle all DevOps and automation tasks by writing your script, it's still hard to synchronize your brain with your colleagues'. This setup allows you to automatically include your config during deployment so you don't have to go through all the steps again.

Implementation of the Setup

In How to Set Up BLT and Gitlab CI to Work with Pantheon Hosting (Part 2), we'll go into the details of the setup and show you how to run a Drupal site wrapped by BLT on a Pantheon server.

Jan 06 2020
Jan 06

"There's an app for that."

We hear this often because it's true. In today's hyper-connected world, offering mobile apps for your business or services have become an expectation. According to Statista, the total number of mobile apps on Apple and Google's app stores are 1.8M and 2.47M respectively. It's now the norm to build apps that support both platforms. But native apps have always been an issue for businesses due to the costly development cycle. Then comes cross-platform technology, which enables developers to build and maintain a single code base for applications that can be published across multiple platforms.   

At Evolving Web, we specialize in web development using Drupal and recently extended our offerings to include mobile solutions. We developed mobile applications using React Native that could be seamlessly integrated to Drupal 8 using modules such as JSON:API or SimpleOAuth, and also implemented third party services such as Firebase or Amplify to enhance the mobile experience with features like push notification or real-time data synchronization.   

We have been actively researching and experimenting with cross-platform technologies and have learned that while they can save a lot of development time and costs, they also present new challenges.

Here's a quick overview of the pros and cons of cross-platform technology:


  • Write once, run anywhere: developers don't need to write Swift for iOS or Java/Kotlin for Android. The whole business logic remains in one single piece of code.
  • Easy code management: a single code base is easier to maintain.
  • Shorter development cycle: write once, deploy to both stores. 


  • Unoptimized app performance: the need to support multiple platforms can diminish performance. 
  • Not-so-native feel: Balancing platform-specific experiences can be difficult due to differences in user experiences for iOS and Android.

In this article, we'll explore our experiences with React Native and Flutter, two mobile application frameworks that will continue to dominate in 2020.

The Top Two Mobile Frameworks

In 2019, React Native took the lead of being the most used cross-platform mobile framework, followed by Flutter, Cordova, Ionic and Xamarin. Flutter has also been a rising star in the last year.  

React Native

Created by Facebook in 2015, React Native is undoubtedly the most adored cross-platform mobile development framework with 83.4k stars on Github so far. The framework allows you to build applications using Javascript or Typescript, and brought in the concept of the bridge that helps you generate and manipulate native mobile UI components from a background Javascript thread. Unlike other frameworks such as PhoneGap, Cordova, and Ionic—which rely on webview to render UI components—React Native allows us to create and manipulate real UIView instances like we would have done with native mobile development.  

Moreover, you can also write modules in native languages such as Objective C, Swift or Java, which allows you to interact with OS APIs if you want to build more sophisticated applications. This opens up more possibilities regarding what you can build with React Native.   

Facebook and Instagram are two top-of-the-chart applications built in React Native, not to mention other names such as Tesla and Bloomberg. In May 2019, Microsoft announced a new performance oriented open source project for React Native developers who want to target Windows, another example of how much the framework is extending their capabilities. 


  • Convenience: benefit from time and cost efficiencies. 
  • Faster refresh: get near-instant feedback for changes on React components.
  • It's a sibling with ReactJS so the web components are reusable. 
  • Awesome performance: thanks to its technical capabilities and big, supportive community.


  • Not completely intuitive: you may need expertise from native developers for platform-specific modules.   

With a vibrant developer community and increasing recognition from tech businesses, React Native will thrive and continue to evolve in 2020.

Apps Built With React Native

Apps built by React Native - Facebook, Tesla, Bloomberg

  • Facebook
  • Tesla
  • Bloomberg  


Flutter is a modern development kit from Google used to build mobile apps for Android, iOS and Google Fuchsia—an operating system that can run on embedded systems in smartphones, tablets, and personal computers.

Google announced Flutter's first stable release in 2018. Despite being quite young on the market, Flutter has quickly gathered a large community and is the fastest-growing skill among software engineers. One of the reasons Flutter has risen so quickly is because of its performance. With the UI refreshing at 60fps—mostly using GPU—each and every pixel on the screen is painted on SkiaCanvas, allowing developers to create sophisticated, smooth and highly customizable UIs.   

In order to build apps with Flutter, developers need to use Dart, a programming language also developed by Google. There are many fantastic features of Dart that make it crucial to Flutter's success, one of which is that Dart is one of the few languages that does compiling in both AOT and JIT. Just-in-time (JIT) compilers run during the execution of the program, compiling on the fly, which provides much faster development cycles as developers see updates right away, though it has slow startup times. On the other hand, ahead-of-time (AOT) compiles high-level programming languages into native machine code so that the resulting file can execute natively and really fast. Flutter's use of Dart benefits from the hot reload thanks to the JIT compiler and the quick execution and startup times due to the AOT compiler.   


  • Hot reload: you can see the results of your changes almost instantly.
  • Speed: fast execution and startup times.
  • High-performing native experience: the UI refreshes up to 60 fps animations.
  • Direct access to native code: you can import libraries and use native APIs.
  • Fantastic testing and performance profiling support. 


  • Relatively young: not as much support as other frameworks. 
  • Less features: less available plug-ins.  

Apps Built With Flutter

Flutter Apps - Google Ads, Alibaba, Top Goals

  • Google Ads
  • Alibaba 
  • Top Goals


If you're a developer with a JS background, React Native's big community will speed up the learning process and is a quick way to familiarize yourself with mobile development.   

If you don't mind learning a new language and want to experiment with a new technology that's performance focused, Flutter is the way to go.  

In 2020, we will continue to bring more beyond-Drupal solutions to our clients and can't wait to maximize the capabilities of React Native and Flutter for future projects. Tune in for future blog articles where we teach you how to integrate these solutions with Drupal!

If you want to learn more about Evolving Web and our culture, feel free to visit our careers page

Jul 09 2018
Jul 09

Gatsby is a really fast React-based static site generator. You can use it to create a static site, with content pulled from Drupal and other content management systems. 

Why Use Gatsby?

Unlike dynamic sites which render pages on-demand, static site generators pre-generate all the pages of the website. This means no more live database querying and no more running through a template engine. Performance goes up and the maintenance cost goes down.

Static site generators have been evolving over the last few years. Tools like Jekyll, Gatsby, Hexo, Hugo become more and more popular. They have been chosen by developers who want a simple website/blog solution. They need very minimal server setup and have a low maintenance cost. However, static site generators usually require writing content in Markdown, which is not a great authoring experience for most content editors.

On the other hand, content management systems such as Drupal and Wordpress can provide a very powerful back-end. Having a WYSIWYG editor and content types help editors to manage content more easily and systematically. However, maintaining a CMS requires hosting a web server and database, and opens you up to security vulnerabilities and performance issues.

Gatsby stands in between the simplicity and robustness of static site, and the versatile back-end of a content management system. Using Gatsby means that you can host the CMS in-house and publish content generated by Gatsby as a static website. The first thing you’ll notice about Gatsby is how amazingly fast it is.

A diagram showing how content gets published with Gatsby

How to Integrate Drupal and Gatsby

In this tutorial, we are going to put together a demo that pulls Drupal content into a Gatsby site. We’ll borrow content of this awesome blog post to create a list of coffee types in Drupal, then transfer the list content to Gatsby.

This goal can be achieved with 4 steps:

  1. Build a Drupal server
  2. Build a Gatsby site
  3. Fetch content from the Drupal server
  4. Publish the Gatsby site

1. Build a Drupal server

Let’s say we already have a Drupal 8 site installed. We’ll need to:

  • Create a content type name Coffee with three fields: Title, Body and Image
    Content type Coffee
  • Turn Drupal into an API server by installing 2 modules jsonapi and jsonapi_extras.
  • Give Anonymous user permission to Access the JSON API resource list
    JSONAPI permission
  • Verify that the API server is working well by going to http://[your-site]/jsonapi as an Anonymous user. The page should show up with all information of your API server
    JSONAPI endpoint


  • If you use Chrome, use JSON Viewer Chrome extension to view JSON data in a better format
  • If you don’t set permission for Anonymous user to Access JSON API resource list, you’ll get error 406 - Not acceptable when trying to connect to Drupal from Gatsby
  • If you don’t have jsonapi_extras installed, you’ll get error 405 - Method Not Allowed when query data from Gatsby

2. Build a Gatsby Site

First, make sure you have node and npm installed on your computer. Verify it by typing node -v and npm -v into Terminal

node -v
npm -v

Install Gatsby’s command line tool

npm install --global gatsby-cli

Create a new Gatsby site and run it, I’ll call my Gatsby site coffees.gatsby

gatsby new coffees.gatsby
cd coffees.gatsby
gatsby develop // start hot-reloading development environment

By default, the new Gatsby site is accessible at localhost:8000

Gatsby default page

3. Fetch Content from the Drupal Server

At this step, we’ll be creating a new simple page /coffees that displays all the coffee types from the Drupal site.

Create the /coffees page

Create a new page in Gatsby is as simple as creating a new JS file. All Gatsby pages should be stored in /src/pages. In this case, we’ll create the file coffees.js in /src/pages and add the following code in coffees.js:

import React from "react"
const CoffeesPage = () => (
    <h1>Different types of coffee</h1>
export default CoffeesPage

This simple code does one thing: create a new page at /coffees. The content of this page is a heading h1 with the text “Different types of coffee”

Page “Different types of coffee”

Query Drupal content using GraphQL

In order to pull data from Drupal 8 site, we’ll need to install the gatsby-source-drupal plugin

// in your coffees.gatsby folder
npm install --save gatsby-source-drupal

Configure the gatsby-source-drupal plugin

// In gatsby-config.js
plugins: [
    resolve: 'gatsby-source-drupal',
    options: {
      baseUrl: 'http://dcmtl2018-demo.server/',
      apiBase: 'jsonapi', // endpoint of Drupal server

After adding the plugin configuration, the site should still be functioning. If Gatsby throws a 406 error, check the permission on the Drupal site; if Gatsby throws a 405 error, make sure module jsonapi_extras is enabled.

Build GraphQL to query all coffee nodes from Drupal

Gatsby comes with an in-browser tool for writing, validating and testing GraphQL queries named GraphiQL, and it can be found at localhost:[port]/___graphql, in our case it’s localhost:8000/___graphql

Let’s try querying all the Coffee nodes in this tutorial

GraphiQL interface

After building the query successfully, let’s go back to the coffees.js file to execute the query.

export const query = graphql`
  query allNodeCoffee {
    allNodeCoffee {
      edges {
        node {
          body {

Then, update the const CoffeesPage to display the title and body content:

const CoffeesPage = ({data}) => (
    <h1>Different types of coffee</h1>
    {{ node }) => (
        <h3>{ node.title }</h3>
        <div dangerouslySetInnerHTML={{ __html: node.body.value }} />

Thanks to hot-reloading, we can see the sweet fruit of the work right after saving the file

Render content

So far, we have done:

  • Create an API server with Drupal and jsonapi, jsonapi_extras
  • Create a Gatsby site with page coffees.js that “reads” content from Drupal server

Let’s move the the last step of the tutorial: publish Gatsby site.

4. Publish the Gatsby Site

Gatsby names itself as a static static site generator, meaning its main purpose is to generate a bunch of static HTML, JS, CSS and images files. This action can be done by only one command:

gatsby build

Once finished, checkout /public folder to see result of your hard work along this long tutorial. Deploying your site is now simply copy/push contents in /public to server.

[embedded content]


In this tutorial, we got to know how to:

  • Create a new Gatsby site
  • Install new plugin in Gatsby
  • Use GraphiQL to write, validate and test GraphQL query

I personally find that Gatsby is a good solution for setting up a simple blog. It’s easy to install, very fast, and requires zero server maintenance. In a future blog post, I’ll talk about how to integrate more complex data from Drupal into Gatsby.

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web