Skip to content

Commit

Permalink
Merge pull request consolidation#445 from consolidation-org/fix-recei…
Browse files Browse the repository at this point in the history
…vecommand

Fixes consolidation#442: Pass a collection to Remote\Ssh
  • Loading branch information
greg-1-anderson authored Sep 10, 2016
2 parents 1356eca + 71d4849 commit d712e16
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 76 deletions.
93 changes: 37 additions & 56 deletions src/Collection/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
use Robo\Task\BaseTask;
use Robo\TaskInfo;
use Robo\Contract\WrappedTaskInterface;
use Robo\Exception\TaskException;
use Robo\Exception\TaskExitException;
use Robo\Contract\CommandInterface;


use Robo\Contract\ProgressIndicatorAwareInterface;
use Robo\Common\ProgressIndicatorAwareTrait;
use Robo\Contract\InflectionInterface;

Expand All @@ -28,7 +30,7 @@
* called. Here, taskDeleteDir is used to remove partial results
* of an unfinished task.
*/
class Collection extends BaseTask implements CollectionInterface
class Collection extends BaseTask implements CollectionInterface, CommandInterface
{
protected $taskList = [];
protected $rollbackStack = [];
Expand Down Expand Up @@ -241,16 +243,6 @@ public function hasTask($name)
return array_key_exists($name, $this->taskList);
}

/**
* Test to see if the given name is an unnamed task, or
* something functionally equivalent. Any numeric index
* is renumbered when added to the collection.
*/
public static function isUnnamedTask($name)
{
return is_numeric($name);
}

/**
* Find an existing named task.
*
Expand Down Expand Up @@ -297,7 +289,7 @@ protected function addCollectionElementToTaskList($name, Element $taskGroup)
{
// If a task name is not provided, then we'll let php pick
// the array index.
if (static::isUnnamedTask($name)) {
if (Result::isUnnamed($name)) {
$this->taskList[] = $taskGroup;
return $this;
}
Expand Down Expand Up @@ -394,23 +386,39 @@ public function progressIndicatorSteps()
{
$steps = 0;
foreach ($this->taskList as $name => $taskGroup) {
foreach ($taskGroup->getTaskList() as $task) {
if ($task instanceof WrappedTaskInterface) {
$task = $task->original();
}
// If the task is a ProgressIndicatorAwareInterface, then it
// will advance the progress indicator a number of times.
if ($task instanceof ProgressIndicatorAwareInterface) {
$steps += $task->progressIndicatorSteps();
}
// We also advance the progress indicator once regardless
// of whether it is progress-indicator aware or not.
$steps++;
}
$steps += $taskGroup->progressIndicatorSteps();
}
return $steps;
}

/**
* A Collection of tasks can provide a command via `getCommand()`
* if it contains a single task, and that task implements CommandInterface.
* @return string
*/
public function getCommand()
{
if (empty($this->taskList)) {
return '';
}

if (count($this->taskList) > 1) {
// TODO: We could potentially iterate over the items in the collection
// and concatenate the result of getCommand() from each one, and fail
// only if we encounter a command that is not a CommandInterface.
throw new TaskException($this, "getCommand() does not work on arbitrary collections of tasks.");
}

$taskElement = reset($this->taskList);
$task = $taskElement->getTask();
$task = ($task instanceof WrappedTaskInterface) ? $task->original() : $task;
if ($task instanceof CommandInterface) {
return $task->getCommand();
}

throw new TaskException($task, get_class($task) . " does not implement CommandInterface, so can't be used to provide a command");
}

/**
* Run our tasks, and roll back if necessary.
*/
Expand Down Expand Up @@ -451,7 +459,7 @@ private function runWithoutCompletion()
* Run every task in a list, but only up to the first failure.
* Return the failing result, or success if all tasks run.
*/
private function runTaskList($name, array $taskList, $result)
private function runTaskList($name, array $taskList, Result $result)
{
try {
foreach ($taskList as $taskName => $task) {
Expand All @@ -465,8 +473,8 @@ private function runTaskList($name, array $taskList, $result)
// We accumulate our results into a field so that tasks that
// have a reference to the collection may examine and modify
// the incremental results, if they wish.
$key = static::isUnnamedTask($taskName) ? $name : $taskName;
$result = $this->accumulateResults($key, $result, $taskResult);
$key = Result::isUnnamed($taskName) ? $name : $taskName;
$result->accumulate($key, $taskResult);
}
} catch (TaskExitException $exitException) {
$this->fail();
Expand Down Expand Up @@ -547,33 +555,6 @@ protected function setParentCollectionForTask($task, $parentCollection)
}
}

