diff --git a/src/Liip/RMT/Action/BaseAction.php b/src/Liip/RMT/Action/BaseAction.php index 502cb41c..0a365ddb 100644 --- a/src/Liip/RMT/Action/BaseAction.php +++ b/src/Liip/RMT/Action/BaseAction.php @@ -51,6 +51,20 @@ public function getInformationRequests() return array(); } + /** + * This method is called after all registered information collectors have + * been called to validate that the action has all necessary information + * if anything is missing an exception should be thrown. + * When called, the 'current-version' and 'new-version' parameters are + * already known, so a check can be made on those. + * + * @throws \Exception + */ + public function validateContext() + { + + } + /** * A common method to confirm success to the user */ diff --git a/src/Liip/RMT/Action/VcsTagAction.php b/src/Liip/RMT/Action/VcsTagAction.php index ecff5aa5..8ab9082a 100644 --- a/src/Liip/RMT/Action/VcsTagAction.php +++ b/src/Liip/RMT/Action/VcsTagAction.php @@ -20,11 +20,20 @@ class VcsTagAction extends BaseAction { public function execute() { - Context::get('vcs')->createTag( - Context::get('vcs')->getTagFromVersion( - Context::getParam('new-version') - ) - ); + Context::get('vcs')->createTag($this->getVersion()); $this->confirmSuccess(); } + + protected function getVersion() + { + return Context::get('vcs')->getTagFromVersion( + Context::getParam('new-version') + ); + } + + public function validateContext() + { + parent::validateContext(); + Context::get('vcs')->validateTag($this->getVersion()); + } } diff --git a/src/Liip/RMT/Command/ReleaseCommand.php b/src/Liip/RMT/Command/ReleaseCommand.php index c799ba94..a5c5f5be 100644 --- a/src/Liip/RMT/Command/ReleaseCommand.php +++ b/src/Liip/RMT/Command/ReleaseCommand.php @@ -136,13 +136,20 @@ protected function execute(InputInterface $input, OutputInterface $output) ); Context::getInstance()->setParameter('new-version', $newVersion); + foreach (array('prerequisites', 'pre-release-actions', 'post-release-actions') as $listName) { + foreach(Context::getInstance()->getList($listName) as $action) { + $action->validateContext(); + } + } + Context::get('version-persister')->validateContext(); + $this->executeActionListIfExist('pre-release-actions'); $this->getOutput()->writeSmallTitle('Release process'); $this->getOutput()->indent(); $this->getOutput()->writeln("A new version named [$newVersion] is going to be released"); - Context::get('version-persister')->save($newVersion); + Context::get('version-persister')->save(); $this->getOutput()->writeln('Release: Success'); $this->getOutput()->unIndent(); diff --git a/src/Liip/RMT/Exception/InvalidTagNameException.php b/src/Liip/RMT/Exception/InvalidTagNameException.php new file mode 100644 index 00000000..30d59dbe --- /dev/null +++ b/src/Liip/RMT/Exception/InvalidTagNameException.php @@ -0,0 +1,18 @@ +executeGitCommand('tag'); } + public function validateTag($tagName) + { + try { + $this->executeGitCommand("check-ref-format --allow-onelevel $tagName"); + } catch(Exception $e) { + throw new InvalidTagNameException("'$tagName' is an invalid tag name for git."); + } + + if(in_array($tagName, $this->getTags())) { + throw new TagAlreadyExistsException("'$tagName' already exists."); + } + } + public function createTag($tagName) { return $this->executeGitCommand("tag $tagName"); @@ -79,6 +96,11 @@ public function getCurrentBranch() throw new \Liip\RMT\Exception('Not currently on any branch'); } + /** + * @param $cmd + * @throws \Liip\RMT\Exception + * @return string[] + */ protected function executeGitCommand($cmd) { // Avoid using some commands in dry mode @@ -86,7 +108,7 @@ protected function executeGitCommand($cmd) if ($cmd !== 'tag') { $cmdWords = explode(' ', $cmd); if (in_array($cmdWords[0], array('tag', 'push', 'add', 'commit'))) { - return; + return []; } } } @@ -95,7 +117,7 @@ protected function executeGitCommand($cmd) $cmd = 'git ' . $cmd; exec($cmd, $result, $exitCode); if ($exitCode !== 0) { - throw new \Liip\RMT\Exception('Error while executing git command: ' . $cmd . "\n" . implode("\n", $result)); + throw new Exception('Error while executing git command: ' . $cmd . "\n" . implode("\n", $result)); } return $result; diff --git a/src/Liip/RMT/VCS/Hg.php b/src/Liip/RMT/VCS/Hg.php index 1fa713fc..1510a1aa 100644 --- a/src/Liip/RMT/VCS/Hg.php +++ b/src/Liip/RMT/VCS/Hg.php @@ -11,6 +11,9 @@ namespace Liip\RMT\VCS; +use Liip\RMT\Exception\InvalidTagNameException; +use Liip\RMT\Exception\TagAlreadyExistsException; + class Hg extends BaseVCS { protected $dryRun = false; @@ -53,6 +56,17 @@ public function getTags() return $tags; } + public function validateTag($tagName) + { + if(preg_match("/[:\r\n]/", $tagName) > 0 || preg_match("/^[0-9]*$/", $tagName) > 0) { + throw new InvalidTagNameException("'$tagName' is an invalid tag name for mercurial."); + } + + if(in_array($tagName, $this->getTags())) { + throw new TagAlreadyExistsException("'$tagName' already exists."); + } + } + public function createTag($tagName) { return $this->executeHgCommand("tag $tagName"); diff --git a/src/Liip/RMT/VCS/VCSInterface.php b/src/Liip/RMT/VCS/VCSInterface.php index a6a858e1..da1f725b 100644 --- a/src/Liip/RMT/VCS/VCSInterface.php +++ b/src/Liip/RMT/VCS/VCSInterface.php @@ -11,6 +11,9 @@ namespace Liip\RMT\VCS; +use Liip\RMT\Exception\InvalidTagNameException; +use Liip\RMT\Exception\TagAlreadyExistsException; + interface VCSInterface { /** @@ -25,6 +28,17 @@ public function getCurrentBranch(); */ public function getTags(); + /** + * Validate that a tag name is valid for the given VCS. If possible + * should also check if this tag already exists or if we can create + * it freely. + * + * @param $tagName + * @throws InvalidTagNameException in case the name is invalid + * @throws TagAlreadyExistsException in case the tag name already exists + */ + public function validateTag($tagName); + /** * Create a new tag at the current position * diff --git a/src/Liip/RMT/Version/Persister/ChangelogPersister.php b/src/Liip/RMT/Version/Persister/ChangelogPersister.php index e1140128..cb63bd3a 100644 --- a/src/Liip/RMT/Version/Persister/ChangelogPersister.php +++ b/src/Liip/RMT/Version/Persister/ChangelogPersister.php @@ -42,11 +42,11 @@ public function getCurrentVersion() return $this->changelogManager->getCurrentVersion(); } - public function save($versionNumber) + public function save() { $comment = Context::get('information-collector')->getValueFor('comment'); $type = Context::get('information-collector')->getValueFor('type', null); - $this->changelogManager->update($versionNumber, $comment, array('type' => $type)); + $this->changelogManager->update(Context::getParam('new-version'), $comment, array('type' => $type)); } public function getInformationRequests() @@ -54,6 +54,11 @@ public function getInformationRequests() return array('comment'); } + public function validateContext() + { + + } + public function init() { // TODO: Implement init() method. diff --git a/src/Liip/RMT/Version/Persister/PersisterInterface.php b/src/Liip/RMT/Version/Persister/PersisterInterface.php index 95940c8f..598b7acb 100644 --- a/src/Liip/RMT/Version/Persister/PersisterInterface.php +++ b/src/Liip/RMT/Version/Persister/PersisterInterface.php @@ -26,10 +26,12 @@ public function __construct($options = array()); */ public function getCurrentVersion(); - public function save($versionNumber); + public function save(); public function getInformationRequests(); + public function validateContext(); + // Use the very first time to init this persistence public function init(); } diff --git a/src/Liip/RMT/Version/Persister/VcsTagPersister.php b/src/Liip/RMT/Version/Persister/VcsTagPersister.php index b6af61f8..82431b31 100644 --- a/src/Liip/RMT/Version/Persister/VcsTagPersister.php +++ b/src/Liip/RMT/Version/Persister/VcsTagPersister.php @@ -46,13 +46,23 @@ public function getCurrentVersion() return array_pop($versions); } - public function save($versionNumber) + public function getNewVersion() { - $tagName = $this->getTagFromVersion($versionNumber); + return $this->getTagFromVersion(Context::getParam('new-version')); + } + + public function save() + { + $tagName = $this->getNewVersion(); Context::get('output')->writeln("Creation of a new VCS tag [$tagName]"); $this->vcs->createTag($tagName); } + public function validateContext() + { + Context::get('vcs')->validateTag($this->getNewVersion()); + } + public function init() { } diff --git a/test/Liip/RMT/Tests/Functional/HgTest.php b/test/Liip/RMT/Tests/Functional/HgTest.php index ad6b6459..ddd68301 100644 --- a/test/Liip/RMT/Tests/Functional/HgTest.php +++ b/test/Liip/RMT/Tests/Functional/HgTest.php @@ -22,13 +22,21 @@ public static function cleanTags($tags) }, $tags); } - public function testInitialVersion() + public function testNoSimpleGenerator() { $this->initHg(); $this->createConfig('simple', 'vcs-tag', array('vcs' => 'hg')); + exec('./RMT release -n --confirm-first 2> /dev/null', $result, $code); + $this->assertEquals($code, 1); + } + + public function testInitialVersion() + { + $this->initHg(); + $this->createConfig('simple', array('name' => 'vcs-tag', 'tag-prefix' => 'v'), array('vcs' => 'hg')); exec('./RMT release -n --confirm-first'); exec('hg tags', $tags); - $this->assertEquals(array('tip', '1'), static::cleanTags($tags)); + $this->assertEquals(array('tip', 'v1'), static::cleanTags($tags)); } public function testInitialVersionSemantic() @@ -43,13 +51,13 @@ public function testInitialVersionSemantic() public function testSimple() { $this->initHg(); - exec('hg tag 1'); - exec('hg tag 3'); + exec('hg tag v1'); + exec('hg tag v3'); exec('hg tag toto'); - $this->createConfig('simple', 'vcs-tag', array('vcs' => 'hg')); + $this->createConfig('simple', array('name' => 'vcs-tag', 'tag-prefix' => 'v'), array('vcs' => 'hg')); exec('./RMT release -n'); exec('hg tags', $tags); - $this->assertEquals(array('tip', '4', 'toto', '3', '1'), static::cleanTags($tags)); + $this->assertEquals(array('tip', 'v4', 'toto', 'v3', 'v1'), static::cleanTags($tags)); } public function testSemantic() @@ -65,12 +73,12 @@ public function testSemantic() public function testTagPrefix() { $this->initHg(); - exec('hg tag 2'); + exec('hg tag v2'); exec('hg tag v_1'); $this->createConfig('simple', array('name' => 'vcs-tag', 'tag-prefix' => 'v_'), array('vcs' => 'hg')); exec('./RMT release -n'); exec('hg tags', $tags); - $this->assertEquals(array('tip', 'v_2', 'v_1', '2'), static::cleanTags($tags)); + $this->assertEquals(array('tip', 'v_2', 'v_1', 'v2'), static::cleanTags($tags)); } public function testTagPrefixWithBranchNamePlaceHolder() diff --git a/test/Liip/RMT/Tests/Unit/VCS/GitTest.php b/test/Liip/RMT/Tests/Unit/VCS/GitTest.php index f96a938c..56ff64e5 100644 --- a/test/Liip/RMT/Tests/Unit/VCS/GitTest.php +++ b/test/Liip/RMT/Tests/Unit/VCS/GitTest.php @@ -60,6 +60,53 @@ public function testGetTags() $this->assertEquals(array('1.0.0', '1.1.0'), $vcs->getTags()); } + /** + * @dataProvider invalidTagNames + * @expectedException \Liip\RMT\Exception\InvalidTagNameException + */ + public function testInvalidateTag($tag) + { + $vcs = new Git(); + $vcs->validateTag($tag); + } + + public function invalidTagNames() + { + return array( + array("test..test"), + array("test*test"), + array('test[test'), + array("test?test"), + array("test\ntest"), + array("test\rtest"), + array("test."), + array("@"), + array("\\"), + ); + } + + /** + * @dataProvider validTagNames + */ + public function testValidateTag($tag) + { + $vcs = new Git(); + $vcs->validateTag($tag); + } + + public function validTagNames() + { + return array( + array('test/test'), + array('test'), + array(2345), + array('1.2'), + array('1.2.3'), + array('v1.2'), + array('v1.2.3'), + ); + } + public function testCreateTag() { $vcs = new Git(); diff --git a/test/Liip/RMT/Tests/Unit/VCS/HgTest.php b/test/Liip/RMT/Tests/Unit/VCS/HgTest.php index beef3c7a..0e9a4bf5 100644 --- a/test/Liip/RMT/Tests/Unit/VCS/HgTest.php +++ b/test/Liip/RMT/Tests/Unit/VCS/HgTest.php @@ -60,6 +60,47 @@ public function testGetTags() $this->assertEquals(array('tip', '1.1.0', '1.0.0'), $vcs->getTags()); } + /** + * @dataProvider invalidTagNames + * @expectedException \Liip\RMT\Exception\InvalidTagNameException + */ + public function testInvalidateTag($tag) + { + $vcs = new Hg(); + $vcs->validateTag($tag); + } + + public function invalidTagNames() + { + return array( + array(2345), + array('test:test'), + array("test\ntest"), + array("test\rtest"), + ); + } + + /** + * @dataProvider validTagNames + */ + public function testValidateTag($tag) + { + $vcs = new Hg(); + $vcs->validateTag($tag); + } + + public function validTagNames() + { + return array( + array('test/test'), + array('test'), + array('1.2'), + array('1.2.3'), + array('v1.2'), + array('v1.2.3'), + ); + } + public function testCreateTag() { $vcs = new Hg();