From 0557f84e6de0a42c1998fcd0c4aad4c8f1c1f0a1 Mon Sep 17 00:00:00 2001 From: Michael Moussa Date: Fri, 22 Jan 2016 17:52:11 -0500 Subject: [PATCH 1/4] Added way to tell tasks that they should only run for certain stages --- src/Task/Task.php | 42 +++++++++++++++++++++++++++++++++++++- test/src/Task/TaskTest.php | 17 ++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Task/Task.php b/src/Task/Task.php index 6a91b54a7..ebcb3859d 100644 --- a/src/Task/Task.php +++ b/src/Task/Task.php @@ -32,6 +32,12 @@ class Task */ private $once = false; + /** + * List of stages in which this task should be executed. + * @var array Key contains stage names. + */ + private $onlyFor = []; + /** * List of servers names there this task should be executed. * @var array Key contains server names. @@ -135,6 +141,18 @@ public function onlyOn($servers = []) return $this; } + /** + * Indicate for which stages this task should be run. + * + * @param array|string $stages + * @return $this + */ + public function onlyFor($stages = []) + { + $this->onlyFor = array_flip(is_array($stages) ? $stages: func_get_args()); + return $this; + } + /** * @return array */ @@ -142,7 +160,29 @@ public function getOnlyOn() { return $this->onlyOn; } - + + /** + * @return array + */ + public function getOnlyFor() + { + return $this->onlyFor; + } + + /** + * Decide to run or not to run for these stages. + * @param $stages + * @return bool + */ + public function runForStages($stages) + { + if (empty($this->onlyFor)) { + return true; + } else { + return !empty(array_intersect($stages, array_keys($this->onlyFor))); + } + } + /** * Decide to run or not to run on this server. * @param string $serverName diff --git a/test/src/Task/TaskTest.php b/test/src/Task/TaskTest.php index d1cb70e8b..4ddca91ad 100644 --- a/test/src/Task/TaskTest.php +++ b/test/src/Task/TaskTest.php @@ -44,8 +44,23 @@ public function testTask() $task->onlyOn(); $this->assertTrue($task->runOnServer('server')); - + $task->setPrivate(); $this->assertTrue($task->isPrivate()); + + $task->onlyFor(['staging', 'production']); + $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyFor()); + $this->assertTrue($task->runForStages(['staging'])); + $this->assertTrue($task->runForStages(['production'])); + $this->assertTrue($task->runForStages(['staging', 'production'])); + + $task->onlyFor('staging', 'production'); + $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyFor()); + $this->assertTrue($task->runForStages(['staging'])); + $this->assertTrue($task->runForStages(['production'])); + $this->assertTrue($task->runForStages(['staging', 'production'])); + + $task->onlyFor(); + $this->assertTrue($task->runForStages('anything')); } } From f8fd055cd74b5a265cadf38cfb58f1e6925e4ca7 Mon Sep 17 00:00:00 2001 From: Michael Moussa Date: Fri, 22 Jan 2016 17:58:49 -0500 Subject: [PATCH 2/4] Added support for onlyFor(...) in the series and parallel executors If the task specifies it must only be run for specific stage(s), then the task will be skipped if (a) the server environment doesn't have any stages, or (b) the server environment does have stages, but none of them match the list of allowed stages. --- src/Executor/ParallelExecutor.php | 13 +++++++++++++ src/Executor/SeriesExecutor.php | 4 ++++ test/fixture/recipe.php | 18 +++++++++++++++++- test/src/Executor/ParallelExecutorTest.php | 17 +++++++++++++++-- test/src/Executor/SeriesExecutorTest.php | 13 +++++++++++-- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/Executor/ParallelExecutor.php b/src/Executor/ParallelExecutor.php index 9410843e2..120e3603d 100644 --- a/src/Executor/ParallelExecutor.php +++ b/src/Executor/ParallelExecutor.php @@ -9,6 +9,7 @@ use Deployer\Console\Output\OutputWatcher; use Deployer\Console\Output\VerbosityString; +use Deployer\Server\Environment; use Deployer\Task\Context; use Pure\Server; use Pure\Storage\ArrayStorage; @@ -45,6 +46,11 @@ class ParallelExecutor implements ExecutorInterface */ private $servers; + /** + * @var \Deployer\Server\Environment[] + */ + private $environments; + /** * @var \Symfony\Component\Console\Input\InputInterface */ @@ -128,6 +134,7 @@ public function run($tasks, $servers, $environments, $input, $output) { $this->tasks = $tasks; $this->servers = $servers; + $this->environments = $environments; $this->input = $input; $this->output = new OutputWatcher($output); $this->informer = new Informer($this->output); @@ -286,6 +293,12 @@ public function sendTasks() foreach ($this->servers as $serverName => $server) { if ($task->runOnServer($serverName)) { + $env = isset($this->environments[$serverName]) ? $this->environments[$serverName] : $this->environments[$serverName] = new Environment(); + + if (!empty($task->getOnlyFor()) && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + continue; + } + $this->informer->onServer($serverName); $this->tasksToDo[$serverName] = $taskName; } diff --git a/src/Executor/SeriesExecutor.php b/src/Executor/SeriesExecutor.php index 54e080cf2..bea79d00d 100644 --- a/src/Executor/SeriesExecutor.php +++ b/src/Executor/SeriesExecutor.php @@ -33,6 +33,10 @@ public function run($tasks, $servers, $environments, $input, $output) if ($task->runOnServer($serverName)) { $env = isset($environments[$serverName]) ? $environments[$serverName] : $environments[$serverName] = new Environment(); + if (!empty($task->getOnlyFor()) && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + continue; + } + $informer->onServer($serverName); try { diff --git a/test/fixture/recipe.php b/test/fixture/recipe.php index 697f0e980..f728c095e 100644 --- a/test/fixture/recipe.php +++ b/test/fixture/recipe.php @@ -1,9 +1,25 @@ stage('production'); +localServer('server4') + ->stage('production'); -task('test', function () { +task('test:hello', function () { writeln('Hello world!'); }); + +task('test:onlyFor', function () { + writeln('You should only see this for production'); +}) + ->onlyFor('production'); + +task('test', [ + 'test:hello', + 'test:onlyFor' +]); diff --git a/test/src/Executor/ParallelExecutorTest.php b/test/src/Executor/ParallelExecutorTest.php index bd06de1ff..e8f333d52 100644 --- a/test/src/Executor/ParallelExecutorTest.php +++ b/test/src/Executor/ParallelExecutorTest.php @@ -26,12 +26,25 @@ protected function loadRecipe() include $this->recipeFile = __DIR__ . '/../../fixture/recipe.php'; } - public function testParallel() + public static function setUpBeforeClass() { define('DEPLOYER_BIN', __DIR__ . '/../../../bin/dep'); - + } + + public function testParallel() + { $display = $this->exec('test', ['--parallel' => true, '--file' => $this->recipeFile]); $this->assertContains('Ok', $display); + $this->assertNotContains('You should only see this for production', $display); + } + + public function testParallelWithStage() + { + $display = $this->exec('test', ['--parallel' => true, '--file' => $this->recipeFile, 'stage' => 'production']); + + $this->assertContains('Ok', $display); + $this->assertContains('[server3] You should only see this for production', $display); + $this->assertContains('[server4] You should only see this for production', $display); } } diff --git a/test/src/Executor/SeriesExecutorTest.php b/test/src/Executor/SeriesExecutorTest.php index 47d7e1cab..694f8eb9a 100644 --- a/test/src/Executor/SeriesExecutorTest.php +++ b/test/src/Executor/SeriesExecutorTest.php @@ -21,7 +21,7 @@ public function testSeriesExecutor() $this->initialize(); $mock = $this->getMockBuilder('stdClass') - ->setMethods(['task', 'once', 'only']) + ->setMethods(['task', 'once', 'only', 'onlyStaging']) ->getMock(); $mock->expects($this->exactly(2)) @@ -30,6 +30,8 @@ public function testSeriesExecutor() ->method('once'); $mock->expects($this->once()) ->method('only'); + $mock->expects($this->once()) + ->method('onlyStaging'); $task = new Task('task', function () use ($mock) { $mock->task(); @@ -45,12 +47,19 @@ public function testSeriesExecutor() }); $taskOnly->onlyOn(['one']); - $tasks = [$task, $taskOne, $taskOnly]; + $taskOnlyStaging = new Task('onlyStaging', function () use ($mock) { + $mock->onlyStaging(); + }); + $taskOnlyStaging->onlyFor('staging'); + + $tasks = [$task, $taskOne, $taskOnly, $taskOnlyStaging]; $environments = [ 'one' => new Environment(), 'two' => new Environment(), ]; + $environments['two']->set('stages', ['staging']); + $servers = [ 'one' => new Local(), 'two' => new Local(), From 28ccd7835648faeed32203ef6039a9c0c570a496 Mon Sep 17 00:00:00 2001 From: Michael Moussa Date: Fri, 22 Jan 2016 19:41:01 -0500 Subject: [PATCH 3/4] Fix for PHP 5.4 --- src/Executor/ParallelExecutor.php | 2 +- src/Executor/SeriesExecutor.php | 2 +- src/Task/Task.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Executor/ParallelExecutor.php b/src/Executor/ParallelExecutor.php index 120e3603d..ebf6726c7 100644 --- a/src/Executor/ParallelExecutor.php +++ b/src/Executor/ParallelExecutor.php @@ -295,7 +295,7 @@ public function sendTasks() if ($task->runOnServer($serverName)) { $env = isset($this->environments[$serverName]) ? $this->environments[$serverName] : $this->environments[$serverName] = new Environment(); - if (!empty($task->getOnlyFor()) && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + if (count($task->getOnlyFor()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { continue; } diff --git a/src/Executor/SeriesExecutor.php b/src/Executor/SeriesExecutor.php index bea79d00d..8447c4ce1 100644 --- a/src/Executor/SeriesExecutor.php +++ b/src/Executor/SeriesExecutor.php @@ -33,7 +33,7 @@ public function run($tasks, $servers, $environments, $input, $output) if ($task->runOnServer($serverName)) { $env = isset($environments[$serverName]) ? $environments[$serverName] : $environments[$serverName] = new Environment(); - if (!empty($task->getOnlyFor()) && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + if (count($task->getOnlyFor()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { continue; } diff --git a/src/Task/Task.php b/src/Task/Task.php index ebcb3859d..0bafe9e51 100644 --- a/src/Task/Task.php +++ b/src/Task/Task.php @@ -179,7 +179,7 @@ public function runForStages($stages) if (empty($this->onlyFor)) { return true; } else { - return !empty(array_intersect($stages, array_keys($this->onlyFor))); + return count(array_intersect($stages, array_keys($this->onlyFor))) > 0; } } From b20df447877bd149d947148a151bb5e5235935fb Mon Sep 17 00:00:00 2001 From: Michael Moussa Date: Mon, 25 Jan 2016 09:34:37 -0500 Subject: [PATCH 4/4] Changed onlyFor to onlyForStage for explicitness --- src/Executor/ParallelExecutor.php | 2 +- src/Executor/SeriesExecutor.php | 2 +- src/Task/Task.php | 14 +++++++------- test/fixture/recipe.php | 6 +++--- test/src/Executor/SeriesExecutorTest.php | 2 +- test/src/Task/TaskTest.php | 10 +++++----- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Executor/ParallelExecutor.php b/src/Executor/ParallelExecutor.php index ebf6726c7..12abc5706 100644 --- a/src/Executor/ParallelExecutor.php +++ b/src/Executor/ParallelExecutor.php @@ -295,7 +295,7 @@ public function sendTasks() if ($task->runOnServer($serverName)) { $env = isset($this->environments[$serverName]) ? $this->environments[$serverName] : $this->environments[$serverName] = new Environment(); - if (count($task->getOnlyFor()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + if (count($task->getOnlyForStage()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { continue; } diff --git a/src/Executor/SeriesExecutor.php b/src/Executor/SeriesExecutor.php index 8447c4ce1..bb655faf4 100644 --- a/src/Executor/SeriesExecutor.php +++ b/src/Executor/SeriesExecutor.php @@ -33,7 +33,7 @@ public function run($tasks, $servers, $environments, $input, $output) if ($task->runOnServer($serverName)) { $env = isset($environments[$serverName]) ? $environments[$serverName] : $environments[$serverName] = new Environment(); - if (count($task->getOnlyFor()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { + if (count($task->getOnlyForStage()) > 0 && (!$env->has('stages') || !$task->runForStages($env->get('stages')))) { continue; } diff --git a/src/Task/Task.php b/src/Task/Task.php index 0bafe9e51..eadccc8e5 100644 --- a/src/Task/Task.php +++ b/src/Task/Task.php @@ -36,7 +36,7 @@ class Task * List of stages in which this task should be executed. * @var array Key contains stage names. */ - private $onlyFor = []; + private $onlyForStage = []; /** * List of servers names there this task should be executed. @@ -147,9 +147,9 @@ public function onlyOn($servers = []) * @param array|string $stages * @return $this */ - public function onlyFor($stages = []) + public function onlyForStage($stages = []) { - $this->onlyFor = array_flip(is_array($stages) ? $stages: func_get_args()); + $this->onlyForStage = array_flip(is_array($stages) ? $stages: func_get_args()); return $this; } @@ -164,9 +164,9 @@ public function getOnlyOn() /** * @return array */ - public function getOnlyFor() + public function getOnlyForStage() { - return $this->onlyFor; + return $this->onlyForStage; } /** @@ -176,10 +176,10 @@ public function getOnlyFor() */ public function runForStages($stages) { - if (empty($this->onlyFor)) { + if (empty($this->onlyForStage)) { return true; } else { - return count(array_intersect($stages, array_keys($this->onlyFor))) > 0; + return count(array_intersect($stages, array_keys($this->onlyForStage))) > 0; } } diff --git a/test/fixture/recipe.php b/test/fixture/recipe.php index f728c095e..0e8e7d045 100644 --- a/test/fixture/recipe.php +++ b/test/fixture/recipe.php @@ -14,12 +14,12 @@ writeln('Hello world!'); }); -task('test:onlyFor', function () { +task('test:onlyForStage', function () { writeln('You should only see this for production'); }) - ->onlyFor('production'); + ->onlyForStage('production'); task('test', [ 'test:hello', - 'test:onlyFor' + 'test:onlyForStage' ]); diff --git a/test/src/Executor/SeriesExecutorTest.php b/test/src/Executor/SeriesExecutorTest.php index 694f8eb9a..fed179a39 100644 --- a/test/src/Executor/SeriesExecutorTest.php +++ b/test/src/Executor/SeriesExecutorTest.php @@ -50,7 +50,7 @@ public function testSeriesExecutor() $taskOnlyStaging = new Task('onlyStaging', function () use ($mock) { $mock->onlyStaging(); }); - $taskOnlyStaging->onlyFor('staging'); + $taskOnlyStaging->onlyForStage('staging'); $tasks = [$task, $taskOne, $taskOnly, $taskOnlyStaging]; diff --git a/test/src/Task/TaskTest.php b/test/src/Task/TaskTest.php index 4ddca91ad..defd9e9f9 100644 --- a/test/src/Task/TaskTest.php +++ b/test/src/Task/TaskTest.php @@ -48,19 +48,19 @@ public function testTask() $task->setPrivate(); $this->assertTrue($task->isPrivate()); - $task->onlyFor(['staging', 'production']); - $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyFor()); + $task->onlyForStage(['staging', 'production']); + $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyForStage()); $this->assertTrue($task->runForStages(['staging'])); $this->assertTrue($task->runForStages(['production'])); $this->assertTrue($task->runForStages(['staging', 'production'])); - $task->onlyFor('staging', 'production'); - $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyFor()); + $task->onlyForStage('staging', 'production'); + $this->assertEquals(['staging' => 0, 'production' => 1], $task->getOnlyForStage()); $this->assertTrue($task->runForStages(['staging'])); $this->assertTrue($task->runForStages(['production'])); $this->assertTrue($task->runForStages(['staging', 'production'])); - $task->onlyFor(); + $task->onlyForStage(); $this->assertTrue($task->runForStages('anything')); } }