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\Runtime;
use Mage\Task\ExecuteOnRollbackInterface;
use Mage\Task\AbstractTask;
use Mage\Task\Exception\ErrorException;
use Mage\Task\Exception\SkipException;
use Mage\Task\TaskFactory;
use Mage\Task\TaskInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
@ -185,8 +185,17 @@ class DeployCommand extends AbstractCommand
$succeededTasks = 0;
foreach ($tasks as $taskName) {
/** @var AbstractTask $task */
$task = $this->taskFactory->get($taskName);
$options = null;
// 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()));
$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>
*/
abstract class AbstractTask
abstract class AbstractTask implements TaskInterface
{
/**
* @var array Task custom options
@ -29,27 +29,6 @@ abstract class AbstractTask
*/
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
*

View file

@ -20,18 +20,21 @@ use ReflectionClass;
* Task Factory
*
* @author Andrés Montañez <andresmontanez@gmail.com>
* @author Kamil Kuzminski <https://github.com/qzminski>
*/
class TaskFactory
{
/**
* Runtime
* @var Runtime
*/
protected $runtime;
private $runtime;
/**
* @var array Registered Tasks
* Registered tasks
* @var TaskInterface[]
*/
protected $registeredTasks = [];
private $tasks = [];
/**
* Constructor
@ -48,11 +51,29 @@ class TaskFactory
* Add a 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)
{
@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);
$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
*
* @param string $name Name/Code or Class of the Task
* @return AbstractTask
* @param array $options
*
* @return TaskInterface
*
* @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)) {
$options = $name;
list($name) = array_keys($name);
$name = key($options);
$options = $options[$name];
}
if (array_key_exists($name, $this->registeredTasks)) {
/** @var AbstractTask $task */
$task = $this->registeredTasks[$name];
$task->setOptions($options);
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;
}
}
if (array_key_exists($name, $this->tasks)) {
$task = $this->tasks[$name];
} else {
$task = $this->createTask($name);
}
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->files()->in(__DIR__ . '/BuiltIn')->name('*Task.php');
$finder->files()->in(__DIR__.'/BuiltIn')->name('*Task.php');
/** @var SplFileInfo $file */
foreach ($finder as $file) {
$class = substr('\\Mage\\Task\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
if (class_exists($class)) {
$reflex = new ReflectionClass($class);
if ($reflex->isInstantiable()) {
$task = new $class();
if ($task instanceof AbstractTask) {
$this->add($task);
}
}
$class = substr('\\Mage\\Task\\BuiltIn\\'.str_replace('/', '\\', $file->getRelativePathname()), 0, -4);
$reflex = new ReflectionClass($class);
// Some classes found in the folder can be abstract
if (!$reflex->isInstantiable()) {
continue;
}
$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']);
$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()

View file

@ -18,29 +18,31 @@ use PHPUnit_Framework_TestCase as TestCase;
class TaskFactoryTest extends TestCase
{
public function testNonInstantiable()
public function testClassNotExists()
{
$runtime = new Runtime();
$factory = new TaskFactory($runtime);
try {
$factory->get('Traversable');
$this->getFactory()->get('Foobar');
} catch (Exception $exception) {
$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 {
$factory->get('stdClass');
$this->getFactory()->get('stdClass');
} catch (Exception $exception) {
$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());
}
}