Skip to content

Commit

Permalink
Allow for aborting completions or rollbacks. (#815)
Browse files Browse the repository at this point in the history
* Allow for cancelling a rollback or completion with ForcedException.

* Rename the AbortTasksException from ForcedException.
  • Loading branch information
q0rban authored and greg-1-anderson committed Dec 14, 2018
1 parent 76bf8ff commit bb8cc0c
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/collections.md
Expand Up @@ -113,6 +113,8 @@ Any task may also implement \Robo\Contract\RollbackInterface; if this is done, t

Use `addAsCompletion($collection)` in place of `addAsRollback($collection)`, or implement \Robo\Contract\CompletionInterface. Completions otherwise work exactly like rollbacks.

By default, rollbacks and completions tasks or callbacks continue even if errors occur. If you would like to explicitly cancel or abort the rollback or completion, you may throw the `\Robo\Exception\AbortTasksException` exception.

### Rollback and Completion Callbacks

You may also provide arbitrary methods as `callable`s to serve as rollback or completion functions, as shown below:
Expand Down
13 changes: 13 additions & 0 deletions src/Collection/Collection.php
@@ -1,6 +1,7 @@
<?php
namespace Robo\Collection;

use Robo\Exception\AbortTasksException;
use Robo\Result;
use Robo\State\Data;
use Psr\Log\LogLevel;
Expand Down Expand Up @@ -254,6 +255,8 @@ public function ignoreErrorsTaskWrapper(TaskInterface $task)
$message = $result->getMessage();
$data = $result->getData();
$data['exitcode'] = $result->getExitCode();
} catch (AbortTasksException $abortTasksException) {
throw $abortTasksException;
} catch (\Exception $e) {
$message = $e->getMessage();
}
Expand Down Expand Up @@ -735,6 +738,10 @@ protected function setParentCollectionForTask($task, $parentCollection)

/**
* Run all of the tasks in a provided list, ignoring failures.
*
* You may force a failure by throwing a ForcedException in your rollback or
* completion task or callback.
*
* This is used to roll back or complete.
*
* @param TaskInterface[] $taskList
Expand All @@ -744,6 +751,12 @@ protected function runTaskListIgnoringFailures(array $taskList)
foreach ($taskList as $task) {
try {
$this->runSubtask($task);
} catch (AbortTasksException $abortTasksException) {
// If there's a forced exception, end the loop of tasks.
if ($message = $abortTasksException->getMessage()) {
$this->logger()->notice($message);
}
break;
} catch (\Exception $e) {
// Ignore rollback failures.
}
Expand Down
13 changes: 13 additions & 0 deletions src/Exception/AbortTasksException.php
@@ -0,0 +1,13 @@
<?php
namespace Robo\Exception;

/**
* By default, rollbacks and completions tasks or callbacks continue even if
* errors occur. If you would like to explicitly cancel or abort the rollback or
* completion, you may throw this exception to abort the subsequent tasks in the
* rollback or completion task list.
*/
class AbortTasksException extends \Exception
{

}
33 changes: 33 additions & 0 deletions tests/cli/CollectionCest.php
Expand Up @@ -4,6 +4,7 @@
use \CliGuy;

use Robo\Collection\Temporary;
use Robo\Exception\AbortTasksException;

class CollectionCest
{
Expand Down Expand Up @@ -110,6 +111,38 @@ public function toRollbackAfterFailureViaACollectionBuilder(CliGuy $I)
$I->dontSeeFileFound('j/k/m/m.txt');
}

public function toAbortRollbackOrCompletion(CliGuy $I)
{
// This is like the previous test, except we throw a ForcedException()
// inside the rollback to abort the rollback.
$collection = $I->collectionBuilder();
$result = $collection->taskFilesystemStack()
->mkdir('j')
->touch('j/j.txt')
->rollback(
$I->taskDeleteDir('j')
)
->rollbackCode(function () {
throw new AbortTasksException('Aborting rollback.');
})
->taskFilesystemStack()
->mkdir('j/k')
->touch('j/k/k.txt')
->taskFilesystemStack()
->mkdir('j/k/m')
->touch('j/k/m/m.txt')
->taskCopyDir(['doesNotExist' => 'copied'])
->run();

$I->assertEquals(1, $result->getExitCode(), $result->getMessage());

// All of the tasks created by the builder should be added
// to a collection, and `run()` should run them all.
$I->seeFileFound('j/j.txt');
$I->seeFileFound('j/k/k.txt');
$I->seeFileFound('j/k/m/m.txt');
}

public function toRollbackAWorkingDir(CliGuy $I)
{
// Run the same test with a working directory. The working
Expand Down

0 comments on commit bb8cc0c

Please sign in to comment.