diff --git a/Mage/Command/AbstractCommand.php b/Mage/Command/AbstractCommand.php index f69b0ef..0d45d5f 100644 --- a/Mage/Command/AbstractCommand.php +++ b/Mage/Command/AbstractCommand.php @@ -26,6 +26,34 @@ abstract class AbstractCommand */ protected $config = null; + /** + * Command's help message + * + * @var string + */ + private $helpMessage; + + /** + * Usage examples. + * + * @var array + */ + private $usageExamples = array(); + + /** + * Command's syntax message + * + * @var string + */ + private $syntaxMessage; + + /** + * Command name + * + * @var string + */ + private $name; + /** * Runs the Command * @return integer exit code @@ -52,4 +80,112 @@ abstract class AbstractCommand { return $this->config; } + + /** + * Sets command name + * + * @param string $name Command name + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Sets command's help message + * + * @param string $message Command's help message + * @return $this + */ + public function setHelpMessage($message) + { + $this->helpMessage = $message; + + return $this; + } + + /** + * Adds command's usage example + * + * @param string $snippet Example's snippet + * @param string $description Example's description + * @return $this + */ + public function addUsageExample($snippet, $description = '') + { + array_push($this->usageExamples, array($snippet, $description)); + + return $this; + } + + /** + * Sets command's syntax message + * + * @param string $message Syntax message + * @return $this + */ + public function setSyntaxMessage($message) + { + $this->syntaxMessage = $message; + + return $this; + } + + /** + * Returns formatted command info + * + * @return string + */ + public function getInfoMessage() + { + $indent = str_repeat(" ", 4); + + $output = ""; + if (!empty($this->name)) { + $output .= "\n"; + $output .= "Command: "; + $output .= $this->name; + } + + if (!empty($this->helpMessage)) { + $output .= "\n"; + $output .= "{$this->helpMessage}\n"; + } + + if (!empty($this->syntaxMessage)) { + $output .= "\n"; + $output .= "Syntax:\n"; + $output .= "$indent{$this->syntaxMessage}"; + $output .= "\n"; + } + + if (!empty($this->usageExamples)) { + $output .= "\n"; + $output .= "Usage examples:\n"; + foreach ($this->usageExamples as $example) { + $snippet = $example[0]; + $description = $example[1]; + $output .= "$indent* "; + if (!empty($description)) { + $description = rtrim($description, ': ') . ":"; + $output .= $description; + $output .= "\n$indent$indent"; + } + + $output .= "$snippet"; + $output .= "\n"; + } + } + + if (empty($output)) { + $output .= "\n"; + $output .= "Sorry, there's no help for this command at the moment."; + $output .= "\n"; + } + + return $output; + } } diff --git a/Mage/Command/BuiltIn/AddCommand.php b/Mage/Command/BuiltIn/AddCommand.php index 26d9a37..7932e5c 100644 --- a/Mage/Command/BuiltIn/AddCommand.php +++ b/Mage/Command/BuiltIn/AddCommand.php @@ -23,6 +23,21 @@ use Exception; */ class AddCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Add command') + ->setHelpMessage('Generates new config for Magallanes. For now, only adding a environment is possible') + ->setSyntaxMessage('mage add [environment] [--name=env_name] [--enableReleases]') + ->addUsageExample( + 'mage add environment --name=production', + 'Add a production environment' + ) + ->addUsageExample( + 'mage add environment --name=qa --enableReleases', + 'Add a QA environment and enable releasing' + ); + } + /** * Adds new Configuration Elements * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/CompileCommand.php b/Mage/Command/BuiltIn/CompileCommand.php index ab92fd2..38a8049 100644 --- a/Mage/Command/BuiltIn/CompileCommand.php +++ b/Mage/Command/BuiltIn/CompileCommand.php @@ -33,6 +33,10 @@ class CompileCommand extends AbstractCommand } $this->compiler = $compiler; + + $this->setName('Compile command') + ->setHelpMessage('Compiles Magallanes to mage.phar file') + ->setSyntaxMessage('mage compile'); } /** diff --git a/Mage/Command/BuiltIn/DeployCommand.php b/Mage/Command/BuiltIn/DeployCommand.php index 9105792..014dea6 100644 --- a/Mage/Command/BuiltIn/DeployCommand.php +++ b/Mage/Command/BuiltIn/DeployCommand.php @@ -103,6 +103,22 @@ class DeployCommand extends AbstractCommand implements RequiresEnvironment */ protected static $failedTasks = 0; + public function __construct() + { + $this->setName('Deploy command') + ->setHelpMessage('Deploys the project into target environment') + ->setSyntaxMessage('mage deploy to:[environment_name]') + ->addUsageExample( + 'mage deploy to:production', + 'Deploy the project into production environment' + ) + ->addUsageExample( + 'mage deploy to:production --overrideRelease', + 'Deploy the project into production environment ' + . 'but skip SkipOnOverride aware tasks' + ); + } + /** * Returns the Status of the Deployment * diff --git a/Mage/Command/BuiltIn/InitCommand.php b/Mage/Command/BuiltIn/InitCommand.php index 326f0e9..a363583 100644 --- a/Mage/Command/BuiltIn/InitCommand.php +++ b/Mage/Command/BuiltIn/InitCommand.php @@ -20,6 +20,20 @@ use Mage\Console; */ class InitCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Initialize command') + ->setHelpMessage('Initialize Magallanes project, create .mage directory with starter configs') + ->setSyntaxMessage('mage init --name=[project_name] [--email=[author_email]]') + ->addUsageExample( + 'mage init --name="My awesome project"', + 'Initialize "My awesome project" configuration' + ) + ->addUsageExample( + 'mage init --name="My project" --email="john.smith@example.com"', + 'Initialize "My project" configuration with email notification enabled for john.smith@example.com' + ); + } /** * Command for Initalize a new Configuration Proyect diff --git a/Mage/Command/BuiltIn/InstallCommand.php b/Mage/Command/BuiltIn/InstallCommand.php index 530e1d7..ffa1dbe 100644 --- a/Mage/Command/BuiltIn/InstallCommand.php +++ b/Mage/Command/BuiltIn/InstallCommand.php @@ -20,6 +20,24 @@ use Mage\Console; */ class InstallCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Install command') + ->setHelpMessage( + 'Installs Magallanes system-widely.' + . ' By default, Magallanes\' going to be installed in /opt/magallanes' + ) + ->setSyntaxMessage('mage install [--installDir=[install_directory]] [--systemWide]') + ->addUsageExample( + 'mage install --installDir=/src/projects/Magellanes', + 'Install Magallanes at /src/projects/Magallanes directory' + ) + ->addUsageExample( + 'mage install --systemWide', + 'Install Magallanes at default directory and creates a symlink in /usr/bin/mage' + ); + } + /** * Installs Magallanes * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/ListCommand.php b/Mage/Command/BuiltIn/ListCommand.php index d7dd082..b0ff426 100644 --- a/Mage/Command/BuiltIn/ListCommand.php +++ b/Mage/Command/BuiltIn/ListCommand.php @@ -23,6 +23,17 @@ use Exception; */ class ListCommand extends AbstractCommand { + public function __construct() + { + $this->setName('List command') + ->setHelpMessage('List available configurations. For now, only environments listing available') + ->setSyntaxMessage('mage list [environments]') + ->addUsageExample( + 'mage list environments', + 'List currently configured environments' + ); + } + /** * Command for Listing Configuration Elements * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/LockCommand.php b/Mage/Command/BuiltIn/LockCommand.php index 09df123..9e00862 100644 --- a/Mage/Command/BuiltIn/LockCommand.php +++ b/Mage/Command/BuiltIn/LockCommand.php @@ -21,6 +21,21 @@ use Mage\Console; */ class LockCommand extends AbstractCommand implements RequiresEnvironment { + public function __construct() + { + $this->setName('Lock command') + ->setHelpMessage( + "Locks the deployment to given environment and creates a lock file " + . "with lock reason and lock performer.\n" + . "You are going to be prompted to provide this information" + ) + ->setSyntaxMessage('mage lock to:[environment_name]') + ->addUsageExample( + 'mage lock to:production', + 'Create a lock to production environment deployment' + ); + } + /** * Locks the Deployment to a Environment * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/ReleasesCommand.php b/Mage/Command/BuiltIn/ReleasesCommand.php index 3a40439..ddbb712 100644 --- a/Mage/Command/BuiltIn/ReleasesCommand.php +++ b/Mage/Command/BuiltIn/ReleasesCommand.php @@ -22,6 +22,36 @@ use Mage\Console; */ class ReleasesCommand extends AbstractCommand implements RequiresEnvironment { + public function __construct() + { + $this->setName('Releases management command') + ->setHelpMessage('Manages releases') + ->setSyntaxMessage( + 'mage releases [list|rollback [--release=[release_id]]] ' + . 'to:[environment_name] [--deleteCurrent]' + ) + ->addUsageExample( + 'mage releases list to:production', + 'List releases on production environment' + ) + ->addUsageExample( + 'mage releases rollback --release=20120101172148 to:production', + 'Rollback 20120101172148 release on production environment' + ) + ->addUsageExample( + 'mage releases rollback --release=-1 to:production', + 'Rollback list release -1 release on production environment' + ) + ->addUsageExample( + 'mage releases rollback --release=0 to:production', + 'Rollback last release on production environment' + ) + ->addUsageExample( + 'mage releases rollback -1 to:production --deleteCurrent', + 'Rollbacks the last release -1 release and removes current release' + ); + } + /** * List the Releases, Rollback to a Release * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/RollbackCommand.php b/Mage/Command/BuiltIn/RollbackCommand.php index dea92b0..88db0f9 100644 --- a/Mage/Command/BuiltIn/RollbackCommand.php +++ b/Mage/Command/BuiltIn/RollbackCommand.php @@ -22,6 +22,24 @@ use Mage\Console; */ class RollbackCommand extends AbstractCommand implements RequiresEnvironment { + public function __construct() + { + $this->setName('Rollback command') + ->setHelpMessage('Rollbacks the release by given release id or index') + ->setSyntaxMessage('mage rollback [releaseId] to:[environment_name]') + ->addUsageExample( + 'mage rollback 20120101172148 to:production', + 'Rollbacks the 20120101172148 release on production environment' + ) + ->addUsageExample( + 'mage rollback -1 to:production', + 'Rollbacks the last release -1 release' + ) + ->addUsageExample( + 'mage rollback -1 to:production --deleteCurrent', + 'Rollbacks the last release -1 release and removes current release' + ); + } /** * Rollback a release * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/UnlockCommand.php b/Mage/Command/BuiltIn/UnlockCommand.php index 2c7d324..874ccef 100644 --- a/Mage/Command/BuiltIn/UnlockCommand.php +++ b/Mage/Command/BuiltIn/UnlockCommand.php @@ -21,6 +21,17 @@ use Mage\Console; */ class UnlockCommand extends AbstractCommand implements RequiresEnvironment { + public function __construct() + { + $this->setName('Unlock command') + ->setHelpMessage('Unlocks deployment for given environment') + ->setSyntaxMessage('mage unlock to:[environment_name]') + ->addUsageExample( + 'mage unlock to:production', + 'Removes the lock form production environment deployment' + ); + } + /** * Unlocks an Environment * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/UpdateCommand.php b/Mage/Command/BuiltIn/UpdateCommand.php index 30cff52..699c90a 100644 --- a/Mage/Command/BuiltIn/UpdateCommand.php +++ b/Mage/Command/BuiltIn/UpdateCommand.php @@ -21,6 +21,13 @@ use Mage\Console; */ class UpdateCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Update command') + ->setHelpMessage('Updates the SCM base code') + ->setSyntaxMessage('mage update'); + } + /** * Updates the SCM Base Code * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Command/BuiltIn/UpgradeCommand.php b/Mage/Command/BuiltIn/UpgradeCommand.php index a9861ac..c6083ee 100644 --- a/Mage/Command/BuiltIn/UpgradeCommand.php +++ b/Mage/Command/BuiltIn/UpgradeCommand.php @@ -20,6 +20,13 @@ use Mage\Console; */ class UpgradeCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Upgrade command') + ->setHelpMessage('Upgrades Magallanes') + ->setSyntaxMessage('mage upgrade'); + } + /** * Source for downloading * @var string diff --git a/Mage/Command/BuiltIn/VersionCommand.php b/Mage/Command/BuiltIn/VersionCommand.php index 0c7938c..665e559 100644 --- a/Mage/Command/BuiltIn/VersionCommand.php +++ b/Mage/Command/BuiltIn/VersionCommand.php @@ -20,6 +20,12 @@ use Mage\Console; */ class VersionCommand extends AbstractCommand { + public function __construct() + { + $this->setName('Version command') + ->setHelpMessage('Displays the current version of Magallanes') + ->setSyntaxMessage('mage version'); + } /** * Display the Magallanes Version * @see \Mage\Command\AbstractCommand::run() diff --git a/Mage/Console.php b/Mage/Console.php index d9fcbb2..ef29492 100644 --- a/Mage/Console.php +++ b/Mage/Console.php @@ -13,6 +13,7 @@ namespace Mage; use Mage\Command\Factory; use Mage\Command\RequiresEnvironment; use Mage\Console\Colors; + use Exception; use RecursiveDirectoryIterator; use SplFileInfo; @@ -94,6 +95,7 @@ class Console try { // Load configuration $config->load($arguments); + } catch (Exception $exception) { $configError = $exception->getMessage(); } @@ -117,6 +119,7 @@ class Console if ($showGreetings) { if (!self::$logEnabled) { self::output('Starting Magallanes', 0, 2); + } else { self::output('Starting Magallanes', 0, 1); self::log("Logging enabled"); @@ -127,15 +130,20 @@ class Console // Run Command - Check if there is a Configuration Error if ($configError !== false) { self::output('' . $configError . '', 1, 2); + } else { // Run Command and check for Command Requirements try { $command = Factory::get($commandName, $config); - if ($command instanceof RequiresEnvironment) { - if ($config->getEnvironment() === false) { - throw new Exception('You must specify an environment for this command.'); - } + if ($config->getParameter('help')) { + self::output($command->getInfoMessage(), 2); + + return 0; + } + + if ($command instanceof RequiresEnvironment && $config->getEnvironment() === false) { + throw new Exception('You must specify an environment for this command.'); } // Run the Command @@ -306,4 +314,5 @@ class Console || self::$config->general('verbose_logging') || self::$config->environmentConfig('verbose_logging', false); } + } diff --git a/tests/MageTest/Command/AbstractCommandTest.php b/tests/MageTest/Command/AbstractCommandTest.php index fed7df5..cd21930 100644 --- a/tests/MageTest/Command/AbstractCommandTest.php +++ b/tests/MageTest/Command/AbstractCommandTest.php @@ -44,4 +44,228 @@ class AbstractCommandTest extends BaseTest $configMock = $this->getMock('Mage\Config'); $this->doTestGetter($this->abstractCommand, 'config', $configMock); } + + public function infoMessageProvider() + { + return array( + 'happy_path' => array( + 'name' => 'Example command', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights' + ) + ), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "Command: Example command\n" + . "This command does everything you want to\n" + . "\n" + . "Syntax:\n" + . " mage example [light]\n" + . "\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + 'no_help_message' => array( + 'name' => 'Example command', + 'helpMessage' => '', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights' + ) + ), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "Command: Example command\n" + . "Syntax:\n" + . " mage example [light]\n" + . "\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + 'no_examples' => array( + 'name' => 'Example command', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array(), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "Command: Example command\n" + . "This command does everything you want to\n" + . "\n" + . "Syntax:\n" + . " mage example [light]\n" + ), + "no_syntax" => array( + 'name' => 'Example command', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights' + ) + ), + 'syntax' => '', + 'output' => "\n" + . "Command: Example command\n" + . "This command does everything you want to\n" + . "\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + "stripping_colons" => array( + 'name' => 'Example command', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command : ' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights: ' + ) + ), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "Command: Example command\n" + . "This command does everything you want to\n" + . "\n" + . "Syntax:\n" + . " mage example [light]\n" + . "\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + "only_help" => array( + 'name' => 'Example command', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array(), + 'syntax' => '', + 'output' => "\n" + . "Command: Example command\n" + . "This command does everything you want to\n" + ), + "only_examples" => array( + 'name' => 'Example command', + 'helpMessage' => '', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights' + ) + ), + 'syntax' => '', + 'output' => "\n" + . "Command: Example command\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + "only_syntax" => array( + 'name' => 'Example command', + 'helpMessage' => '', + 'examples' => array(), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "Command: Example command\n" + . "Syntax:\n" + . " mage example [light]\n" + ), + "no_name" => array( + 'name' => '', + 'helpMessage' => 'This command does everything you want to', + 'examples' => array( + array( + 'snippet' => 'mage example', + 'description' => 'Default command' + ), + array( + 'snippet' => 'mage example light', + 'description' => 'Runs the command with lights' + ) + ), + 'syntax' => 'mage example [light]', + 'output' => "\n" + . "This command does everything you want to\n" + . "\n" + . "Syntax:\n" + . " mage example [light]\n" + . "\n" + . "Usage examples:\n" + . " * Default command:\n" + . " mage example\n" + . " * Runs the command with lights:\n" + . " mage example light\n" + ), + "no_info_at_all" => array( + 'name' => '', + 'helpMessage' => '', + 'examples' => array(), + 'syntax' => '', + 'output' => "\n" + . "Sorry, there's no help for this command at the moment.\n" + ) + ); + } + + /** + * @covers ::getInfoMessage + * @covers ::setHelpMessage + * @covers ::addUsageExample + * @covers ::setSyntaxMessage + * @covers ::setName + * + * @dataProvider infoMessageProvider + */ + public function testGetInfoMessage($name, $helpMessage, $examples, $syntax, $expectedMessage) + { + /** @var AbstractCommand $command */ + $command = $this->getMockForAbstractClass('Mage\Command\AbstractCommand'); + + $command->setName($name); + + foreach ($examples as $example) { + $command->addUsageExample($example['snippet'], $example['description']); + } + + $command->setHelpMessage($helpMessage); + $command->setSyntaxMessage($syntax); + + $actualMessage = $command->getInfoMessage(); + $this->assertEquals($expectedMessage, $actualMessage); + + } } diff --git a/tests/MageTest/Command/BuiltIn/CompileCommandTest.php b/tests/MageTest/Command/BuiltIn/CompileCommandTest.php index 44b4486..77af2a3 100644 --- a/tests/MageTest/Command/BuiltIn/CompileCommandTest.php +++ b/tests/MageTest/Command/BuiltIn/CompileCommandTest.php @@ -16,6 +16,7 @@ use malkusch\phpmock\MockBuilder; * @uses malkusch\phpmock\MockBuilder * @uses Mage\Console * @uses Mage\Console\Colors + * @uses Mage\Command\AbstractCommand */ class CompileCommandTest extends BaseTest { diff --git a/tests/MageTest/Command/BuiltIn/ListCommandTest.php b/tests/MageTest/Command/BuiltIn/ListCommandTest.php index 4d193e1..36c1389 100644 --- a/tests/MageTest/Command/BuiltIn/ListCommandTest.php +++ b/tests/MageTest/Command/BuiltIn/ListCommandTest.php @@ -93,6 +93,7 @@ class ListCommandTest extends BaseTest } /** + * @covers ::__construct * @covers ::run * @covers ::listEnvironments * @dataProvider listEnvironmentsProvider @@ -109,6 +110,7 @@ class ListCommandTest extends BaseTest } /** + * @covers ::__construct * @covers ::run */ public function testRunWithInvalidCommand() diff --git a/tests/MageTest/Command/BuiltIn/LockCommandTest.php b/tests/MageTest/Command/BuiltIn/LockCommandTest.php index e56b63f..84ab390 100644 --- a/tests/MageTest/Command/BuiltIn/LockCommandTest.php +++ b/tests/MageTest/Command/BuiltIn/LockCommandTest.php @@ -173,6 +173,7 @@ class LockCommandTest extends BaseTest } /** + * @covers ::__construct * @covers ::run * @dataProvider lockCommandProvider */ diff --git a/tests/MageTest/Command/BuiltIn/UnlockCommandTest.php b/tests/MageTest/Command/BuiltIn/UnlockCommandTest.php index 1732811..be22134 100644 --- a/tests/MageTest/Command/BuiltIn/UnlockCommandTest.php +++ b/tests/MageTest/Command/BuiltIn/UnlockCommandTest.php @@ -98,6 +98,7 @@ class UnlockCommandTest extends BaseTest } /** + * @covers ::__construct * @covers ::run * @dataProvider runProvider */ diff --git a/tests/MageTest/Command/BuiltIn/VersionCommandTest.php b/tests/MageTest/Command/BuiltIn/VersionCommandTest.php index 6565510..d6a85db 100644 --- a/tests/MageTest/Command/BuiltIn/VersionCommandTest.php +++ b/tests/MageTest/Command/BuiltIn/VersionCommandTest.php @@ -8,16 +8,18 @@ use MageTest\TestHelper\BaseTest; use PHPUnit_Framework_TestCase; /** - * @coversDefaultClass Mage\Command\BuiltIn\VersionCommands + * @coversDefaultClass Mage\Command\BuiltIn\VersionCommand * @group Mage_Command_BuildIn_VersionCommand * @uses Mage\Console * @uses Mage\Console\Colors + * @uses Mage\Command\AbstractCommand */ class VersionCommandTest extends BaseTest { /** * @group 175 - * @covers Mage\Command\BuiltIn\VersionCommand::run() + * @covers ::__construct + * @covers ::run() */ public function testRun() {