Drush in the Composer Era

32
Drush in the Composer Era 1 Photo © 2012 by Charlie Nguyen , CC BY 2.0

Transcript of Drush in the Composer Era

Page 2: Drush in the Composer Era

2

OverviewUsing Composer Dependency Hell Site-Local Drush

Configuration Filter The FutureDrush Extensions

Page 3: Drush in the Composer Era

It is now possible to use Composer to add code directly to Drupal:

drush dl drupal-8cd drupal-8composer require drupal/og:8.*

composer update will not update drupal/core; use drush pm-update drupal for this.

3

Standard Composer used in D8

Page 4: Drush in the Composer Era

web├── autoload.php├── composer.json├── core│ └─ composer.json├── index.php└── vendor └─ autoload.php

4

wikimedia/composer-merge-plugin

{ "name": "drupal/drupal", "require": { "composer/installers": "^1.0.21", "wikimedia/composer-merge-plugin": "^1.3.0" }, "extra": { "_readme": [ "By default Drupal loads the autoloader from ./vendor/autoload.php.", "To change the autoloader you can edit ./autoload.php." ], "merge-plugin": { "include": [

"core/composer.json" ], "recurse": false, "replace": false, "merge-extra": false } },}

Page 5: Drush in the Composer Era

Uses “split core” to manage Drupal core files:

composer create-project drupal-composer/drupal-project:8.x-dev my-site-name --stability dev --no-interaction

5

Drupal 8: “drupal-project”

Page 6: Drush in the Composer Era

project├── composer.json├── vendor│ └── autoload.php└── web ├── autoload.php ├── core │ └── composer.json └── index.php

6

drupal-project Layout{ "name": "drupal-composer/drupal-project", "repositories": [ { "type": "composer", "url": "https://packagist.drupal-composer.org" } ], "require": { "composer/installers": "^1.0.20", "drupal/core": "8.0.*", "drush/drush": "dev-master", "drupal/console": "dev-master" }, "extra": { "installer-paths": { "web/core": ["type:drupal-core"], "web/modules/contrib/{$name}": ["type:drupal-module"], "drush/contrib/{$name}": ["type:drupal-drush"] } }}

Page 7: Drush in the Composer Era

7

Drupal 7: Example CI Scriptsexample-drupal7-circle-composer

example-drupal7-travis-composerCI

+

+

Page 8: Drush in the Composer Era

Use craychee/rootcanal to post-process your Composer install:

"config": { "bin-dir": "bin" }, "scripts": { "post-install-cmd": [ "bin/rootcanal" ], "post-update-cmd": [ "bin/rootcanal" ] }}

http://craychee.io/blog/2015/08/01/no-excuses-part3-composer/ 8

Drupal 7: rootcanal

Page 9: Drush in the Composer Era

Dependency Hell

9

Page 10: Drush in the Composer Era

The Multiple Autoloaders Problem

10

Two autoloaders may directly or indirectly include the same dependent library.

Drupal 8require autoload.php

Badly-Behaved Drush Extensionrequire autoload.php

"name": "symfony/event-dispatcher","version": "v2.7.5",

"name": "symfony/event-dispatcher","version": "3.*",

Page 11: Drush in the Composer Era

Version Mismatch Breaks

11

Drupal\Component\EventDispatcher

Event

isPropagationStoppedstopPropagation

symfony/event-dispatcher v2.7.5 symfony/event-dispatcher v3.x

