BLT

Extending and overriding BLT

Note

Acquia will end support for BLT on December 31, 2024. For more information on how to replace your BLT implementation with updated functionality, see You don’t need BLT on Acquia Cloud.

BLT provides an extensive plugin and configuration system to support customization.

Adding a custom Robo hook or command

BLT uses Robo and the Annotated Command library to define commands. You can use annotated commands to define new custom commands or hook into existing commands, such as executing custom code before or after an existing BLT command.

You can generate a file containing an example custom command and hook by running the following command:

blt recipes:blt:command:init

You can place custom commands in a different directory in your project or even a separate Composer package, as long as you expose the command file using PSR4.

To create your own Robo PHP command or hook, complete the following steps:

  1. Create a new file named using the required pattern *Commands.php. For instance, the generated example uses the file name ExampleCommands.php.
  2. Use a namespace ending in *\Blt\Plugin\Commands exposed using PSR4 in your composer.json file. For instance, the generated example uses the namespace Example\Blt\Plugin\Commands.
  3. Follow the Robo PHP Getting started guide to write a custom command. For a list of all available hook types, see Annotated Command’s hook types.

Replacing or overriding a Robo command

To replace a BLT command with your own custom version, use the replace command annotation for your custom command.

Note

If you replace a BLT command, you take responsibility for maintaining your custom command. Your command may break when changes are made to the upstream version of the command in BLT itself.

Disabling a command

You can disable any BLT command. This will cause the target to be skipped during the normal build process. To disable a target, add a disable-targets key to your blt.yml file, as follows:

disable-targets:
  tests:
    phpcs:
      sniff:
        all: true
        files: true

This snippet causes BLT builds to skip the tests:phpcs:sniff:all and tests:phpcs:sniff:files targets.

Adding or overriding filesets

You can also define your own custom filesets or override existing filesets.

Note

To change the behavior of PHPCodeSniffer, see the documentation on the tests:phpcs:sniff:all command in the following section.

You can generate a file with an example custom fileset by running the following command:

blt recipes:blt:filesystem:init

You can place custom commands in a different directory in your project or even a separate Composer package, as long as you expose the command file using PSR4.

To create your own fileset definition, complete the following steps:

  1. Create a new file named using the required pattern *Filesets.php. For instance, the generated example uses the file name ExampleFilesets.php.
  2. Use a namespace ending in *\Blt\Plugin\Filesets exposed using PSR4 in your composer.json file. For instance, the generated example uses the namespace Example\Blt\Plugin\Filesets.
  3. Create a public method in the Filesets class in the generated file.
  4. Add a Fileset annotation to your public method, specifying its id:

    @fileset(id="files.yaml.custom")
    
  5. Instantiate and return a Symfony\Component\Finder\Finder object. The files found by the finder form the fileset.

To change the filesets used in commands such as tests:twig:lint:all, tests:yaml:lint:all, and tests:php:lint, add the following configuration key to blt/blt.yml:

validate:
  yaml:
    filesets:
      - files.yaml.custom

Overriding the environment detector

BLT includes a unified environment detector class providing information about the current hosting environment such as the stage (dev, stage, or prod), provider (Acquia or Pantheon), and type (local or CI). The environment detector primarily examines environment variables and system configuration files to give details about the current environment.

You can extend the environment detector to support custom environments or override the detection behavior of built-in environments.

Note

Page requests (due to settings.php includes) and BLT commands, can both invoke the environment detector, so performance is critical. The detector can’t depend on a UI, the Drupal container, or Robo configuration.

To override environment detector methods, create a new BLT plugin as follows:

  1. Create a new custom environment detector class implementing BLT’s environment detector.
  2. Override any supported method in your custom class.
  3. Expose your custom class using PSR4 and add your class to Composer’s classmap in your plugin’s composer.json file.

BLT’s Environment Detector will discover your overrides using PSR4 and re-dispatch any method calls to your custom implementation.

As a reference implementation, the BLT Tugboat plugin illustrates how to override the Environment Detector in practice.

For more discussion on the Environment Detector architecture, design choices, and performance considerations, see this issue.

Modifying your BLT configuration

You can customize your BLT configuration by overriding the value of default variable values. The build.yml file contains the default values of all BLT variables.

Overriding a variable value

BLT loads configuration values from the following list of YAML files, in the listed order:

  1. vendor/acquia/blt/config/build.yml
  2. blt/blt.yml
  3. blt/[environment].blt.yml
  4. docroot/sites/[site]/blt.yml
  5. docroot/sites/[site]/[environment].blt.yml

Values loaded from the later files will overwrite values in earlier files.

Note

If you want to override a non-empty value with an empty value, the override value must be set to null, and not '' or [].

Overriding variables project-wide

You can override any variable value by adding an entry for that variable to your blt/blt.yml file. This change will be committed to your repository and shared by all developers for the project. For example:

behat.tags: @mytags

Overriding variables locally

You can override a variable value for your local computer in the same way that you can for specific environments.

For instructions about how to do this, see the following section, and use local for the environment value.