/**
* Add the results from the most recent task to the accumulated
* results from all tasks that have run so far, merging data
* as necessary.
*/
public function accumulateResults($key, Result $result, Result $taskResult)
{
// If the result is not set or is not a Result, then ignore it
if (isset($result) && ($result instanceof Result)) {
// If the task is unnamed, then all of its data elements
// just get merged in at the top-level of the final Result object.
if (static::isUnnamedTask($key)) {
$result->merge($taskResult);
} elseif (isset($result[$key])) {
// There can only be one task with a given name; however, if
// there are tasks added 'before' or 'after' the named task,
// then the results from these will be stored under the same
// name unless they are given a name of their own when added.
$current = $result[$key];
$result[$key] = $taskResult->merge($current);
} else {
$result[$key] = $taskResult;
}
}
return $result;
}

/**
* Run all of the tasks in a provided list, ignoring failures.
* This is used to roll back or complete.
Expand Down
17 changes: 16 additions & 1 deletion src/Collection/CollectionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use ReflectionClass;
use Robo\Task\BaseTask;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;

/**
* Creates a collection, and adds tasks to it. The collection builder
Expand Down Expand Up @@ -45,7 +47,7 @@
* In the example above, the `taskDeleteDir` will be called if
* ```
*/
class CollectionBuilder extends BaseTask implements NestedCollectionInterface, WrappedTaskInterface
class CollectionBuilder extends BaseTask implements NestedCollectionInterface, WrappedTaskInterface, CommandInterface
{
protected $commandFile;
protected $collection;
Expand Down Expand Up @@ -354,6 +356,19 @@ protected function runTasks()
return $this->getCollection()->run();
}

public function getCommand()
{
if (!$this->collection && $this->currentTask) {
$task = $this->currentTask;
$task = ($task instanceof WrappedTaskInterface) ? $task->original() : $task;
if ($task instanceof CommandInterface) {
return $task->getCommand();
}
}

return $this->getCollection()->getCommand();
}

