Executing Drush commands from a Phing build file

Automating repetitive tasks doesn't only give a satisfying feeling; it also prevents mistakes that you can easily overlook. Let's take setting up your development environment when working on a Drupal project, which is a process you ideally should do every time you start working on something else. For instance, when starting to implement a feature, solving a bug, or loading in someone else's work for a peer review. By scripting this procedure, you ensure that a new database is loaded, all updates and configuration changes are applied, the caches are cleared/rebuild, a one-time login is created, et cetera.

One of the many tools available to run scripted tasks is Phing. Phing is similar to Apache Ant, uses XML files to describe tasks, runs on Windows, Mac and Linux, and can be added to your Drupal project using composer require phing/phing. Like many Drupal developers, I use Drush to communicate to sites from the command-line. Below, I've added an example build file to illustrate the target I use to invoke Drush commands using Phing. First, general properties are defined and a target to see which commands are available (i.e., list-commands, also shown when invoking phing without command-line arguments). Then, the drush target to invoke either drush @environment <command> [arguments] or drush <command> @source @destination [arguments]. Finally, to complete the example, the target user:login is provided, which uses the drush target to generate a one-time login link.

<?xml version="1.0" encoding="UTF-8"?>

<project name="Drush target" description="Example to illustrate invoking Drush commands in a Phing script." default="list-commands">

  <!-- Generic properties, for a local Drupal project. -->
  <property name="dir.project" value="${project.basedir}" />
  <property name="dir.vendor" value="${dir.project}/vendor" />
  <property name="dir.bin" value="${dir.vendor}/bin" />
  <property name="dir.web" value="${dir.project}/web" />
  <property name="drush.path" value="${dir.bin}/drush" />
  <property name="phing.path" value="${dir.bin}/phing" />

  <!-- Default target that lists all available phing commands. -->
  <target name="list-commands" hidden="true">
    <exec command="${phing.path} -l" passthru="true" />
  </target>

  <!-- Execute a Drush command.

       The command execute depends on the parameters set, either in the form of:
       - drush @environment <command> [arguments]
       - drush <command> @source @destination [arguments]

       @property environment
         The environment that the Drush command should be executed to. Normally, this property should always be set,
         however, it is optional when both the source and destination properties are set.
       @property source
         (Optional) When set this environment acts as the source. In case the destination property is not set, the
         environment property will be used as destination value.
       @property destination
         (Optional) When set this environment acts as the destination. In case the source property is not set, the
         environment property will be used as the source value.
       @property command
         The Drush command to invoke.
       @property arguments
         (Optional) Arguments that should be provided as options to the command.
  -->
  <target name="drush" hidden="true">
    <!-- Set default values for the optional properties, without overwriting the value if has already been set. -->
    <property name="source" value="" />
    <property name="destination" value="" />
    <property name="arguments" value="" />

    <!-- Determine whether there are source and destination parameter for the command. -->
    <if>
      <and>
        <not><equals arg1="${source}" arg2="" /></not>
        <equals arg1="${destination}" arg2="" />
      </and>
      <then>
        <property name="destination" value="${environment}" override="true" />
      </then>
      <elseif>
        <and>
          <equals arg1="${source}" arg2="" />
          <not><equals arg1="${destination}" arg2="" /></not>
        </and>
        <then>
          <property name="source" value="${environment}" override="true" />
        </then>
      </elseif>
    </if>

    <!-- Invoke the appropriate command based on whether the target property has been set. -->
    <if>
      <and>
        <equals arg1="${source}" arg2="" />
        <equals arg1="${destination}" arg2="" />
      </and>
      <then>
        <echo>Performing 'drush &#64;${environment} ${command} ${arguments}'</echo>
        <exec command="${drush.path} &#64;${environment} ${command} ${arguments}" dir="${dir.web}" passthru="true" />
      </then>
      <else>
        <echo>Performing 'drush ${command} &#64;${source} &#64;${destination} ${arguments}'</echo>
        <exec command="${drush.path} ${command} &#64;${source} &#64;${destination} ${arguments}" dir="${dir.web}" passthru="true" />
      </else>
    </if>
  </target>

  <!-- Generate a one time login link. -->
  <target name="user:login" description="Generate a one-time login link for the local development environment.">
    <!-- The property should be set here, since it could already be provided by another phingcall invocation. -->
    <property name="environment" value="self" />

    <phingcall target="drush">
      <property name="command" value="user:login" />
      <property name="arguments" value="--name &quot;<name>&quot;" />
    </phingcall>
  </target>

</project>

For the drush target to work, the environment property should be set (use self to indicate the local environment's alias). In case the Drush command involves two environments, e.g., when using drush rsync,  the source and/or destination properties should be set (for the one left empty, the value of the environment is used).

Category
Phing
Drupal
Drush
Development environment setup

Plain text

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