Overriding variables in specific environments

You can override a variable value for specific environments (such as the local or ci environments) by adding an entry for the variable to a file named in the pattern of [environment].blt.yml (for example, ci.blt.yml).

BLT detects only the local and ci environments. You can pass --environment as an argument to BLT to specify the correct environmental configuration to load.

Overriding variables at runtime

You may overwrite a variable value at runtime by specifying the variable value in your blt command using the argument syntax -D [key]=[value]. For example:

blt tests:behat:run -D behat.tags='@mytags'

For configuration values that are indexed arrays, you can override individual values using the numeric index, such as git.remotes.0.

Modifying the build artifact

To modify the behavior of the artifact:build target, you may override BLT’s deploy configuration. For example, review the deploy key in the build.yml file.

You can customize which files are committed to the build artifact using two general methods:

  • An exclude file that prevents certain files from being copied into the build artifact
  • A gitignore file that prevents files in the build directory from being committed to Git

Excluding files from the build artifact

This is the preferred method of modifying which files are included in the build artifact. It prevents files from being copied from the source directory to the build artifact.

To change which files are rsynced to the artifact, provide your own deploy.exclude_file value in the blt.yml file. For example, review the upstream deploy-exclude.txt file:

deploy:
   exclude_file: ${repo.root}/blt/deploy/rsync-exclude.txt

If you want to add to the upstream deploy-exclude.txt file instead of overriding it, you don’t need to define your own deploy.exclude_file. Instead, leverage the deploy-exclude-additions.txt file found under the top-level BLT directory by adding each file or directory you want to exclude on its own line. For example:

/directorytoexclude
excludeme.txt

Ignoring build artifact files using gitignore

Files that are generated by Composer (such as contributed Drupal modules) should be excluded from the build artifact by using a custom gitignore file.

To change which files Git must ignore in the artifact, provide your own deploy.gitignore_file value in the blt.yml file. For example, review the upstream .gitignore file.

deploy:
   gitignore_file: ${repo.root}/blt/deploy/.gitignore

Additional build artifact customization

If neither of the above methods work, you can modify any aspect of the artifact after it is built by using the post-deploy-build hook or postArtifactBuild command, as described below.

Pre and post command hooks

BLT provides two types of hooks for running custom tasks before, during, or after a built-in command.

BLT native command hooks

Some built-in commands that expect user modification explicitly call command hooks. Such values can be overridden using the command-hooks key in the blt.yml file.

The currently-available hooks are:

  • post-setup-build
  • post-deploy-build
  • pre-config-import
  • post-config-import
  • frontend-assets
  • frontend-reqs
  • frontend-test

For example, once the artifact is built, you can execute a custom command by providing your own command-hooks.post-deploy-build.dir and command-hooks.post-deploy-build.command values in the blt.yml file.

# Executed after deployment artifact is created.
post-deploy-build:
   dir: ${deploy.dir}/docroot/profiles/contrib/acquia_cms
   command: npm install

Robo annotated command hooks

BLT is built using Robo, which provides the Annotated Command system. Annotated commands allow you to hook into _any_ BLT command, but require the called code to be provided via a custom command rather than the blt.yml file.

For example, you can accomplish the same task of modifying the build artifact as in the previous section using an annotated command hook.

/**
* This will be called after the artifact:build command.
*
* @hook post-command artifact:build
*/
public function postArtifactBuild() {
   $this->doSomething();
}

For more information, see the main section on Robo annotated commands.

Modifying Git hooks

You may disable a Git hook by setting its value under git.hooks to false:

git:
   hooks:
      pre-commit: false

You can use a custom Git hook in place of BLT’s default Git hooks by setting its value under git.hooks to the directory path containing the hook. The directory must contain an executable file named after the Git hook:

git:
   hooks:
      pre-commit: ${repo.root}/my-custom-git-hooks

In the preceding example, an executable file named pre-commit should exist in ${repo.root}/my-custom-git-hooks.

Execute blt blt:init:git-hooks after modifying these values for changes to take effect. As most projects will already have a git key in their blt.yml file, ensure that you append hooks to the existing key.

By default, BLT will execute the internal:git-hook:execute:commit-msg command when new Git commits are made. This command validates that the commit message matches the regular expression defined in git.commit-msg.pattern. You can override the default configuration.

Modifying tests

You can modify the following test targets:

  • tests:behat:run: To modify the behavior of the tests:behat:run target, you may override BLT’s Behat configuration. For examples, review the build.yml file.
  • tests:phpcs:sniff:all: To change the behavior of the tests:phpcs:sniff:all target, you may copy phpcs.xml.dist to phpcs.xml in your repository’s root directory and modify the XML. For more information, see the official PHPCS documentation.
  • tests:twig:lint:all: To prevent validation failures on any Twig filters or functions created in custom or contrib module twig.extension services, add filters and functions as follows:

    validate:
      twig:
        filters:
          - my_filter_1
          - my_filter_2
        functions:
          - my_function_1
          - my_function_2