SCSS

Bootstrap your Composer project

Thumbnail

Si Hobbs

|

Making the switch to Composer can be daunting, so here's a leg up to get you started.

When I started working with Composer in late 2015 for my first Drupal 8 project, it was a little daunting. "Best practice" was an emerging cross-over between PHP package management Drupal way of doing things, you couldn't go to any one source for the full picture. 

In 2017, the situation has improved dramatically:

  • Drupal.org auto-generates composer.json data for all projects
  • Modules usually have solutions for adding 3rd-party libraries scripts.
  • Composer performance has improved.
  • Most distributions have a "Composer project" quickstart guide.
  • There are a range of composer examples and blog posts.

Shout outs

These are not "further reading", they are better reading, so I've put them at the top:

So why this article?

This article just looks at some of the starting points for your composer-based Drupal project.

Step 1: Make a choice

You can often create a new project from an existing component using:

composer create-project SOME/PROJECT DIRECTORY

which is the equivalent of doing a git clone and composer install. But you can also require the same components using:

composer require SOME/PROJECT

You'll create your project once, then then require additional components, so you should take some time to choose the right starting point. What are the starting points?

drupal-composer/drupal-project

The Drupal Composer Working Group have maintained a template for over two years. You can find it at https://github.com/drupal-composer/drupal-project.

The goal is a best-practice practice Drupal 8 installation, which includes Drush and Drupal Console, frameworks for PHPUnit and Behat testing, and some other helpers like cweagans/composer-patches.

This is the solution that I prefer. The decision to use Drupal shouldn't be coupled with how you host, develop, automate, or integrate. However there will be times where you take a different route. 

acquia/blt

A project created with Acquia BLT's composer.json will contain the best practice  of the Acquia Professional Services team. By default, this includes the same tools you'll get with drupal-composer/drupal-project, plus you'll get orchestration and automation tools and a Drupal installation based on the Lightning distribution.

There's a big assumption that you're hosting on Acquia, and perhaps you intend to use Acquia Pipelines too, but you don't have to. Technically speaking, Lightning and Pipelines are optional, but you will need some technical experience to swap in a different Drupal distribution, or use a custom Jenkins CI solution.

If Acquia has been preselected as a complete solution for all the things, then I'd recommend BLT - especially with the recent 8.9 release. Otherwise, BLT might add undesirable complexity, abstracting away processes and tools which you could easily learn yourself, for fun and profit.

You can also use composer require acquia/blt after the initial creation, to make your project less coupled to BLT. You'll need to do a little work to configure various paths, but it's worth it in my experience.

geerlingguy/drupal-vm

Drupal-VM by Jeff Geerling is a very worthy solution for local development, according to the quickstart guide, you can use Drupal VM to start your project. You don't run composer create-project yourself, it runs on the initial vagrant up.

You can add Drupal VM as a dependency with composer require, and this will probably become the default - like BLT, it's considered more complex this way, but it makes ongoing maintenance easier. (As a bonus, there is a new "scaffolding" method in the pipeline for smoother Vagrantfile setup.)

acquia/reservoir

This one falls into a different category. It's not so much a starting point for a new Drupal site, rather than a highly constrained Drupal build that does one job well - be a headless content repository. It's a very tight and clean install.  In this case you would start your project with acquia/reservoir-project and probably make only minimal changes.

Summary

After trying a few methods, I've settled on drupal-composer/drupal-project as the way to go. Even if I need BLT, I prefer to control the way that BLT is added or removed.

Step 2: Create a project

Let's create a project and then break it down. Following the drupal-composer/drupal-project instructions, we'll create a project. The resulting composer.json will probably look just like this.

I want to break it down and show how I have customised it for Lil Engine's site website repo. I think this exercise is a useful introduction to Composer.

General options

Change the name, description and authors, it's your new project. The rest of these settings remain unchanged. Here is the before and after.

Initial

"name": "drupal-composer/drupal-project",
    "description": "...",
    "type": "project",
    "license": "GPL-2.0+",
    "authors": [
        {
            "name": "",
            "role": ""
        }
    ],
    …
    "minimum-stability": "dev",
    "prefer-stable": true,

Customised

"name": "lilengine/web",
    "description": "Lil Engine website",
    "type": "project",
    "license": "GPL-2.0+",
    "authors": [
        {
            "name": "Si Hobbs",
            "role": "Developer"
        },
        {
            "name": "Campbell Tilley",
            "role": "Developer"
        },
    ],
    …
    "minimum-stability": "dev",
    "prefer-stable": true,

 