public function original()
{
return $this->getCollection();
Expand Down
21 changes: 21 additions & 0 deletions src/Collection/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Robo\Collection;

use Robo\Contract\TaskInterface;
use Robo\Contract\WrappedTaskInterface;
use Robo\Contract\ProgressIndicatorAwareInterface;

/**
* One element in a collection. Each element consists of a task
Expand Down Expand Up @@ -58,4 +60,23 @@ public function getTaskList()
{
return array_merge($this->getBefore(), [$this->getTask()], $this->getAfter());
}

public function progressIndicatorSteps()
{
$steps = 0;
foreach ($this->getTaskList() as $task) {
if ($task instanceof WrappedTaskInterface) {
$task = $task->original();
}
// If the task is a ProgressIndicatorAwareInterface, then it
// will advance the progress indicator a number of times.
if ($task instanceof ProgressIndicatorAwareInterface) {
$steps += $task->progressIndicatorSteps();
}
// We also advance the progress indicator once regardless
// of whether it is progress-indicator aware or not.
$steps++;
}
return $steps;
}
}
9 changes: 8 additions & 1 deletion src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function set($key, $value)
return $this;
}

public function setGlobalOptions($input)
public function getGlobalOptionDefaultValues()
{
$globalOptions =
[
Expand All @@ -30,6 +30,13 @@ public function setGlobalOptions($input)
self::SUPRESS_MESSAGES => false,
];

return $globalOptions;
}

public function setGlobalOptions($input)
{
$globalOptions = $this->getGlobalOptionDefaultValues();

foreach ($globalOptions as $option => $default) {
$value = $input->hasOption($option) ? $input->getOption($option) : null;
// Unfortunately, the `?:` operator does not differentate between `0` and `null`
Expand Down
33 changes: 33 additions & 0 deletions src/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,39 @@ public function getContext()
];
}

/**
* Add the results from the most recent task to the accumulated
* results from all tasks that have run so far, merging data
* as necessary.
*/
public function accumulate($key, Result $taskResult)
{
// If the task is unnamed, then all of its data elements
// just get merged in at the top-level of the final Result object.
if (static::isUnnamed($key)) {
$this->merge($taskResult);
} elseif (isset($this[$key])) {
// There can only be one task with a given name; however, if
// there are tasks added 'before' or 'after' the named task,
// then the results from these will be stored under the same
// name unless they are given a name of their own when added.
$current = $this[$key];
$this[$key] = $taskResult->merge($current);
} else {
$this[$key] = $taskResult;
}
}

/**
* We assume that named values (e.g. for associative array keys)
* are non-numeric; numeric keys are presumed to simply be the
* index of an array, and therefore insignificant.
*/
public static function isUnnamed($key)
{
return is_numeric($key);
}

/**
* @return TaskInterface
*/
Expand Down
9 changes: 7 additions & 2 deletions src/Robo.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static function unsetContainer()
/**
* Returns the currently active global container.
*
* @return \League\Container\ContainerInterface|null
* @return \League\Container\ContainerInterface
*
* @throws \RuntimeException
*/
Expand All @@ -74,10 +74,14 @@ public static function hasContainer()
*/
public static function createDefaultContainer($input = null, $output = null, $app)
{
// Do not allow this function to be called more than once.
if (static::hasContainer()) {
return static::getContainer();
}

// Set up our dependency injection container.
$container = new Container();
static::configureContainer($container, $input, $output, $app);
static::setContainer($container);

return $container;
}
Expand All @@ -89,6 +93,7 @@ public static function configureContainer($container, $input = null, $output = n
{
// Self-referential container refernce for the inflector
$container->add('container', $container);
static::setContainer($container);

// Create default input and output objects if they were not provided
if (!$input) {
Expand Down
21 changes: 10 additions & 11 deletions src/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ public function execute($argv, $output = null, $appName = null, $appVersion = nu
{
$argv = $this->shebang($argv);
$argv = $this->processRoboOptions($argv);
$app = Robo::createDefaultApplication($appName, $appVersion);
$commandFiles = $this->getRoboFileCommands($app, $output);
$app = null;
if ($appName && $appVersion) {
$app = Robo::createDefaultApplication($appName, $appVersion);
}
$commandFiles = $this->getRoboFileCommands($output);
return $this->run($argv, $output, $app, $commandFiles);
}

Expand All @@ -109,19 +112,15 @@ public function run($input = null, $output = null, $app = null, $commandFiles =
$this->setInput($input);
$this->setOutput($output);

// If our client gave us a container, then also set it inside
// the static Robo class.
if ($this->getContainer()) {
Robo::setContainer($this->getContainer());
}
// If we were not provided a container, then create one
if (!Robo::hasContainer()) {
Robo::createDefaultContainer($input, $output, $app);
$this->setContainer(Robo::getContainer());
if (!$this->getContainer()) {
$container = Robo::createDefaultContainer($input, $output, $app);
$this->setContainer($container);
// Automatically register a shutdown function and
// an error handler when we provide the container.
$this->installRoboHandlers();
}

if (!$app) {
$app = Robo::application();
}
Expand All @@ -140,7 +139,7 @@ public function run($input = null, $output = null, $app = null, $commandFiles =
return $statusCode;
}

protected function getRoboFileCommands($app, $output)
protected function getRoboFileCommands($output)
{
if (!$this->loadRoboFile($output)) {
return;
Expand Down
9 changes: 8 additions & 1 deletion src/Task/Remote/Ssh.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;
use Robo\Task\BaseTask;
use Robo\Contract\SimulatedInterface;

/**
* Runs multiple commands on a remote server.
Expand Down Expand Up @@ -45,7 +46,7 @@
* and stop the chain if one command fails
* @method $this remoteDir(string $remoteWorkingDirectory) Changes to the given directory before running commands
*/
class Ssh extends BaseTask implements CommandInterface
class Ssh extends BaseTask implements CommandInterface, SimulatedInterface
{
use \Robo\Common\CommandReceiver;
use \Robo\Common\ExecOneCommand;
Expand Down Expand Up @@ -177,6 +178,12 @@ public function run()
return $this->executeCommand($command);
}

public function simulate($context)
{
$command = $this->getCommand();
$this->printTaskInfo("Running {command}", ['command' => $command] + $context);
}

protected function validateParameters()
{
if (empty($this->hostname)) {
Expand Down
Loading

0 comments on commit d712e16

Please sign in to comment.