Skip to content

Commit

Permalink
#88: Include Drupal Console in Drush's composer.json file. Build Drus…
Browse files Browse the repository at this point in the history
…h command records for all Console commands, so that they can be displayed in Drush's help output, and executed. This allows us to run commands such as 'drush @Remote x-generate-module', and remotely run Console commands with a Drush alias.  Code is still rough at the moment.  Console commands are converted from 'generate:module' to 'x-generate-module' to match Drush command conventions without overlapping with existing Drush commands. This won't be the final transformation, but some discussion will be necessary before we can decide on what it should be.
  • Loading branch information
greg-1-anderson committed Apr 15, 2015
1 parent 86742e0 commit f2a2511
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 35 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"d11wtq/boris": "~1.0",
"symfony/yaml": "~2.2",
"symfony/var-dumper": "2.6.3",
"pear/console_table": "~1.2.0"
"pear/console_table": "~1.2.0",
"drupal/console": "dev-master"
},
"require-dev": {
"phpunit/phpunit": ">=3.5",
Expand Down
78 changes: 44 additions & 34 deletions includes/command.inc
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,17 @@ function drush_redispatch_get_options() {
// If we can parse the current command, then examine all contexts
// in order for any option that is directly related to the current command
$command = drush_parse_command();
$command_options = drush_get_options_for_command($command);
// If --bootstrap-to-first-arg is specified, do not
// pass it along to remote commands.
unset($options['bootstrap-to-first-arg']);

return $options;
}

function drush_get_options_for_command($command)
{
$options = array();
if (is_array($command)) {
foreach ($command['options'] as $key => $value) {
// Strip leading --
Expand All @@ -722,10 +733,6 @@ function drush_redispatch_get_options() {
}
}
}
// If --bootstrap-to-first-arg is specified, do not
// pass it along to remote commands.
unset($options['bootstrap-to-first-arg']);

return $options;
}

Expand Down Expand Up @@ -1010,40 +1017,43 @@ function drush_get_commands($reset = FALSE) {
}

$list = drush_commandfile_list();
$results = array();
foreach ($list as $commandfile => $path) {
if (drush_command_hook($commandfile, 'drush_command')) {
$function = $commandfile . '_drush_command';
$result = $function();
foreach ((array)$result as $key => $command) {
// Add some defaults and normalize the command descriptor.
$command += drush_command_defaults($key, $commandfile, $path);

// Add engine data.
drush_merge_engine_data($command);

// Translate command.
drush_command_translate($command);

// If the command callback is not 'drush_command', then
// copy the callback function to an alternate element
// of the command array that will be called when Drush
// calls the command function hooks. Then, set the
// callback to drush_command so that the function hooks
// will be called.
if (($command['callback'] != 'drush_command') && $command['invoke hooks']) {
$command['primary function'] = $command['callback'];
$command['callback'] = 'drush_command';
}
$results = array_merge($results, (array)$function());
}
}
$console_commands = drush_get_context('CONSOLE_APPLICATION_COMMANDS');
$results = array_merge($results, $console_commands);
foreach ((array)$results as $key => $command) {
// Add some defaults and normalize the command descriptor.
$command += drush_command_defaults($key, $commandfile, $path);

$commands[$key] = $command;
// For every alias, make a copy of the command and store it in the command list
// using the alias as a key
if (isset($command['aliases']) && count($command['aliases'])) {
foreach ($command['aliases'] as $alias) {
$commands[$alias] = $command;
$commands[$alias]['is_alias'] = TRUE;
}
}
// Add engine data.
drush_merge_engine_data($command);

// Translate command.
drush_command_translate($command);

// If the command callback is not 'drush_command', then
// copy the callback function to an alternate element
// of the command array that will be called when Drush
// calls the command function hooks. Then, set the
// callback to drush_command so that the function hooks
// will be called.
if (($command['callback'] != 'drush_command') && $command['invoke hooks']) {
$command['primary function'] = $command['callback'];
$command['callback'] = 'drush_command';
}

$commands[$key] = $command;
// For every alias, make a copy of the command and store it in the command list
// using the alias as a key
if (isset($command['aliases']) && count($command['aliases'])) {
foreach ($command['aliases'] as $alias) {
$commands[$alias] = $command;
$commands[$alias]['is_alias'] = TRUE;
}
}
}
Expand Down
179 changes: 179 additions & 0 deletions includes/consoleadapter.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<?php

/**
* @file
* An Adapter class that allows Drush to call Symfony Console commands.
*/

use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\EventDispatcher\EventDispatcher;

use Drupal\AppConsole\Command\Command;
use Drupal\AppConsole\Console\Shell;
use Drupal\AppConsole\Console\Application;
use Drupal\AppConsole\Command\Helper\ShellHelper;
use Drupal\AppConsole\Command\Helper\KernelHelper;
use Drupal\AppConsole\Command\Helper\DialogHelper;
use Drupal\AppConsole\Command\Helper\RegisterCommandsHelper;
use Drupal\AppConsole\Utils\StringUtils;
use Drupal\AppConsole\Utils\Validators;
use Drupal\AppConsole\Command\Helper\TranslatorHelper;
use Drupal\AppConsole\UserConfig;
use Drupal\AppConsole\Command\Helper\DrupalAutoloadHelper;
use Drupal\AppConsole\EventSubscriber\ShowGeneratedFilesListener;
use Drupal\AppConsole\EventSubscriber\ShowWelcomeMessageListener;
use Drupal\AppConsole\Command\Helper\MessageHelper;
use Drupal\AppConsole\Command\Helper\ChainCommandHelper;
use Drupal\AppConsole\EventSubscriber\CallCommandListener;
use Drupal\AppConsole\EventSubscriber\ShowCompletedMessageListener;
use Drupal\AppConsole\EventSubscriber\ValidateDependenciesListener;

function consoleadapter_find_console()
{
// Once the type of the Drupal Console has been updated, we will find it in our
// vendor directory. For now, it is mis-installed to 'modules/console'.
$locations = array(
drush_get_context('DRUSH_VENDOR_PATH', '') . '/drupal/console/',
DRUSH_BASE_PATH . '/modules/console/',
);
foreach ($locations as $path) {
if (is_dir($path)) {
return $path;
}
}
return FALSE;
}

function consoleadapter_preflight() {
// Find the Drupal App Console root; fail if it is not available.
$consoleRoot = consoleadapter_find_console();
if (!$consoleRoot) {
return drush_set_error('DRUSH_NO_CONSOLE_APP', dt("Could not find Drupal Console Application"));
}

// Now we will initialize and run the Drupal App Console
$config = new UserConfig();

$translatorHelper = new TranslatorHelper();
$translatorHelper->loadResource($config->get('application.language'), $consoleRoot);

$application = new Application($config);
$application->setDirectoryRoot($consoleRoot);

$helpers = [
'kernel' => new KernelHelper(),
'shell' => new ShellHelper(new Shell($application)),
'dialog' => new DialogHelper(),
'register_commands' => new RegisterCommandsHelper($application),
'stringUtils' => new StringUtils(),
'validators' => new Validators(),
'translator' => $translatorHelper,
'drupal-autoload' => new DrupalAutoloadHelper(),
'message' => new MessageHelper($translatorHelper),
'chain' => new ChainCommandHelper(),
];

$application->addHelpers($helpers);

$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new ValidateDependenciesListener());
$dispatcher->addSubscriber(new ShowWelcomeMessageListener());
$dispatcher->addSubscriber(new ShowGeneratedFilesListener());
$dispatcher->addSubscriber(new CallCommandListener());
$dispatcher->addSubscriber(new ShowCompletedMessageListener());

$application->setDispatcher($dispatcher);
$application->setDefaultCommand('list');

consoleadapter_add_global_commands($application);

drush_set_context('CONSOLE_APPLICATION', $application);
}

// $registerCommandsHelper->getCommands()
// $consoleCommands = $registerCommandsHelper->getConsoleCommands();
// $customCommands = $registerCommandsHelper->getCustomCommands();


function consoleadapter_add_global_commands($application) {
$registerCommandsHelper = $application->getHelperSet()->get('register_commands');
$items = consoleadapter_convert_console_commands_to_drush($registerCommandsHelper->getConsoleCommands());
drush_set_context('CONSOLE_APPLICATION_COMMANDS', $items);
}

function consoleadapter_add_module_commands() {
/*
$root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
$application = drush_get_context('CONSOLE_APPLICATION');
$registerCommandsHelper = $application->getHelperSet()->get('register_commands');
$commands = drush_get_context('CONSOLE_APPLICATION_COMMANDS');
// Add commands defined by Drupal modules
$items = consoleadapter_convert_console_commands_to_drush($registerCommandsHelper->getCustomCommands());
drush_set_context('CONSOLE_APPLICATION_COMMANDS', array_merge($commands, $items));
*/
}

function consoleadapter_callback() {
// Get the application object
$application = drush_get_context('CONSOLE_APPLICATION');
// Recover our command record
$command = drush_get_command();

// Build the command line arguments for the redispatch to the Console application
// TODO: if we wrote our own version of ArgvInput, we could pass $drush_args
// and $command_options to it directly, and avoid having to join-and-reparse
// the args and options.
$argv[] = 'console';
$argv[] = $command['console-command-name'];
$drush_args = drush_get_arguments();
array_shift($drush_args);
$command_options = drush_get_options_for_command($command);
$options = array();
foreach ($command_options as $key => $value) {
$option = "--$key";
if (!$value) {
$option .= '=0';
}
elseif ($value !== TRUE) {
$option .= "=$value";
}
$options[] = $option;
}
$argv = array_merge($argv, $drush_args, $options);

$application->run(new ArgvInput($argv));
}

function consoleadapter_convert_console_commands_to_drush($console_command_list) {
$items = array();
foreach ($console_command_list as $console_command) {
$console_command_name = $console_command->getName();
$drush_name = 'x-' . strtr($console_command_name, ':', '-');
$definition = $console_command->getDefinition();
$arguments = array();
foreach ($definition->getArguments() as $arg) {
// We also have $arg->getDefault() and $arg->isRequired() that we could use here
// to create a more complex Drush help record.
$arguments[$arg->getName()] = $arg->getDescription();
}
$options = array();
foreach ($definition->getOptions() as $opt) {
// We also have $opt->getDefault() and $opt->isValueOptional() and $opt->getShortcut()
$options[$opt->getName()] = $opt->getDescription();
}
$items[$drush_name] = array(
// 'console-command' => $console_command,
'console-command-name' => $console_command_name,
'description' => $console_command->getDescription(),
'aliases' => $console_command->getAliases(),
'arguments' => $arguments,
'options' => $options,
'core' => array('8+'),
'callback' => 'consoleadapter_callback',
// We will let the console command bootstrap, since it expects to do that.
'bootstrap' => DRUSH_BOOTSTRAP_NONE,
);
}
return $items;
}
8 changes: 8 additions & 0 deletions includes/preflight.inc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function drush_preflight_prepare() {
require_once DRUSH_BASE_PATH . '/includes/cache.inc';
require_once DRUSH_BASE_PATH . '/includes/filesystem.inc';
require_once DRUSH_BASE_PATH . '/includes/dbtng.inc';
require_once DRUSH_BASE_PATH . '/includes/consoleadapter.inc';

// Stash our vendor path and classloader.
drush_set_context('DRUSH_VENDOR_PATH', dirname(realpath($vendor_path)));
Expand Down Expand Up @@ -188,6 +189,10 @@ function drush_preflight() {
drush_load_config('custom');

_drush_preflight_global_options();

// Create the Console Application object, and add the global Console commands
consoleadapter_preflight();

// Load all the commandfiles findable from any of the
// scopes listed above.
_drush_find_commandfiles_drush();
Expand Down Expand Up @@ -263,6 +268,9 @@ function drush_preflight_root() {
// Load the config options from Drupal's /drush and sites/all/drush directories,
// even prior to bootstrapping the root.
drush_load_config('drupal');

// Add whatever drupal console module commands we can find in the root
consoleadapter_add_module_commands();
}

function drush_preflight_site() {
Expand Down

0 comments on commit f2a2511

Please sign in to comment.