Removing BLT on Acquia Cloud: How to Get Started

  • Last updated
  • 2 minute read

Goal

This document addresses the most common uses of BLT and provides guidance in how to replace them. If you use more advanced BLT feature you can reach out to Acquia PS.

Overview

BLT has been an invaluable tool for developers, offering a structured and opinionated approach to navigating various workflows in the development cycle. It provided a suite of commands such as sync, deploy, and others, along with Cloud hooks to automate tasks post-code deployment or database copy. Additionally, BLT's "command hooks" allowed users to extend functionality as needed, such as building frontend requirements and assets.

Acquia BLT also introduced some settings "magic," enabling configuration splits, including Memcached configuration, and the addition of extra configuration-oriented files through organized *.settings.php files. While our documentation provides basic examples and reflects some of the necessary changes, this guide aims to be a comprehensive resource to help you transition smoothly from BLT.

Using ACLI and Composer Scripts

For most cases, the tools that you are going to need are ACLI and Composer scripts. Here are some useful links:

It is important to make sure ACLI is installed and configured correctly. Typing lando acli, ddev acli, fin acli will confirm that ACLI is working correctly

  1.  The BLT Settings goodies

    To continue benefiting from the configuration enhancements of BLT, Acquia recommends using the Drupal Recommended Settings Plugin (DRS). In the linked docs, you can also find specific information on migrating this part of BLT to the DRS plugin. Once the migration is done, you should have most of the functionality of BLT already in your codebase. If you are using Acquia Site Factory, check the specific section about using DRS with ACSF.

     

    • Confirm that you are on the latest version of BLT 13.
    • Install the new BLT 14:
    composer require acquia/blt:^14
    • Run the blt migrate command:
    ./vendor/bin/blt blt:migrate
    • You should see this message:
     [WARNING] This script will update following files from site [default] with following changes.
    
    
     -------------------- ----------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------
      File                 Snippet to remove                                                             Snippet to add
     -------------------- ----------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------
      settings.php         require DRUPAL_ROOT . "/../vendor/acquia/blt/settings/blt.settings.php";      require DRUPAL_ROOT . "/../vendor/acquia/drupal-recommended-settings/settings/acquia-recommended.settings.php";
      settings.php         /**                                                                           /**
                            * IMPORTANT.                                                                  * IMPORTANT.
                            *                                                                             *
                            * Do not include additional settings here. Instead, add them to settings      * Do not include additional settings here. Instead, add them to settings
                            * included by `blt.settings.php`. See BLT's documentation for more detail.    * included by `acquia-recommended.settings.php`. See Acquia's documentation for more detail.
                            *                                                                             *
                            * @link https://docs.acquia.com/blt/                                          * @link https://docs.acquia.com/
                            */                                                                            */
      settings.php         use Acquia\Blt\Robo\Common\EnvironmentDetector;                               use Acquia\Drupal\RecommendedSettings\Helpers\EnvironmentDetector;
      local.settings.php   blt_override_config_directories                                               drs_override_config_directories
     -------------------- ----------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------
    
    • Respond yes.
    • Celebrate the beginning of the removal of BLT. 🎉
  2. Syncing the site(s) down [BLT sync and BLT dsa commands]

    BLT provides ways to pull the site down and files with blt sync (dsa) command:

    Description:
      Synchronize local env from remote (remote --> local).
    
    Usage:
      drupal:sync:default:site [options]
      ds
      drupal:sync
      drupal:sync:default
      sync
      sync:refresh
    
    Options:
          --sync-public-files
          --sync-private-files
      -h, --help                     Display help for the given command. When no command is given display help for the list command
      -q, --quiet                    Do not output any message
      -V, --version                  Display this application version
          --ansi|--no-ansi           Force (or disable --no-ansi) ANSI output
      -n, --no-interaction           Do not ask any interactive question
      -D, --define=DEFINE            Define a configuration item value. (multiple values allowed)
          --environment=ENVIRONMENT  Set the environment to load config from blt/[env].yml file.
          --site=SITE                The multisite to execute this command against.
      -v|vv|vvv, --verbose           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
    
    Help:
      Copies remote db to local db, re-imports config, and executes db updates
      for each multisite.

     

    To replace this action we are going to need ACLI, and the pull:database command.

     

    While BLT uses configuration to determine the source environment in blt.yml, we are not going to cover that here, instead we will call the environment directly. The command is simply:

    acli pull:db [site].[env]

    This will clear caches and sanitize the database afterward. However, there are some limitations compared to BLT:

    • BLT uses Drush directly, and you could try the same, but in this example, ACLI will only copy the latest backup of the database. This can be solved by using --on-demand when calling the command. ACLI will create a backup and then pull it down.
    • BLT will run update:db and config:import right after pulling the database down, which is very important if you are testing your production database against your current code. For this, we are going to use bash scripts and Composer scripts.

    Lets do this! We can use this example scripts to run a combination of commands to have similar functionality. All examples assume all scripts are in the scripts folder.

    Full disclosure: I am not a BASH expert, so these scripts were AI-aided, and while I worked to make sure they are right. These scripts are not officially supported by Acquia, and serve only as examples.

    The scripts we use depend on whether the application is single or multisite.

    Using ACLI directly and having a composer script to run Drush commands (Single site).

    Run the ACLI command.

    acli pull:db [site].[env]

    Add a composer script to your composer.json to run Drush commands.

      "scripts": {
        "post-acli-pull-database": [
          "./scripts/drush-common.sh site.prod"
      ]
      }

     

    Using the executor script (Single site or Multisite)

    In the example scripts I mention early there is a folder that allows the scripts to run in a "modular" form; We have 3 of them:

    • The executor.sh Orchestrates the call.
    • The pull-db.sh pulls the DB using ACLI.
    • The drush-common.sh runs the necessary drush commands.

    There are 2 versions of the executor script: one for single sites, another for multisites. Both are called with a single parameter which is the [site].[env] which indicates where to get the DBs from, the command: pulldb or drush-common.

     

    Copy these 3 scripts to you script folder and use the executor command that fits your application. Then you can run:

    ./scripts/executor pulldb site.dev

     

  3. The most common BLT Command hooks: frontend-reqs and frontend-assets

    The most common command hooks used with BLT are the ones needed to compile your theme automatically, before a deployment, when yoi pull the DB down, or anytime you need something locally. 

    In the example scripts you will find 2 scripts:

    • installthemes.sh that runs as the frontend-reqs.
    • compileassets.sh that runs as frontend-assets.

    These two scripts go through your theme/custom folder and run the command that you need to install the requirements or compile the theme. To use them we are going to create some composer scripts:

    {
      "scripts": {
        "frontend-reqs": [
          // This script installs dependencies for the custom themes.
          "./scripts/installthemes.sh"
        ],
        "frontend-assets": [
          // This script compiles the assets for the custom themes.
          "./scripts/compileassets.sh"
        ],
        "frontend-build": [
          // This script installs dependencies and compiles the assets for the custom themes.
          "@frontend-reqs",
          "@frontend-assets"
        ]
      }
    }

     

    Suggestion: You can call these commands after pulling database(s) to compile your theme immediately. 

    composer run-script frontend-build

     

  4. Blt deploy alternative

    BLT provided a deploy command, which was useful for customers using external CI products (in other words not Acquia Pipelines or Acquia Code Studio). To replace the deploy command we are going to use ACLI. Here are some considerations:

    • The recommended command is acli push:artifact.
    • There are some limitations for this command, basically on the compiled files for your theme (CSS and JS mostly). Read the caveat in our known issues with ACLI.
    • We will be using some Composer scripts and configs for this to work as expected.
    • We will take advantage of our frontend scripts.

     

    As of this writing, here is how it works (Note: I will update it if things change).

    Lets start:

    • Create a script called post-install-cmd in your composer.json to compile your theme assets.
     "scripts": {
        "frontend-reqs": [
          "./scripts/installthemes.sh"
        ],
        "frontend-assets": [
          "./scripts/compileassets.sh"
        ],
        "frontend-build": [
          "@frontend-reqs",
          "@frontend-assets"
        ],
        "post-install-cmd": [
          "@frontend-build"
        ]
      }
    • Add a Installer Path in your composer.json for the custom theme folder, such as
          "docroot/themes/custom/": [
            "type:drupal-custom-theme"
          ],

     

    Disclaimer: The Composer entries are a workaround until this issue is solved. The use of post-install-cmd makes the theme install and compilation run every time composer install is run, which can be a burden in your daily development tasks. There is an example here, which is a workaround that will enable you to have the frontend scripts run only when in a CI environment.

    • Finally, run the push:artifact command.
    acli push:artifact site.dev --no-interaction -v
    • Note: You can use the acli push:artifact parameters to personalize how the branches or tags are created in your Acquia repository:
    > acli help push:artifact
    Description:
      Build and push a code artifact to a Cloud Platform environment
    
    Usage:
      push:artifact [options] [--] [<environmentId>]
      push:artifact [<environmentAlias>]
      push:artifact myapp.dev
      push:artifact prod:myapp.dev
      push:artifact 12345-abcd1234-1111-2222-3333-0e02b2c3d470
      push:artifact --destination-git-branch=main-build
      push:artifact --source-git-tag=foo-build --destination-git-tag=1.0.0
      push:artifact [email protected]:example.git [email protected]:example.git --destination-git-branch=main-build
    
    Arguments:
      environmentId                                        The Cloud Platform environment ID or alias (i.e. an application and environment name optionally prefixed with the realm)
    
    Options:
          --dir=DIR                                        The directory containing the Drupal project to be pushed
          --no-sanitize                                    Do not sanitize the build artifact
          --dry-run                                        Deprecated: Use no-push instead
          --no-push                                        Do not push changes to Acquia Cloud
          --no-commit                                      Do not commit changes. Implies no-push
          --no-clone                                       Do not clone repository. Implies no-commit and no-push
      -u, --destination-git-urls=DESTINATION-GIT-URLS      The URL of your git repository to which the artifact branch will be pushed. Use multiple times for multiple URLs. (multiple values allowed)
      -b, --destination-git-branch=DESTINATION-GIT-BRANCH  The destination branch to push the artifact to
      -t, --destination-git-tag=DESTINATION-GIT-TAG        The destination tag to push the artifact to. Using this option requires also using the --source-git-tag option
      -s, --source-git-tag=SOURCE-GIT-TAG                  The source tag from which to create the tag artifact
      -h, --help                                           Display help for the given command. When no command is given display help for the list command
      -q, --quiet                                          Do not output any message
      -V, --version                                        Display this application version
          --ansi|--no-ansi                                 Force (or disable --no-ansi) ANSI output
      -n, --no-interaction                                 Do not ask any interactive question
      -v|vv|vvv, --verbose                                 Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

     

  5. The Post deploy code hook

    BLT provided two different types of hooks for your repository: one for Acquia Site Factory and another for Acquia Cloud Classic. I have added a couple of examples for each one of these.

    If you are on Acquia Cloud Classic you should be moving soon to Acquia Cloud Next, which is recommended and you should use Acquia Cloud Actions to execute the tasks that before were done by the post-deploy-code hook. The examples here are primarily for Acquia Cloud Classic.

    Post Deploy code for Acquia Site Factory

    BLT replaced the post-site-install and the db-update Factory Hooks to run the drupal:update task, I have some example scripts here.

    Note: This scripts are an example and should be tested before going into production.

    This is just an example, use the one from the repo for the latest fixes.

    #!/bin/bash
    #
    # Factory Hook: db-update
    #
    # This is an example script to perform necessary tasks using Drush
    # commands during a database update on ACSF.
    # Note: This is a starting point and may need customization.
    #
    # Usage: db-update.sh sitegroup env db-role domain custom-arg
    
    # Exit immediately on error and enable verbose log output.
    set -ev
    
    # Map the script inputs to convenient names:
    sitegroup="$1"
    env="$2"
    db_role="$3"
    domain="$4"
    
    # Get the internal domain name based on the site, environment, and db role arguments
    uri=$(/usr/bin/env php /mnt/www/html/$sitegroup.$env/hooks/acquia/uri.php $sitegroup $env $db_role)
    IFS='.' read -a name <<< "$uri"
    
    # Drush executable
    drush="/mnt/www/html/$sitegroup.$env/vendor/bin/drush"
    
    echo "Running Drush deploy tasks on $uri domain in $env environment on the $sitegroup subscription."
    
    # Execute the updates using Drush.
    $drush --uri=$domain/ updatedb -y
    result=$?
    [ $result -ne 0 ] && exit $result
    
    $drush --uri=$domain/ config-import -y
    result=$?
    [ $result -ne 0 ] && exit $result
    
    $drush --uri=$domain/ config-import -y
    result=$?
    [ $result -ne 0 ] && exit $result
    
    # Check config status
    config_status_output=$($drush --uri=$domain/ config-status 2>&1)
    if [[ "$config_status_output" != *"No differences"* ]]; then
      echo "Error: Config status check failed for site: $domain"
      echo "Details: $config_status_output"
      exit 1
    fi
    
    $drush --uri=$domain/ cache-rebuild
    result=$?
    [ $result -ne 0 ] && exit $result
    
    $drush --uri=$domain/ deploy:hook -y
    result=$?
    [ $result -ne 0 ] && exit $result
    
    # Clean up the Drush cache directory
    echo "Removing temporary Drush cache files."
    # Add any necessary cleanup commands here, for example:
    # rm -rf /tmp/drush*
    
    set +v
    
    # Exit with the status of the last Drush command
    exit $result

     

    Post code deploy hook for Acquia Cloud Classic

    BLT creates a post deploy code to ensure that Drush commands are run after new code is deployed. Remember, If you move into Acquia Cloud Next, this is done via Acquia Cloud Actions.

    I have created and example here, test this code to see if it works for you. There are two examples:

    Here is the example of a monolithic hook:

    
    #!/bin/bash
    
    # Set the root directory relative to the script location
    HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    DRUPAL_ROOT="$(dirname "$(dirname "$HOOKS_DIR")")/docroot"
    
    # Check if DRUPAL_ROOT exists and is a directory
    if [ ! -d "$DRUPAL_ROOT" ]; then
      echo "Error: DRUPAL_ROOT directory does not exist: $DRUPAL_ROOT"
      exit 1
    fi
    
    # Set the path to the Drush command
    DRUSH_CMD="$(dirname "$(dirname "$HOOKS_DIR")")/vendor/bin/drush"
    
    # Check if the Drush command exists and is executable
    if [ ! -x "$DRUSH_CMD" ]; then
      echo "Error: Drush command not found or not executable: $DRUSH_CMD"
      exit 1
    fi
    
    # Function to run Drush command for a given multisite
    run_drush_for_multisite() {
      local site_path=$1
      local command=$2
      echo "Running Drush command for site: $site_path: $command"
      result=$($DRUSH_CMD --root="$DRUPAL_ROOT" --uri="$site_path" "$command" -y 2>&1)
      echo "$result"  # Debug statement to see the output
      # Check if the Drush command succeeded
      if [ $? -ne 0 ]; then
        echo "Error: Drush command '$command' failed for site: $site_path"
        exit 1
      fi
    
      # Special handling for config-status
      if [ "$command" == "config-status" ]; then
        if ! printf "%s" "$result" | grep -q "No differences"; then
          echo "Error: Config status check failed for site: $site_path"
          echo "Details: $result"
          exit 1
        fi
      fi
    }
    
    # Detect multisites by looking for settings.php files
    MULTISITE_URIS=()
    
    # Always ensure 'default' site is included
    if [ -f "$DRUPAL_ROOT/sites/default/settings.php" ]; then
      MULTISITE_URIS+=("default")
    else
      echo "Error: Default site settings.php not found in $DRUPAL_ROOT/sites/default"
      exit 1
    fi
    
    for site_path in "$DRUPAL_ROOT/sites"/*/; do
      if [ -f "${site_path}settings.php" ]; then
        site_uri=$(basename "$site_path")
        # Exclude 'default' as it's already added
        if [ "$site_uri" != "default" ]; then
          MULTISITE_URIS+=("$site_uri")
        fi
      fi
    done
    
    # Define the Drush commands to be executed for each multisite
    DRUSH_COMMANDS=(
      "updatedb"
      "config-import"
      "config-import"
      "config-import"
      "config-status"
      "cache-rebuild"
      "deploy:hook"
    )
    
    # Run the Drush commands for each detected multisite
    for site in "${MULTISITE_URIS[@]}"; do
      for command in "${DRUSH_COMMANDS[@]}"; do
        run_drush_for_multisite "$site" "$command"
      done
    done
    
    echo "Drush commands completed for all sites."
    

     

     

     

  6. External CI services

    Some customers want to use their own CI server: GitHub Actions, Gitlab CI, Azure Pipelines, etc. Here are some considerations:

    • All acli commands must be authenticated via acli auth:login.
    • If possible make sure it runs on an Ubuntu Box, if not, any Linux box will do.
    • On the build stage, install ACLI per our documentation.
    • Do not forget to also install node, NPM, and anything needed for theme compilation.
    • On the test stage, run any test you are using. You can also test pulling down your production database against your new code to ensure a successful deployment. Make sure your CI box has MySQL configured and you can connect to it from Drupal; this sometimes requires creating a manual settings.local.php.
    • For the deployment stage use acli push:artifact.

    If you use Github Actions and Lando or DDEV, and you have everything working on your local using lando|ddev acli, and you can run every command that you need to have a successful artifact in Acquia.

    You can use the official actions for Lando and DDEV to make your life easier.