public function dispatch($event_name, Event $event) { $event->setDispatcher($this);

Event

isPropagationStoppedstopPropagationsetDispatchergetDispatchergetNamesetName

Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher.dispatch

Page 12: Drush in the Composer Era

Site-Local Drush

12

Solution to Dependency Hell

Well-Behaved Drush Extensions

Page 13: Drush in the Composer Era

Site-Local Drush

13

Page 14: Drush in the Composer Era

Drush dispatch now happens in four phases:

14

Drush Dispatch

Drush Finder Drush Wrapper Drush Launcher Drush Application

Page 15: Drush in the Composer Era

15

Drush Finder

Responsible for finding the correct Drush to run

● Checks to see if it can find a Drupal site

● If the Drupal site contains a Drush script, use it

● If no Site-Local Drush is found, use global Drush

Does not look at alias files.

PHP Script

Page 16: Drush in the Composer Era

Optional. Located at the Drupal Root if it exists.

User may customize this script to:

● Add site-specific options (e.g. commandfile locations)

● Select the location of the Drush launcher (if in a non-standard location)

If there is no Drush Wrapper, then the Drush Finder will find and execute the Drush Launcher.

16

Drush Wrapper

Shell Script

Page 17: Drush in the Composer Era

17

Drush Launcher

Sets up the PHP operating environment.

● Select php.ini file to use

● Select php executable to use

● Passes info about environment to Drush

Always launches the Drush that is located next to it.

Shell Script

Page 18: Drush in the Composer Era

18

Drush Application

Contains all the code that is Drush.

● Load configuration and command file

● Parse site alias files

● Might dispatch again, e.g. if site alias is remotePHP Application

Page 19: Drush in the Composer Era

Manage Drush and Drupal

Place Drush and Drupal in the same composer.json file:

cd /path/to/drupal-8-rootcomposer require drush/drush:8.*

It is also possible to use site-local Drush with a non-Composer-managed Drupal site just to attach a specific version of Drush to that site:

cd /path/to/old/drupal-rootcomposer require drush/drush:6.*

19

CRITICAL

Page 21: Drush in the Composer Era

PROBLEM:

A Drush extension desires to use Composer Libraries, but also wants to work with both Composer-managed sites, and sites that do not use Composer.

SOLUTION:

Include a composer.json file as usual, and require libraries there. In your drush extension’s hook_drush_init, call drush_autoload(__FILE__).

21

Composer Libraries in Extensions

my.drush.inc:

function my_drush_init() { drush_autoload(__FILE__); }

Page 22: Drush in the Composer Era

IT’S SIMPLE.

If Drush has located a Drupal site that has an autoloader, then it assumes that all extensions that need an autoloader are part of the Drupal site’s composer.json file. Drush only loads Drupal’s autoload.php.

If the Drupal site does not have an autoloader, then Drush will find the extension’s autoloader, and load it.

AN EXTENSION CAN’T GET THIS RIGHT. ALWAYS USE drush_autoload.

22

drush_autoload Function

Page 23: Drush in the Composer Era

Global Extensions

23

Drush Extensions that use Composer libraries must be included locally as part of your Drupal project:

cd /path/to/projectcomposer require drupal/drush_iq

Policy files can continue to live in a global location, e.g. $HOME/.drush.

Page 24: Drush in the Composer Era

24

Include Global Extensions in Project

{ "name": "drupal/drupal", "require": { "composer/installers": "^1.0.21", "wikimedia/composer-merge-plugin": "^1.3.0", "drush/drush": "dev-master" }, "extra": { "merge-plugin": { "include": [ "/Users/ME/.drush-extensions/composer.json" ], "recurse": false, "replace": false, "merge-extra": false } },}

Drupal Project composer.json:

Global Extensions List composer.json:

$HOME└── .drush-extensions ├── composer.json ├── composer.lock ├── drush │ └── registry_rebuild │ ├── registry_rebuild.drush.inc │ └── registry_rebuild.php └── vendor └── autoload.php

Page 25: Drush in the Composer Era

Drush Configuration Filter

25

Page 26: Drush in the Composer Era

Configuration Workflow

26

Code and Content move in only one direction, but Configuration can move in either direction.

Page 27: Drush in the Composer Era

To keep development modules out of exported configuration, define:

__ROOT__/drush/drushrc.php:$command_specific['config-export']['skip-modules'] = array('devel');

$command_specific['config-import']['skip-modules'] = array(‘devel');

This only works with the Drush config-import and config-export commands. Use caution with the Configuration Synchronization admin page.

27

Configuration Filter

Page 28: Drush in the Composer Era

The Future

28

Page 29: Drush in the Composer Era

Drush and Drupal Console

29

The more code you have, the more code you have to maintain.

However, the maintenance process adds value. New code should benefit from existing code.

Duplicate functions that do the same thing should be avoided to reduce maintenance requirements.

&

Page 30: Drush in the Composer Era

Drush Bootstrap Refactor

30

Drush now has separate bootstrap classes for each framework

Boot

validRoot($path);

DrupalBoot6

validRoot($path);

DrupalBoot7

validRoot($path);

DrupalBoot8

validRoot($path);

SOMEDAY?

Page 31: Drush in the Composer Era

By factoring Site Aliases and Backend Invoke into separate libraries, the “Drush Finder” can handle remote dispatches.

31

Refactor into Composer Libraries

Drush Finder Drush Wrapper Drush Launcher Drush Application

Page 32: Drush in the Composer Era

cd /path/to/drupal-projectcomposer require pantheon-systems/behat-drush-endpoint

32

Drush Driver Enhancement

YES!