From cec0bc1255a904d7d9ff73cae3aaedf771a12c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Monta=C3=B1ez?= Date: Tue, 10 Jan 2017 21:28:32 -0300 Subject: [PATCH] [Nostromo] Refactor Deployment, add Strategies. --- src/Command/BuiltIn/DeployCommand.php | 153 +++++------------- .../BuiltIn/Releases/RollbackCommand.php | 9 +- src/Deploy/Strategy/ReleasesStrategy.php | 122 ++++++++++++++ src/Deploy/Strategy/RsyncStrategy.php | 96 +++++++++++ src/Deploy/Strategy/StrategyInterface.php | 35 ++++ src/Runtime/Runtime.php | 30 ++++ tests/Resources/testhost-force-release.yml | 2 +- 7 files changed, 334 insertions(+), 113 deletions(-) create mode 100644 src/Deploy/Strategy/ReleasesStrategy.php create mode 100644 src/Deploy/Strategy/RsyncStrategy.php create mode 100644 src/Deploy/Strategy/StrategyInterface.php diff --git a/src/Command/BuiltIn/DeployCommand.php b/src/Command/BuiltIn/DeployCommand.php index 7249056..72d7438 100644 --- a/src/Command/BuiltIn/DeployCommand.php +++ b/src/Command/BuiltIn/DeployCommand.php @@ -10,6 +10,7 @@ namespace Mage\Command\BuiltIn; +use Mage\Deploy\Strategy\StrategyInterface; use Mage\Runtime\Exception\RuntimeException; use Mage\Runtime\Runtime; use Mage\Task\ExecuteOnRollbackInterface; @@ -63,6 +64,9 @@ class DeployCommand extends AbstractCommand try { $this->runtime->setEnvironment($input->getArgument('environment')); + $strategy = $this->runtime->guessStrategy(); + $this->taskFactory = new TaskFactory($this->runtime); + $output->writeln(sprintf(' Environment: %s', $this->runtime->getEnvironment())); $this->log(sprintf('Environment: %s', $this->runtime->getEnvironment())); @@ -76,6 +80,8 @@ class DeployCommand extends AbstractCommand $output->writeln(sprintf(' Logfile: %s', $this->runtime->getConfigOptions('log_file'))); } + $output->writeln(sprintf(' Strategy: %s', $strategy->getName())); + if ($input->getOption('branch') !== false) { $this->runtime->setEnvironmentConfig('branch', $input->getOption('branch')); } @@ -85,9 +91,7 @@ class DeployCommand extends AbstractCommand } $output->writeln(''); - - $this->taskFactory = new TaskFactory($this->runtime); - $this->runDeployment($output); + $this->runDeployment($output, $strategy); } catch (RuntimeException $exception) { $output->writeln(''); $output->writeln(sprintf('%s', $exception->getMessage())); @@ -104,131 +108,52 @@ class DeployCommand extends AbstractCommand * Run the Deployment Process * * @param OutputInterface $output + * @param StrategyInterface $strategy * @throws RuntimeException */ - protected function runDeployment(OutputInterface $output) + protected function runDeployment(OutputInterface $output, StrategyInterface $strategy) { // Run "Pre Deploy" Tasks $this->runtime->setStage(Runtime::PRE_DEPLOY); - $preDeployTasks = $this->runtime->getTasks(); - - if ($this->runtime->getEnvironmentConfig('branch', false) && !$this->runtime->inRollback()) { - if (!in_array('git/change-branch', $preDeployTasks)) { - array_unshift($preDeployTasks, 'git/change-branch'); - } - } - - if ($this->runtime->getEnvironmentConfig('releases', false) && !$this->runtime->inRollback()) { - if (!in_array('deploy/targz/prepare', $preDeployTasks)) { - array_push($preDeployTasks, 'deploy/targz/prepare'); - } - } - - if (!$this->runTasks($output, $preDeployTasks)) { - throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); + if (!$this->runTasks($output, $strategy->getPreDeployTasks())) { + $this->halt(); } // Run "On Deploy" Tasks - $hosts = $this->runtime->getEnvironmentConfig('hosts'); - if (count($hosts) == 0) { - $output->writeln(' No hosts defined, skipping On Deploy tasks'); - $output->writeln(''); - } else { - $this->runtime->setStage(Runtime::ON_DEPLOY); - $onDeployTasks = $this->runtime->getTasks(); - - if ($this->runtime->getEnvironmentConfig('releases', false)) { - if (!in_array('deploy/targz/copy', $onDeployTasks) && !$this->runtime->inRollback()) { - array_unshift($onDeployTasks, 'deploy/targz/copy'); - } - - if (!in_array('deploy/release/prepare', $onDeployTasks) && !$this->runtime->inRollback()) { - array_unshift($onDeployTasks, 'deploy/release/prepare'); - } - } else { - if (!in_array('deploy/rsync', $onDeployTasks) && !$this->runtime->inRollback()) { - array_unshift($onDeployTasks, 'deploy/rsync'); - } - } - - foreach ($hosts as $host) { - $this->runtime->setWorkingHost($host); - if (!$this->runTasks($output, $onDeployTasks)) { - $this->runtime->setWorkingHost(null); - throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); - } - $this->runtime->setWorkingHost(null); - } - } + $this->runtime->setStage(Runtime::ON_DEPLOY); + $this->runOnHosts($output, $strategy->getOnDeployTasks()); // Run "On Release" Tasks - $hosts = $this->runtime->getEnvironmentConfig('hosts'); - if (count($hosts) == 0) { - $output->writeln(' No hosts defined, skipping On Release tasks'); - $output->writeln(''); - } else { - $this->runtime->setStage(Runtime::ON_RELEASE); - $onReleaseTasks = $this->runtime->getTasks(); - - if ($this->runtime->getEnvironmentConfig('releases', false)) { - if (!in_array('deploy/release', $onReleaseTasks)) { - array_unshift($onReleaseTasks, 'deploy/release'); - } - } - - foreach ($hosts as $host) { - $this->runtime->setWorkingHost($host); - if (!$this->runTasks($output, $onReleaseTasks)) { - $this->runtime->setWorkingHost(null); - throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); - } - $this->runtime->setWorkingHost(null); - } - } + $this->runtime->setStage(Runtime::ON_RELEASE); + $this->runOnHosts($output, $strategy->getOnReleaseTasks()); // Run "Post Release" Tasks - $hosts = $this->runtime->getEnvironmentConfig('hosts'); - if (count($hosts) == 0) { - $output->writeln(' No hosts defined, skipping Post Release tasks'); - $output->writeln(''); - } else { - $this->runtime->setStage(Runtime::POST_RELEASE); - $postReleaseTasks = $this->runtime->getTasks(); - - if ($this->runtime->getEnvironmentConfig('releases', false) && !$this->runtime->inRollback()) { - if (!in_array('deploy/release/cleanup', $postReleaseTasks)) { - array_unshift($postReleaseTasks, 'deploy/release/cleanup'); - } - } - - foreach ($hosts as $host) { - $this->runtime->setWorkingHost($host); - if (!$this->runTasks($output, $postReleaseTasks)) { - $this->runtime->setWorkingHost(null); - throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); - } - $this->runtime->setWorkingHost(null); - } - } + $this->runtime->setStage(Runtime::POST_RELEASE); + $this->runOnHosts($output, $strategy->getPostReleaseTasks()); // Run "Post Deploy" Tasks $this->runtime->setStage(Runtime::POST_DEPLOY); - $postDeployTasks = $this->runtime->getTasks(); - if ($this->runtime->getEnvironmentConfig('releases', false) && !$this->runtime->inRollback()) { - if (!in_array('deploy/targz/cleanup', $postDeployTasks)) { - array_unshift($postDeployTasks, 'deploy/targz/cleanup'); - } + if (!$this->runTasks($output, $strategy->getPostDeployTasks())) { + $this->halt(); } + } - if ($this->runtime->getEnvironmentConfig('branch', false) && !$this->runtime->inRollback()) { - if (!in_array('git/change-branch', $postDeployTasks)) { - array_push($postDeployTasks, 'git/change-branch'); + protected function runOnHosts(OutputInterface $output, $tasks) + { + $hosts = $this->runtime->getEnvironmentConfig('hosts'); + if (count($hosts) == 0) { + $output->writeln(sprintf(' No hosts defined, skipping %s tasks', $this->getStageName())); + $output->writeln(''); + } else { + foreach ($hosts as $host) { + $this->runtime->setWorkingHost($host); + if (!$this->runTasks($output, $tasks)) { + $this->runtime->setWorkingHost(null); + $this->halt(); + } + $this->runtime->setWorkingHost(null); } } - - if (!$this->runTasks($output, $postDeployTasks)) { - throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); - } } /** @@ -304,4 +229,14 @@ class DeployCommand extends AbstractCommand return ($succeededTasks == $totalTasks); } + + /** + * Halts the current process + * + * @throws RuntimeException + */ + protected function halt() + { + throw new RuntimeException(sprintf('Stage "%s" did not finished successfully, halting command.', $this->getStageName()), 50); + } } diff --git a/src/Command/BuiltIn/Releases/RollbackCommand.php b/src/Command/BuiltIn/Releases/RollbackCommand.php index 8e7fb1c..5214199 100644 --- a/src/Command/BuiltIn/Releases/RollbackCommand.php +++ b/src/Command/BuiltIn/Releases/RollbackCommand.php @@ -58,6 +58,9 @@ class RollbackCommand extends DeployCommand try { $this->runtime->setEnvironment($input->getArgument('environment')); + $strategy = $this->runtime->guessStrategy(); + $this->taskFactory = new TaskFactory($this->runtime); + if (!$this->runtime->getEnvironmentConfig('releases', false)) { throw new RuntimeException('Releases are not enabled', 70); } @@ -79,10 +82,10 @@ class RollbackCommand extends DeployCommand $output->writeln(sprintf(' Logfile: %s', $this->runtime->getConfigOptions('log_file'))); } - $output->writeln(''); + $output->writeln(sprintf(' Strategy: %s', $strategy->getName())); - $this->taskFactory = new TaskFactory($this->runtime); - $this->runDeployment($output); + $output->writeln(''); + $this->runDeployment($output, $strategy); } catch (RuntimeException $exception) { $output->writeln(sprintf('%s', $exception->getMessage())); $this->statusCode = $exception->getCode(); diff --git a/src/Deploy/Strategy/ReleasesStrategy.php b/src/Deploy/Strategy/ReleasesStrategy.php new file mode 100644 index 0000000..0ae0bdf --- /dev/null +++ b/src/Deploy/Strategy/ReleasesStrategy.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Mage\Deploy\Strategy; + +use Mage\Runtime\Exception\RuntimeException; +use Mage\Runtime\Runtime; + +/** + * Strategy for Deployment with Releases, using TarGz and SCP + * + * @author Andrés Montañez + */ +class ReleasesStrategy implements StrategyInterface +{ + /** + * @var Runtime + */ + protected $runtime; + + public function getName() + { + return 'Releases'; + } + + public function setRuntime(Runtime $runtime) + { + $this->runtime = $runtime; + } + + public function getPreDeployTasks() + { + $this->checkStage(Runtime::PRE_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if ($this->runtime->getBranch() && !$this->runtime->inRollback() && !in_array('git/change-branch', $tasks)) { + array_unshift($tasks, 'git/change-branch'); + } + + if (!$this->runtime->inRollback() && !in_array('deploy/targz/prepare', $tasks)) { + array_push($tasks, 'deploy/targz/prepare'); + } + + return $tasks; + } + + public function getOnDeployTasks() + { + $this->checkStage(Runtime::ON_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if (!$this->runtime->inRollback() && !in_array('deploy/targz/copy', $tasks)) { + array_unshift($tasks, 'deploy/targz/copy'); + } + + if (!$this->runtime->inRollback() && !in_array('deploy/release/prepare', $tasks)) { + array_unshift($tasks, 'deploy/release/prepare'); + } + + return $tasks; + } + + public function getOnReleaseTasks() + { + $this->checkStage(Runtime::ON_RELEASE); + $tasks = $this->runtime->getTasks(); + + if (!in_array('deploy/release', $tasks)) { + array_unshift($tasks, 'deploy/release'); + } + + return $tasks; + } + + public function getPostReleaseTasks() + { + $this->checkStage(Runtime::POST_RELEASE); + $tasks = $this->runtime->getTasks(); + + if (!in_array('deploy/release/cleanup', $tasks)) { + array_unshift($tasks, 'deploy/release/cleanup'); + } + + return $tasks; + } + + public function getPostDeployTasks() + { + $this->checkStage(Runtime::POST_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if (!$this->runtime->inRollback() && !in_array('deploy/targz/cleanup', $tasks)) { + array_unshift($tasks, 'deploy/targz/cleanup'); + } + + if ($this->runtime->getBranch() && !$this->runtime->inRollback() && !in_array('git/change-branch', $tasks)) { + array_push($tasks, 'git/change-branch'); + } + + return $tasks; + } + + /** + * Check the runtime stage is correct + * + * @param $stage + * @throws RuntimeException + */ + private function checkStage($stage) + { + if ($this->runtime->getStage() !== $stage) { + throw new RuntimeException(sprintf('Invalid stage, got "%s" but expected "%"', $this->runtime->getStage(), $stage)); + } + } +} diff --git a/src/Deploy/Strategy/RsyncStrategy.php b/src/Deploy/Strategy/RsyncStrategy.php new file mode 100644 index 0000000..499a6f6 --- /dev/null +++ b/src/Deploy/Strategy/RsyncStrategy.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Mage\Deploy\Strategy; + +use Mage\Runtime\Exception\RuntimeException; +use Mage\Runtime\Runtime; + +/** + * Strategy for Deployment with Rsync + * + * @author Andrés Montañez + */ +class RsyncStrategy implements StrategyInterface +{ + /** + * @var Runtime + */ + protected $runtime; + + public function getName() + { + return 'Rsync'; + } + + public function setRuntime(Runtime $runtime) + { + $this->runtime = $runtime; + } + + public function getPreDeployTasks() + { + $this->checkStage(Runtime::PRE_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if ($this->runtime->getBranch() && !$this->runtime->inRollback() && !in_array('git/change-branch', $tasks)) { + array_unshift($tasks, 'git/change-branch'); + } + + return $tasks; + } + + public function getOnDeployTasks() + { + $this->checkStage(Runtime::ON_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if (!$this->runtime->inRollback() && !in_array('deploy/rsync', $tasks)) { + array_unshift($tasks, 'deploy/rsync'); + } + + return $tasks; + } + + public function getOnReleaseTasks() + { + return []; + } + + public function getPostReleaseTasks() + { + return []; + } + + public function getPostDeployTasks() + { + $this->checkStage(Runtime::POST_DEPLOY); + $tasks = $this->runtime->getTasks(); + + if ($this->runtime->getBranch() && !$this->runtime->inRollback() && !in_array('git/change-branch', $tasks)) { + array_push($tasks, 'git/change-branch'); + } + + return $tasks; + } + + /** + * Check the runtime stage is correct + * + * @param $stage + * @throws RuntimeException + */ + private function checkStage($stage) + { + if ($this->runtime->getStage() !== $stage) { + throw new RuntimeException(sprintf('Invalid stage, got "%s" but expected "%"', $this->runtime->getStage(), $stage)); + } + } +} diff --git a/src/Deploy/Strategy/StrategyInterface.php b/src/Deploy/Strategy/StrategyInterface.php new file mode 100644 index 0000000..6bd9c77 --- /dev/null +++ b/src/Deploy/Strategy/StrategyInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Mage\Deploy\Strategy; + +use Mage\Runtime\Runtime; + +/** + * Interface for Deploy Strategies + * + * @author Andrés Montañez + */ +interface StrategyInterface +{ + public function getName(); + + public function setRuntime(Runtime $runtime); + + public function getPreDeployTasks(); + + public function getOnDeployTasks(); + + public function getOnReleaseTasks(); + + public function getPostReleaseTasks(); + + public function getPostDeployTasks(); +} diff --git a/src/Runtime/Runtime.php b/src/Runtime/Runtime.php index a84a7d5..a3cdb61 100644 --- a/src/Runtime/Runtime.php +++ b/src/Runtime/Runtime.php @@ -10,6 +10,9 @@ namespace Mage\Runtime; +use Mage\Deploy\Strategy\ReleasesStrategy; +use Mage\Deploy\Strategy\RsyncStrategy; +use Mage\Deploy\Strategy\StrategyInterface; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Symfony\Component\Process\Process; @@ -472,4 +475,31 @@ class Runtime $userData = posix_getpwuid(posix_geteuid()); return $userData['name']; } + + /** + * Shortcut for getting Branch information + * + * @return boolean|string + */ + public function getBranch() + { + return $this->getEnvironmentConfig('branch', false); + } + + /** + * Guesses the Deploy Strategy to use + * + * @return StrategyInterface + */ + public function guessStrategy() + { + $strategy = new RsyncStrategy(); + + if ($this->getEnvironmentConfig('releases', false)) { + $strategy = new ReleasesStrategy(); + } + + $strategy->setRuntime($this); + return $strategy; + } } diff --git a/tests/Resources/testhost-force-release.yml b/tests/Resources/testhost-force-release.yml index c99f893..e59bccb 100644 --- a/tests/Resources/testhost-force-release.yml +++ b/tests/Resources/testhost-force-release.yml @@ -11,5 +11,5 @@ magephp: - ./web/app_dev.php hosts: - host2 - on-release: + on-deploy: - deploy/release \ No newline at end of file