Skip to content

Commit

Permalink
Fixes consolidation#442: Allow CollectionBuilder, Collection and Simu…
Browse files Browse the repository at this point in the history
…lated tasks to be used by tasks (e.g. Remote\Ssh) that require a CommandInterface.
  • Loading branch information
greg-1-anderson committed Sep 9, 2016
1 parent 1356eca commit 551a819
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 11 deletions.
33 changes: 32 additions & 1 deletion src/Collection/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
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;
Expand All @@ -28,7 +31,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 @@ -411,6 +414,34 @@ public function 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
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
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
9 changes: 6 additions & 3 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 Down Expand Up @@ -140,7 +143,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
25 changes: 23 additions & 2 deletions src/Task/Simulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
use Robo\Contract\SimulatedInterface;
use Robo\Log\RoboLogLevel;
use Psr\Log\LogLevel;
use Robo\Contract\CommandInterface;

class Simulator extends BaseTask
class Simulator extends BaseTask implements CommandInterface
{
protected $task;
protected $constructorParameters;
Expand Down Expand Up @@ -63,6 +64,23 @@ public function run()
return $result;
}

/**
* Danger: reach through the simulated wrapper and pull out the command
* to be executed. This is used when using a simulated task with another
* simulated task that runs commands, e.g. the Remote\Ssh task. Using
* a simulated CommandInterface task with a non-simulated task may produce
* unexpected results (e.g. execution!).
*
* @return string
*/
public function getCommand()
{
if (!$this->task instanceof CommandInterface) {
throw new TaskException($this->task, 'Simulated task that is not a CommandInterface used as a CommandInterface.');
}
return $this->task->getCommand();
}

protected function formatParameters($action)
{
$parameterList = array_map([$this, 'convertParameter'], $action);
Expand All @@ -78,11 +96,14 @@ protected function convertParameter($item)
return $this->shortenParameter(var_export($item, true));
}
if (is_object($item)) {
return $this->shortenParameter(var_export($item, true), '[' . get_class($item). 'object]');
return '[' . get_class($item). ' object]';
}
if (is_string($item)) {
return $this->shortenParameter("'$item'");
}
if (is_null($item)) {
return 'null';
}
return $item;
}

Expand Down
6 changes: 4 additions & 2 deletions src/Task/Testing/PHPUnit.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ public function files($files)
* @param string $file path to file to test
* @return $this
*/
public function file($file) {
public function file($file)
{
return $this->files($file);
}

Expand All @@ -140,7 +141,8 @@ public function file($file) {
* @param string $dir path to directory to test
* @return $this
*/
public function dir($dir) {
public function dir($dir)
{
return $this->dir($dir);
}

Expand Down
11 changes: 11 additions & 0 deletions tests/src/RoboFileFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,15 @@ public function testVerbosity()
$this->logger->notice('This is a notice log message.');
$this->logger->debug('This is a debug log message.');
}

public function testDeploy()
{
$gitTask = $this->taskGitStack()
->pull();

$this->taskSshExec('mysite.com')
->remoteDir('/var/www/somesite')
->exec($gitTask)
->run();
}
}
17 changes: 17 additions & 0 deletions tests/unit/RunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ public function testSymfonyStyle()
$this->guy->seeInOutput('Some text in section one.');
}

public function testDeploy()
{
$argv = ['placeholder', 'test:deploy', '--simulate'];
$this->runner->execute($argv, $this->guy->capturedOutputStream());
$this->guy->seeInOutput('[Simulator] Simulating Remote\\Ssh(\'mysite.com\', null)');
$this->guy->seeInOutput('[Simulator] Running ssh mysite.com \'cd "/var/www/somesite" && git pull\'');
}

public function testRunnerTryError()
{
$argv = ['placeholder', 'test:error'];
Expand All @@ -122,6 +130,15 @@ public function testRunnerTryError()
$this->assertTrue($result > 0);
}

public function testRunnerTrySimulatedError()
{
$argv = ['placeholder', 'test:error', '--simulate'];
$result = $this->runner->execute($argv, $this->guy->capturedOutputStream());

$this->guy->seeInOutput('Simulating Exec');
$this->assertEquals(0, $result);
}

public function testRunnerTryException()
{
$argv = ['placeholder', 'test:exception', '--task'];
Expand Down

0 comments on commit 551a819

Please sign in to comment.