diff --git a/Mage/Command/BuiltIn/DeployCommand.php b/Mage/Command/BuiltIn/DeployCommand.php
index 8dd6c9d..193e792 100644
--- a/Mage/Command/BuiltIn/DeployCommand.php
+++ b/Mage/Command/BuiltIn/DeployCommand.php
@@ -129,6 +129,24 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
return self::$deployStatus;
}
+ /**
+ * Launch a Dry run command for Rsync strategy
+ * Without others sub tasks.
+ */
+ public function dryRun()
+ {
+ if ($this->getConfig()->deployment('strategy') != 'rsync') {
+ Console::output('Error : Dry-run task is only available when using Rsync strategy.', 1, 1);
+ return 232;
+ }
+ else {
+ // Launch Pre-DryRun tasks.
+ $this->runNonDeploymentTasks(AbstractTask::STAGE_PRE_DRYRUN, $this->getConfig(), 'Pre-DryRun');
+ // Launch Deployment in dryrun mode (true).
+ $this->runDeploymentTasks(TRUE);
+ }
+ }
+
/**
* Deploys the Application
* @see \Mage\Command\AbstractCommand::run()
@@ -153,6 +171,11 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
touch(getcwd() . '/.mage/~working.lock');
}
+ // If we are in dry run only
+ if ($this->getConfig()->getParameter('dry-run') === true) {
+ return $this->dryRun();
+ }
+
// Release ID
$this->getConfig()->setReleaseId(date('YmdHis'));
@@ -229,7 +252,7 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
if (self::$deployStatus === self::FAILED) {
$exitCode = 1;
}
-
+
return $exitCode;
}
@@ -306,7 +329,7 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
}
}
- protected function runDeploymentTasks()
+ protected function runDeploymentTasks($dryrun = false)
{
if (self::$deployStatus == self::FAILED) {
return;
@@ -340,7 +363,11 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
Console::output('Deploying to ' . $this->getConfig()->getHost() . '');
- $tasksToRun = $this->getConfig()->getTasks();
+ $tasksToRun = array();
+
+ if ($dryrun !== false) {
+ $tasksToRun = $this->getConfig()->getTasks();
+ }
$deployStrategy = $this->chooseDeployStrategy();
@@ -382,7 +409,7 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
}
// Releasing
- if (self::$deployStatus == self::SUCCEDED && $this->getConfig()->release('enabled', false) === true) {
+ if (self::$deployStatus == self::SUCCEDED && $dryrun !== true && $this->getConfig()->release('enabled', false) === true) {
// Execute the Releases
Console::output('Starting the Releasing');
$completedTasks = 0;
@@ -522,7 +549,6 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment
if ($runTask === true) {
try {
$result = $task->run();
-
if ($result === true) {
Console::output('OK', 0);
$result = true;
diff --git a/Mage/Task/AbstractTask.php b/Mage/Task/AbstractTask.php
index ec1d4e0..cce9c6f 100644
--- a/Mage/Task/AbstractTask.php
+++ b/Mage/Task/AbstractTask.php
@@ -28,6 +28,12 @@ abstract class AbstractTask
*/
const STAGE_PRE_DEPLOY = 'pre-deploy';
+ /**
+ * Stage Constant for Pre Dry-Run
+ * @var string
+ */
+ const STAGE_PRE_DRYRUN = 'pre-dryrun';
+
/**
* Stage Constant for Deployment
* @var string
diff --git a/Mage/Task/BuiltIn/Deployment/Strategy/RsyncTask.php b/Mage/Task/BuiltIn/Deployment/Strategy/RsyncTask.php
index 16804b0..2e180c7 100644
--- a/Mage/Task/BuiltIn/Deployment/Strategy/RsyncTask.php
+++ b/Mage/Task/BuiltIn/Deployment/Strategy/RsyncTask.php
@@ -13,6 +13,7 @@ namespace Mage\Task\BuiltIn\Deployment\Strategy;
use Mage\Console;
use Mage\Task\BuiltIn\Deployment\Strategy\BaseStrategyTaskAbstract;
use Mage\Task\Releases\IsReleaseAware;
+use Mage\Task\BuiltIn\Releases\ListTask;
/**
* Task for Sync the Local Code to the Remote Hosts via RSYNC
@@ -27,6 +28,9 @@ class RsyncTask extends BaseStrategyTaskAbstract implements IsReleaseAware
*/
public function getName()
{
+ if ($this->getConfig()->getParameter('dry-run', false) === true) {
+ return 'Dry-run via Rsync [built-in]';
+ }
if ($this->getConfig()->release('enabled', false) === true) {
if ($this->getConfig()->getParameter('overrideRelease', false) === true) {
return 'Deploy via Rsync (with Releases override) [built-in]';
@@ -43,27 +47,130 @@ class RsyncTask extends BaseStrategyTaskAbstract implements IsReleaseAware
}
}
+
+ /**
+ * @param bool|FALSE $dryRun
+ * @return string
+ */
+ protected function getDeployCommand($dryRun = false)
+ {
+ $deployToDirectory = $this->getDeployToDirectory($dryRun);
+
+ $excludes = $this->getExcludes();
+ $excludesListFilePath = $this->getConfig()->deployment('excludes_file', '');
+
+ $strategyFlags = $this->getConfig()->deployment('strategy_flags', $this->getConfig()->general('strategy_flags', array()));
+ if (isset($strategyFlags['rsync'])) {
+ $strategyFlags = $strategyFlags['rsync'];
+ } else {
+ $strategyFlags = '';
+ }
+
+ // Add two flags if we are in Dry run.
+ if ($dryRun === true) {
+ $dryRunFlags = array(
+ '--dry-run',
+ '--itemize-changes',
+ '--omit-dir-times'
+ );
+ $strategyFlags = str_replace($dryRunFlags, '', $strategyFlags);
+ $strategyFlags .= implode(' ', $dryRunFlags);
+ }
+
+ $command = 'rsync -avz '
+ . $strategyFlags . ' '
+ . '--rsh="ssh ' . $this->getConfig()->getHostIdentityFileOption() . '-p' . $this->getConfig()->getHostPort() . '" '
+ . $this->excludes($excludes) . ' '
+ . $this->excludesListFile($excludesListFilePath) . ' '
+ . $this->getConfig()->deployment('from') . ' '
+ . ($this->getConfig()->deployment('user') ? $this->getConfig()->deployment('user') . '@' : '')
+ . $this->getConfig()->getHostName() . ':' . $deployToDirectory;
+
+ return $command;
+ }
+
+ /**
+ * @param bool|FALSE $dryRun
+ * @return string
+ */
+ protected function getDeployToDirectory($dryRun = false)
+ {
+ $deployTo = rtrim($this->getConfig()->deployment('to'), '/');
+
+ // If releases aren't enabled, deploy dir is simply deploy to config.
+ if ($this->getConfig()->release('enabled', false) !== true) {
+ return $deployTo;
+ }
+
+ // If dryrun, check from the latest release.
+ if ($dryRun === true) {
+ $release = ListTask::getCurrentRelease();
+ }
+ else {
+ $release = $this->getConfig()->getReleaseId();
+ }
+ $releasesDirectory = $this->getConfig()->release('directory', 'releases');
+ $deployToDirectory = $deployTo . '/' . $releasesDirectory . '/' . $release;
+
+ return $deployToDirectory;
+ }
+
+ /**
+ * @return bool
+ */
+ public function dryRun()
+ {
+ Console::output('');
+ if ($this->getConfig()->release('enabled', false) === true) {
+ $currentRelease = ListTask::getCurrentRelease();
+ Console::output('Checking files on the latest release '.$currentRelease.' on host '.$this->getConfig()->getHostName().'...', 2, 1);
+ }
+ else {
+ Console::output('Checking files on host '.$this->getConfig()->getHostName().'...', 2, 1);
+ }
+
+ // Get deploy command with dryRun option (true).
+ $command = $this->getDeployCommand(true);
+ $result = $this->runCommandLocal($command, $output);
+
+ // Put each line in an array (CHR(10) = Carriage return).
+ $lines = explode(CHR(10), $output);
+ if (count($lines)) {
+ Console::output('');
+ Console::output('---------- Dry run result ------------', 2, 1);
+ foreach ($lines as $key => $line) {
+ Console::output($line, 2, 1);
+ }
+ Console::output('---------- End Dry run ---------------', 2, 1);
+ }
+ return $result;
+
+ }
+
/**
* Syncs the Local Code to the Remote Host
* @see \Mage\Task\AbstractTask::run()
*/
public function run()
{
+ $excludes = $this->getExcludes();
+
+ // Launch dry run mode
+ if ($this->getConfig()->getParameter('dry-run') === true) {
+ return $this->dryRun();
+ }
+
$this->checkOverrideRelease();
- $excludes = $this->getExcludes();
- $excludesListFilePath = $this->getConfig()->deployment('excludes_file', '');
-
// If we are working with releases
- $deployToDirectory = $this->getConfig()->deployment('to');
if ($this->getConfig()->release('enabled', false) === true) {
+
$releasesDirectory = $this->getConfig()->release('directory', 'releases');
$symlink = $this->getConfig()->release('symlink', 'current');
$currentRelease = false;
- $deployToDirectory = rtrim($this->getConfig()->deployment('to'), '/')
- . '/' . $releasesDirectory
- . '/' . $this->getConfig()->getReleaseId();
+
+ $deployToDirectory = $this->getDeployToDirectory();
Console::log('Deploy to ' . $deployToDirectory);
$resultFetch = $this->runCommandRemote('ls -ld ' . $symlink . ' | cut -d"/" -f2', $currentRelease);
@@ -86,23 +193,7 @@ class RsyncTask extends BaseStrategyTaskAbstract implements IsReleaseAware
}
}
- // Strategy Flags
- $strategyFlags = $this->getConfig()->deployment('strategy_flags', $this->getConfig()->general('strategy_flags', array()));
- if (isset($strategyFlags['rsync'])) {
- $strategyFlags = $strategyFlags['rsync'];
- } else {
- $strategyFlags = '';
- }
-
- $command = 'rsync -avz '
- . $strategyFlags . ' '
- . '--rsh="ssh ' . $this->getConfig()->getHostIdentityFileOption() . '-p' . $this->getConfig()->getHostPort() . '" '
- . $this->excludes($excludes) . ' '
- . $this->excludesListFile($excludesListFilePath) . ' '
- . $this->getConfig()->deployment('from') . ' '
- . ($this->getConfig()->deployment('user') ? $this->getConfig()->deployment('user') . '@' : '')
- . $this->getConfig()->getHostName() . ':' . $deployToDirectory;
-
+ $command = $this->getDeployCommand();
$result = $this->runCommandLocal($command);
return $result;
diff --git a/Mage/Task/BuiltIn/Releases/ListTask.php b/Mage/Task/BuiltIn/Releases/ListTask.php
index 9b86852..05426f9 100644
--- a/Mage/Task/BuiltIn/Releases/ListTask.php
+++ b/Mage/Task/BuiltIn/Releases/ListTask.php
@@ -45,9 +45,7 @@ class ListTask extends AbstractTask implements IsReleaseAware
$releases = ($output == '') ? array() : explode(PHP_EOL, $output);
// Get Current
- $result = $this->runCommandRemote('ls -l ' . $symlink, $output) && $result;
- $currentRelease = explode('/', $output);
- $currentRelease = trim(array_pop($currentRelease));
+ $currentRelease = $this->getCurrentRelease();
if (count($releases) == 0) {
Console::output('No releases available ... ', 2);
@@ -92,6 +90,18 @@ class ListTask extends AbstractTask implements IsReleaseAware
}
}
+ /**
+ * Get the latest release.
+ */
+ public function getCurrentRelease()
+ {
+ $symlink = $this->getConfig()->release('symlink', 'current');
+ $this->runCommandRemote('ls -l ' . $symlink, $output);
+ $currentRelease = explode('/', $output);
+ $currentRelease = trim(array_pop($currentRelease));
+ return $currentRelease;
+ }
+
/**
* Calculates a Human Readable Time Difference
* @param string $releaseDate
diff --git a/docs/commands.txt b/docs/commands.txt
index 178bad2..424ede9 100644
--- a/docs/commands.txt
+++ b/docs/commands.txt
@@ -30,6 +30,9 @@ mage deploy to:production
# Deploys Application to Production environment, overriding the current release
mage deploy to:production --overrideRelease
+# Don't deploy application, only output the files to be deployed (works with Rsync strategy only).
+mage deploy to:production --dry-run
+
# Locks deployment to Production environment
mage lock to:production
diff --git a/docs/example-config/.mage/config/environment/production.yml b/docs/example-config/.mage/config/environment/production.yml
index 1af997f..96791e1 100644
--- a/docs/example-config/.mage/config/environment/production.yml
+++ b/docs/example-config/.mage/config/environment/production.yml
@@ -14,6 +14,8 @@ hosts:
- s01.example.com
- s02.example.com
tasks:
+ pre-dryrun:
+# - grunt
pre-deploy:
- scm/update
on-deploy:
diff --git a/docs/example-config/.mage/config/environment/staging.yml b/docs/example-config/.mage/config/environment/staging.yml
index 51f4f06..e5e9a55 100644
--- a/docs/example-config/.mage/config/environment/staging.yml
+++ b/docs/example-config/.mage/config/environment/staging.yml
@@ -14,6 +14,8 @@ hosts:
- localhost
- 127.0.0.1
tasks:
+ pre-dryrun:
+# - grunt
pre-deploy:
# - sampleTask
# - failTask