This commit is contained in:
Kamil Kuzminski 2017-04-14 19:20:07 +00:00 committed by GitHub
commit 686817ef20
6 changed files with 188 additions and 74 deletions

View file

@ -14,10 +14,10 @@ use Mage\Deploy\Strategy\StrategyInterface;
use Mage\Runtime\Exception\RuntimeException; use Mage\Runtime\Exception\RuntimeException;
use Mage\Runtime\Runtime; use Mage\Runtime\Runtime;
use Mage\Task\ExecuteOnRollbackInterface; use Mage\Task\ExecuteOnRollbackInterface;
use Mage\Task\AbstractTask;
use Mage\Task\Exception\ErrorException; use Mage\Task\Exception\ErrorException;
use Mage\Task\Exception\SkipException; use Mage\Task\Exception\SkipException;
use Mage\Task\TaskFactory; use Mage\Task\TaskFactory;
use Mage\Task\TaskInterface;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -185,8 +185,17 @@ class DeployCommand extends AbstractCommand
$succeededTasks = 0; $succeededTasks = 0;
foreach ($tasks as $taskName) { foreach ($tasks as $taskName) {
/** @var AbstractTask $task */ $options = null;
$task = $this->taskFactory->get($taskName);
// Handle the options
if (is_array($taskName)) {
$options = $taskName;
$taskName = key($options);
$options = $options[$taskName];
}
/** @var TaskInterface $task */
$task = $this->taskFactory->get($taskName, $options);
$output->write(sprintf(' Running <fg=magenta>%s</> ... ', $task->getDescription())); $output->write(sprintf(' Running <fg=magenta>%s</> ... ', $task->getDescription()));
$this->log(sprintf('Running task %s (%s)', $task->getDescription(), $task->getName())); $this->log(sprintf('Running task %s (%s)', $task->getDescription(), $task->getName()));

View file

@ -17,7 +17,7 @@ use Mage\Runtime\Runtime;
* *
* @author Andrés Montañez <andresmontanez@gmail.com> * @author Andrés Montañez <andresmontanez@gmail.com>
*/ */
abstract class AbstractTask abstract class AbstractTask implements TaskInterface
{ {
/** /**
* @var array Task custom options * @var array Task custom options
@ -29,27 +29,6 @@ abstract class AbstractTask
*/ */
protected $runtime; protected $runtime;
/**
* Get the Name/Code of the Task
*
* @return string
*/
abstract public function getName();
/**
* Get a short Description of the Task
*
* @return string
*/
abstract public function getDescription();
/**
* Executes the Command
*
* @return bool
*/
abstract public function execute();
/** /**
* Set additional Options for the Task * Set additional Options for the Task
* *

View file

@ -20,18 +20,21 @@ use ReflectionClass;
* Task Factory * Task Factory
* *
* @author Andrés Montañez <andresmontanez@gmail.com> * @author Andrés Montañez <andresmontanez@gmail.com>
* @author Kamil Kuzminski <https://github.com/qzminski>
*/ */
class TaskFactory class TaskFactory
{ {
/** /**
* Runtime
* @var Runtime * @var Runtime
*/ */
protected $runtime; private $runtime;
/** /**
* @var array Registered Tasks * Registered tasks
* @var TaskInterface[]
*/ */
protected $registeredTasks = []; private $tasks = [];
/** /**
* Constructor * Constructor
@ -48,11 +51,29 @@ class TaskFactory
* Add a Task * Add a Task
* *
* @param AbstractTask $task * @param AbstractTask $task
*
* @deprecated Deprecated since 3.0, to be removed in 4.0.
* Use the TaskFactory::addTask() instead.
*/ */
public function add(AbstractTask $task) public function add(AbstractTask $task)
{ {
@trigger_error(
'Using TaskFactory::add() has been deprecated and will no longer work in 4.0. Use the TaskFactory::addTask() instead.',
E_USER_DEPRECATED
);
$task->setRuntime($this->runtime); $task->setRuntime($this->runtime);
$this->registeredTasks[$task->getName()] = $task; $this->tasks[$task->getName()] = $task;
}
/**
* Add a task
*
* @param TaskInterface $task
*/
public function addTask(TaskInterface $task)
{
$this->tasks[$task->getName()] = $task;
} }
/** /**
@ -60,58 +81,101 @@ class TaskFactory
* in that case the class will be instantiated * in that case the class will be instantiated
* *
* @param string $name Name/Code or Class of the Task * @param string $name Name/Code or Class of the Task
* @return AbstractTask * @param array $options
*
* @return TaskInterface
*
* @throws RuntimeException * @throws RuntimeException
*/ */
public function get($name) public function get($name, array $options = null)
{ {
$options = []; // TODO: backwards compatibility, remove in 4.0
if (is_array($name)) { if (is_array($name)) {
$options = $name; $options = $name;
list($name) = array_keys($name); $name = key($options);
$options = $options[$name]; $options = $options[$name];
} }
if (array_key_exists($name, $this->registeredTasks)) { if (array_key_exists($name, $this->tasks)) {
/** @var AbstractTask $task */ $task = $this->tasks[$name];
$task = $this->registeredTasks[$name]; } else {
$task->setOptions($options); $task = $this->createTask($name);
return $task;
} elseif (class_exists($name)) {
$reflex = new ReflectionClass($name);
if ($reflex->isInstantiable()) {
$task = new $name();
if ($task instanceof AbstractTask) {
$task->setOptions($options);
$this->add($task);
return $task;
}
}
} }
throw new RuntimeException(sprintf('Invalid task name "%s"', $name)); $task->setRuntime($this->runtime);
if ($options !== null) {
$task->setOptions($options);
}
return $task;
} }
/** /**
* Load BuiltIn Tasks * Load the built-in tasks
*
* @throws RuntimeException
*/ */
protected function loadBuiltInTasks() private function loadBuiltInTasks()
{ {
$finder = new Finder(); $finder = new Finder();
$finder->files()->in(__DIR__ . '/BuiltIn')->name('*Task.php'); $finder->files()->in(__DIR__.'/BuiltIn')->name('*Task.php');
/** @var SplFileInfo $file */ /** @var SplFileInfo $file */
foreach ($finder as $file) { foreach ($finder as $file) {
$class = substr('\\Mage\\Task\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4); $class = substr('\\Mage\\Task\\BuiltIn\\'.str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
if (class_exists($class)) { $reflex = new ReflectionClass($class);
$reflex = new ReflectionClass($class);
if ($reflex->isInstantiable()) { // Some classes found in the folder can be abstract
$task = new $class(); if (!$reflex->isInstantiable()) {
if ($task instanceof AbstractTask) { continue;
$this->add($task);
}
}
} }
$this->createTask($class);
}
}
/**
* Create the task
*
* @param string $class
*
* @return TaskInterface
*
* @throws RuntimeException
*/
private function createTask($class)
{
$this->validateTask($class);
/** @var TaskInterface $task */
$task = new $class();
// Register the task
$this->addTask($task);
return $task;
}
/**
* Validate the task
*
* @param string $class
*
* @throws RuntimeException
*/
private function validateTask($class)
{
if (!class_exists($class)) {
throw new RuntimeException(sprintf('The class "%s" does not exist', $class));
}
$reflex = new ReflectionClass($class);
if (!$reflex->implementsInterface(TaskInterface::class)) {
throw new RuntimeException(
sprintf('The class "%s" must implement the "%s" interface', $class, TaskInterface::class)
);
} }
} }
} }

View file

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Magallanes package.
*
* (c) Andrés Montañez <andres@andresmontanez.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mage\Task;
use Mage\Runtime\Runtime;
/**
* The task interface
*
* @author Kamil Kuzminski <https://github.com/qzminski>
*/
interface TaskInterface
{
/**
* Get the Name/Code of the Task
*
* @return string
*/
public function getName();
/**
* Get a short Description of the Task
*
* @return string
*/
public function getDescription();
/**
* Executes the Command
*
* @return bool
*/
public function execute();
/**
* Set additional Options for the Task
*
* @param array $options Options
*
* @return TaskInterface
*/
public function setOptions($options = []);
/**
* Set the Runtime instance
*
* @param Runtime $runtime
*
* @return TaskInterface
*/
public function setRuntime(Runtime $runtime);
}

View file

@ -122,7 +122,7 @@ class DeployCommandMiscTasksTest extends TestCase
$tester->execute(['command' => $command->getName(), 'environment' => 'test']); $tester->execute(['command' => $command->getName(), 'environment' => 'test']);
$this->assertEquals(7, $tester->getStatusCode()); $this->assertEquals(7, $tester->getStatusCode());
$this->assertContains('Invalid task name "invalid/task"', $tester->getDisplay()); $this->assertContains('The class "invalid/task" does not exist', $tester->getDisplay());
} }
public function testBrokenGitBranch() public function testBrokenGitBranch()

View file

@ -18,29 +18,31 @@ use PHPUnit_Framework_TestCase as TestCase;
class TaskFactoryTest extends TestCase class TaskFactoryTest extends TestCase
{ {
public function testNonInstantiable() public function testClassNotExists()
{ {
$runtime = new Runtime();
$factory = new TaskFactory($runtime);
try { try {
$factory->get('Traversable'); $this->getFactory()->get('Foobar');
} catch (Exception $exception) { } catch (Exception $exception) {
$this->assertTrue($exception instanceof RuntimeException); $this->assertTrue($exception instanceof RuntimeException);
$this->assertEquals('Invalid task name "Traversable"', $exception->getMessage()); $this->assertEquals('The class "Foobar" does not exist', $exception->getMessage());
} }
} }
public function testNotExtendingAbstractTask() public function testNotImplementingInterface()
{ {
$runtime = new Runtime();
$factory = new TaskFactory($runtime);
try { try {
$factory->get('stdClass'); $this->getFactory()->get('stdClass');
} catch (Exception $exception) { } catch (Exception $exception) {
$this->assertTrue($exception instanceof RuntimeException); $this->assertTrue($exception instanceof RuntimeException);
$this->assertEquals('Invalid task name "stdClass"', $exception->getMessage()); $this->assertEquals(
'The class "stdClass" must implement the "Mage\Task\TaskInterface" interface',
$exception->getMessage()
);
} }
} }
private function getFactory()
{
return new TaskFactory(new Runtime());
}
} }