"repositories"

I've named the key for Drupal packages repository "drupal", which is common practice. We will add many more repositories (mostly for Javascript libraries we want to import) and this keeps things clear.

Initial

"repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ],

Customized

  "repositories": [
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        },
    ],

"require"

The require section contains the tools that you would expect to see on your production system. No custom modules or themes, just Drupal 8.

drupal/core - This isn't actually Drupal, it's a fork of Drupal that integrates better with composer - thanks to Florian Weber and the Drupal Composer working group.

drush/drush - Drupal shell client, every devops old friend.

drupal/console - Drupal console client, every devops new friend.

composer/installers - Many pre-composer PHP projects have whacky file systems for plugins, rather than the contempary ./vendor directory. Composer Installers gives us a way to put modules, themes and libraries into the appropriate directories. 

cweagans/composer-patches - Keeping track of your bugs has never been easier, this allows you to keep a list of patch URLs in your composer.json.

drupal-composer/drupal-scaffold - Tells Composer how to manage Drupal core updates, all those extra files like robots.txt and index.php.

webflo/drupal-finder and webmozart/path-util - These are utility classes for the ScriptHandler.php (scripts that run on composer update/install events).

"require-dev"

The require dev section contains the baseline testing tools, a known working combination of PHPUnit, Behat, and other drivers.

"scripts"

The default scripts here are provided by drupal-composer/drupal-project to make the process of setting up and updating Drupal core easier. If you see something like:

"scripts": {
  "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
  "random-script": "DrupalComposer\\DrupalScaffold\\Plugin::random"
}

Then you can run composer run-script random-script to execute those scripts.

I do not usually add additional scripts to this section, even though I add custom scripts into the ./scripts directory. In some ways it seems like a good idea to do it this way.

"config"

There are a large number of config options for Composer. You can browse them all at Composer.org

The "sort-packages" option is set to true in our composer.json, this is handy when your composer.json gets really long. If you are going to edit your composer.json by hand, and don't want strict ordering, you should set this to "false".

Summary

That's a first look at our new composer.json. In the next section we will look at some tips and tricks, which will introduce some of the other settings.

Step 3: My preferences

I want to show the rest of the composer.json from drupal-composer/drupal-project by looking at how you might use it. 

Control "scaffolding" of core updates

When you update Drupal core with composer, a "drupal-scaffold" script will update all the random files that "ship with Drupal", but which are not inside the Drupal package (they are not in the ./core directory).

For lilengine.co I added some configuration to the "extra" section of the composer.json. My preference is to let the plugin update all the files, but that I don't want it to update the example files (just a personal thing we can discuss over beer). If you were modifying robots.txt, or other files, you would add them here too. There is more documentation at drupal-project/drupal-scaffold.

"extra": {
  ...
  "drupal-scaffold": {
    "excludes": [
  	"sites/default/default.services.yml",
  	"sites/default/default.settings.php",
  	"sites/example.settings.local.php",
  	"sites/example.sites.php"
    ],
    "omit-defaults": false
  }

Prevent Composer getting certain package versions

When you have a large chain of composer dependencies in your project (package A requires package B requires package C) you can sometimes get packages you don't want.
I had a situation where a BLT update was pulling Drupal 8.3.5, but this broke our build for reasons I forget. It wasn't our composer.json that requried drupal/core, so we couldn't change our require statement. To keep our build running, we added:

    "conflict": {
        "drupal/core": "8.3.5"
    },

Since 8.3.4 still satisfied our requirements, Composer settled on that. It's not the only way we could have solved this, we could have created an upstream patch on BLT, but this was a temporary problem and called for a temporary solution.

You'll also see that composer.json already has a conflict with drupal/drupal, which is the vanilla Drupal core. You'll remember that we use drupal/core for better Composer support. This prevents breaking the composer build by adding a requirement (directly or indirectly) on the wrong Drupal package.

"conflict": {
  "drupal/drupal": "*"
},

Conclusion

That's a very high level look how to start your Drupal project with Composer, and the contents of the composer.json. I recommend you have a read of the links at the top of the article, because are some great tips for making the most of your Composer in your workflow.

Add new comment

The content of this field is kept private and will not be shown publicly.

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.

Comments

  • Allowed HTML tags: <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <p>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
  • Use [gist:#####] where ##### is your gist number to embed the gist
    You may also include a specific file within a multi-file gist with [gist:####:my_file].

Spread the word