diff --git a/composer.json b/composer.json
index 24b9332..efc67a1 100644
--- a/composer.json
+++ b/composer.json
@@ -21,7 +21,7 @@
"require": {
"symfony/console": "^2.7",
"psr/log": "^1.0",
- "baleen/migrations": "^0.5",
+ "baleen/migrations": "^0.6",
"symfony/yaml": "^2.7",
"symfony/config": "^2.7",
"league/container": "^1.3",
diff --git a/src/Command/Timeline/AbstractTimelineCommand.php b/src/Command/Timeline/AbstractTimelineCommand.php
index a53b1f8..7353896 100644
--- a/src/Command/Timeline/AbstractTimelineCommand.php
+++ b/src/Command/Timeline/AbstractTimelineCommand.php
@@ -21,6 +21,7 @@
namespace Baleen\Cli\Command\Timeline;
use Baleen\Cli\Command\AbstractCommand;
+use Baleen\Migrations\Storage\StorageInterface;
use Baleen\Migrations\Timeline;
use Symfony\Component\Console\Input\InputOption;
@@ -33,17 +34,27 @@ abstract class AbstractTimelineCommand extends AbstractCommand
{
const COMMAND_GROUP = 'timeline';
const OPT_DRY_RUN = 'dry-run';
+ const OPT_NO_STORAGE = 'no-storage';
/** @var Timeline */
protected $timeline;
+ /** @var StorageInterface */
+ protected $storage;
+
/**
* @inheritDoc
*/
public function configure()
{
parent::configure();
- $this->addOption(self::OPT_DRY_RUN, 'd', InputOption::VALUE_NONE, 'Execute the migration on dry-run mode.');
+ $this->addOption(self::OPT_DRY_RUN, 'd', InputOption::VALUE_NONE, 'Execute the migration on dry-run mode.')
+ ->addOption(
+ self::OPT_NO_STORAGE,
+ null,
+ InputOption::VALUE_NONE,
+ 'Do not persist execution results to storage.'
+ );
}
/**
@@ -61,4 +72,20 @@ public function setTimeline(Timeline $timeline)
{
$this->timeline = $timeline;
}
+
+ /**
+ * @return StorageInterface
+ */
+ public function getStorage()
+ {
+ return $this->storage;
+ }
+
+ /**
+ * @param StorageInterface $storage
+ */
+ public function setStorage($storage)
+ {
+ $this->storage = $storage;
+ }
}
diff --git a/src/Command/Timeline/ExecuteCommand.php b/src/Command/Timeline/ExecuteCommand.php
index 9a8121c..cb17187 100644
--- a/src/Command/Timeline/ExecuteCommand.php
+++ b/src/Command/Timeline/ExecuteCommand.php
@@ -82,7 +82,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
$canExecute = $this->getHelper('question')->ask($input, $output, new ConfirmationQuestion($question));
}
if ($canExecute) {
- $this->getTimeline()->runSingle($version, $options);
+ $result = $this->getTimeline()->runSingle($version, $options);
+ if ($result) {
+ $version = $result;
+ $this->getStorage()->update($version);
+ }
$output->writeln("Version {$version->getId()} migrated $direction successfully.");
}
}
diff --git a/src/Command/Timeline/MigrateCommand.php b/src/Command/Timeline/MigrateCommand.php
index 16963aa..9133c26 100644
--- a/src/Command/Timeline/MigrateCommand.php
+++ b/src/Command/Timeline/MigrateCommand.php
@@ -26,6 +26,8 @@
use Baleen\Migrations\Event\Timeline\MigrationEvent;
use Baleen\Migrations\Migration\Options;
use Baleen\Migrations\Timeline;
+use Baleen\Migrations\Version;
+use Baleen\Migrations\Version\Collection\MigratedVersions;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -51,6 +53,9 @@ class MigrateCommand extends AbstractTimelineCommand
/** @var ProgressBar */
protected $progress;
+ /** @var bool */
+ protected $saveChanges = true;
+
/** @var array */
protected $strategies = [
Options::DIRECTION_UP => 'upTowards',
@@ -61,6 +66,9 @@ class MigrateCommand extends AbstractTimelineCommand
/** @var bool */
protected $trackProgress = true;
+ /** @var string */
+ protected $directionPhrase = 'Migrating to';
+
/**
* @inheritdoc
*/
@@ -91,16 +99,19 @@ public function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
$targetArg = $input->getArgument(self::ARG_TARGET);
+ $this->saveChanges = !$input->getOption(self::OPT_NO_STORAGE);
$strategy = $this->getStrategyOption($input);
$options = new Options(Options::DIRECTION_UP); // this value will get replaced by timeline later
$options->setDryRun($input->getOption(self::OPT_DRY_RUN));
+ $options->setExceptionOnSkip(false);
$this->trackProgress = ($output->getVerbosity() !== OutputInterface::VERBOSITY_QUIET)
&& !$input->getOption(self::OPT_NOPROGRESS);
$this->attachEvents($output);
+ /** @var Version\Collection\LinkedVersions $results */
$this->getTimeline()->$strategy($targetArg, $options);
}
@@ -118,6 +129,20 @@ protected function attachEvents(OutputInterface $output)
$dispatcher->addListener(EventInterface::MIGRATION_BEFORE, [$this, 'onMigrationBefore']);
$dispatcher->addListener(EventInterface::MIGRATION_AFTER, [$this, 'onMigrationAfter']);
}
+
+ if ($this->saveChanges) {
+ $dispatcher->addListener(EventInterface::MIGRATION_AFTER, [$this, 'saveVersionListener']);
+ }
+ }
+
+ /**
+ * saveVersionListener
+ * @param MigrationEvent $event
+ */
+ public function saveVersionListener(MigrationEvent $event)
+ {
+ $version = $event->getVersion();
+ $this->getStorage()->update($version);
}
/**
@@ -155,8 +180,10 @@ public function onMigrationAfter(MigrationEvent $event)
public function onCollectionBefore(CollectionEvent $event)
{
$target = $event->getTarget();
+
$this->output->writeln(sprintf(
- '[START] Migrating towards %s:',
+ '[START] Migrating %s to %s:',
+ $event->getOptions()->isDirectionUp() ? 'up' : 'down',
$target->getId()
));
if ($this->trackProgress) {
@@ -175,7 +202,7 @@ public function onCollectionAfter()
$this->progress->finish();
$this->output->writeln(''); // new line after progress bar
}
- $this->output->writeln('[END] All done!');
+ $this->output->writeln('[END]');
}
/**
diff --git a/src/Container/ServiceProvider/DefaultProvider.php b/src/Container/ServiceProvider/DefaultProvider.php
index 92d2f27..e24c1f6 100644
--- a/src/Container/ServiceProvider/DefaultProvider.php
+++ b/src/Container/ServiceProvider/DefaultProvider.php
@@ -78,7 +78,8 @@ public function register()
->invokeMethod('setStorage', [StorageProvider::SERVICE_STORAGE]);
$container->inflector(AbstractTimelineCommand::class)
- ->invokeMethod('setTimeline', [TimelineProvider::SERVICE_TIMELINE]);
+ ->invokeMethod('setTimeline', [TimelineProvider::SERVICE_TIMELINE])
+ ->invokeMethod('setStorage', [StorageProvider::SERVICE_STORAGE]);
$container->inflector(InitCommand::class)
->invokeMethod('setConfigStorage', [AppConfigProvider::SERVICE_CONFIG_STORAGE]);
diff --git a/test/Command/Timeline/MigrateCommandTest.php b/test/Command/Timeline/MigrateCommandTest.php
index 8ee3b83..a9d783f 100644
--- a/test/Command/Timeline/MigrateCommandTest.php
+++ b/test/Command/Timeline/MigrateCommandTest.php
@@ -69,9 +69,10 @@ public function testConfigure()
* testExecute
* @param $verbosity
* @param $noProgress
+ * @param $noStorage
* @dataProvider executeProvider
*/
- public function testExecute($verbosity, $noProgress)
+ public function testExecute($verbosity, $noProgress, $noStorage)
{
// values don't matter here
$strategy = 'both';
@@ -90,6 +91,7 @@ public function testExecute($verbosity, $noProgress)
$this->input->shouldReceive('getArgument')->with(MigrateCommand::ARG_TARGET)->once()->andReturn($target);
$this->input->shouldReceive('getOption')->with(MigrateCommand::OPT_DRY_RUN)->once()->andReturn($dryRun);
+ $this->input->shouldReceive('getOption')->with(MigrateCommand::OPT_NO_STORAGE)->once()->andReturn($noStorage);
$this->instance->shouldReceive('getStrategyOption')->with($this->input)->andReturn($strategy);
$this->instance->shouldReceive('attachEvents')->once()->with($this->output);
$this->instance->shouldReceive('getTimeline->' . $strategy)->once()->with($target, m::type(Options::class));
@@ -97,6 +99,7 @@ public function testExecute($verbosity, $noProgress)
$this->execute();
$this->assertEquals($shouldTrackProgress, $this->getPropVal('trackProgress', $this->instance));
+ $this->assertEquals(!$noStorage, $this->getPropVal('saveChanges', $this->instance));
}
/**
@@ -112,7 +115,8 @@ public function executeProvider()
OutputInterface::VERBOSITY_VERY_VERBOSE,
OutputInterface::VERBOSITY_DEBUG,
];
- return $this->combinations([$verbosities, [true, false]]);
+ $trueFalse = [true, false];
+ return $this->combinations([$verbosities, $trueFalse, $trueFalse]);
}
/**
@@ -149,7 +153,7 @@ public function getStrategyOptionProvider()
*/
public function testOnCollectionAfter()
{
- $this->output->shouldReceive('writeln')->with('/done/')->once();
+ $this->output->shouldReceive('writeln')->with('/END/')->once();
$this->setPropVal('output', $this->output, $this->instance);
$this->invokeMethod('onCollectionAfter', $this->instance);
}
@@ -157,13 +161,18 @@ public function testOnCollectionAfter()
/**
* testOnCollectionBefore
* @param bool $trackProgress
+ * @param bool $isDirectionUp
+ * @dataProvider onCollectionBeforeProvider
*/
- public function testOnCollectionBefore($trackProgress = true)
+ public function testOnCollectionBefore($trackProgress = true, $isDirectionUp = true)
{
$target = new Version('v10');
/** @var m\Mock|CollectionEvent $event */
$event = m::mock(CollectionEvent::class);
- $event->shouldReceive(['getTarget' => $target])->once();
+ $event->shouldReceive([
+ 'getTarget' => $target,
+ 'getOptions->isDirectionUp' => $isDirectionUp,
+ ])->once();
$this->output->shouldReceive('writeln')->with('/' . $target->getId() . '/')->once();
$this->setPropVal('output', $this->output, $this->instance);
@@ -182,11 +191,14 @@ public function testOnCollectionBefore($trackProgress = true)
$this->invokeMethod('onCollectionBefore', $this->instance, [$event]);
}
- public function trackProgressProvider()
+ /**
+ * onCollectionBeforeProvider
+ * @return array
+ */
+ public function onCollectionBeforeProvider()
{
- return [
- [true], [false]
- ];
+ $trueFalse = [true, false];
+ return $this->combinations([$trueFalse, $trueFalse]);
}
/**
@@ -205,17 +217,53 @@ public function testOnMigrationBefore()
$this->invokeMethod('onMigrationBefore', $this->instance, [$event]);
}
- public function testAttachEvents($verbosity = 1)
+ /**
+ * testAttachEvents
+ * @param int $verbosity
+ * @param $saveChanges
+ * @dataProvider attachEventsProvider
+ */
+ public function testAttachEvents($verbosity, $saveChanges)
{
$dispatcher = m::mock(EventDispatcher::class);
$this->instance->shouldReceive('getTimeline->getEventDispatcher')->once()->andReturn($dispatcher);
+ $this->setPropVal('saveChanges', $saveChanges, $this->instance);
$this->output->shouldReceive('getVerbosity')->andReturn($verbosity);
+ $counts = [
+ EventInterface::MIGRATION_BEFORE => 0,
+ EventInterface::MIGRATION_AFTER => 0,
+ EventInterface::COLLECTION_BEFORE => 0,
+ EventInterface::COLLECTION_AFTER => 0,
+ ];
if ($verbosity >= OutputInterface::VERBOSITY_NORMAL) {
- $dispatcher->shouldReceive('addListener')->once()->with(EventInterface::MIGRATION_BEFORE, m::any());
- $dispatcher->shouldReceive('addListener')->once()->with(EventInterface::MIGRATION_AFTER, m::any());
- $dispatcher->shouldReceive('addListener')->once()->with(EventInterface::COLLECTION_BEFORE, m::any());
- $dispatcher->shouldReceive('addListener')->once()->with(EventInterface::COLLECTION_AFTER, m::any());
+ $counts = [
+ EventInterface::MIGRATION_BEFORE => 1,
+ EventInterface::MIGRATION_AFTER => 1,
+ EventInterface::COLLECTION_BEFORE => 1,
+ EventInterface::COLLECTION_AFTER => 1,
+ ];
+ }
+ if ($saveChanges) {
+ $counts[EventInterface::MIGRATION_AFTER] += 1;
+ }
+ foreach ($counts as $event => $count) {
+ $dispatcher->shouldReceive('addListener')->times($count)->with($event, m::any());
}
$this->invokeMethod('attachEvents', $this->instance, [$this->output]);
}
+
+ /**
+ * attachEventsProvider
+ * @return array
+ */
+ public function attachEventsProvider()
+ {
+ $trueFalse = [true, false];
+ $verbosities = [
+ OutputInterface::VERBOSITY_QUIET,
+ OutputInterface::OUTPUT_NORMAL,
+ OutputInterface::VERBOSITY_VERY_VERBOSE,
+ ];
+ return $this->combinations([$verbosities, $trueFalse]);
+ }
}