diff options
author | Joas Schilling <coding@schilljs.com> | 2017-06-01 16:56:34 +0200 |
---|---|---|
committer | Morris Jobke <hey@morrisjobke.de> | 2017-07-05 13:01:19 +0200 |
commit | 15eec7b83c6198a124c2720e8ecc988605428f54 (patch) | |
tree | 62f47bb629b621b883efb17c02194972ba20a71f /core/Command | |
parent | efa52ec1113eeccbd3935a8c96ea23c47ca190ab (diff) | |
download | nextcloud-server-15eec7b83c6198a124c2720e8ecc988605428f54.tar.gz nextcloud-server-15eec7b83c6198a124c2720e8ecc988605428f54.zip |
Start migrations
Fixme:
- Install and update of apps
- No revert on live systems (debug only)
- Service adjustment to our interface
- Loading via autoloader
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'core/Command')
-rw-r--r-- | core/Command/Db/Migrations/ExecuteCommand.php | 67 | ||||
-rw-r--r-- | core/Command/Db/Migrations/GenerateCommand.php | 166 | ||||
-rw-r--r-- | core/Command/Db/Migrations/MigrateCommand.php | 64 | ||||
-rw-r--r-- | core/Command/Db/Migrations/StatusCommand.php | 115 |
4 files changed, 412 insertions, 0 deletions
diff --git a/core/Command/Db/Migrations/ExecuteCommand.php b/core/Command/Db/Migrations/ExecuteCommand.php new file mode 100644 index 00000000000..6aad4f4973f --- /dev/null +++ b/core/Command/Db/Migrations/ExecuteCommand.php @@ -0,0 +1,67 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Core\Command\Db\Migrations; + + +use OC\DB\MigrationService; +use OC\Migration\ConsoleOutput; +use OCP\IDBConnection; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class ExecuteCommand extends Command { + + /** @var IDBConnection */ + private $connection; + + /** + * ExecuteCommand constructor. + * + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this + ->setName('migrations:execute') + ->setDescription('Execute a single migration version manually.') + ->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on') + ->addArgument('version', InputArgument::REQUIRED, 'The version to execute.', null); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output)); + $version = $input->getArgument('version'); + + $ms->executeStep($version); + } + +} diff --git a/core/Command/Db/Migrations/GenerateCommand.php b/core/Command/Db/Migrations/GenerateCommand.php new file mode 100644 index 00000000000..307989c845a --- /dev/null +++ b/core/Command/Db/Migrations/GenerateCommand.php @@ -0,0 +1,166 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Core\Command\Db\Migrations; + + +use OC\DB\MigrationService; +use OC\Migration\ConsoleOutput; +use OCP\IConfig; +use OCP\IDBConnection; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateCommand extends Command { + + private static $_templateSimple = + '<?php +namespace <namespace>; + +use OCP\Migration\ISimpleMigration; +use OCP\Migration\IOutput; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version<version> implements ISimpleMigration { + + /** + * @param IOutput $out + */ + public function run(IOutput $out) { + // auto-generated - please modify it to your needs + } +} +'; + + private static $_templateSchema = + '<?php +namespace <namespace>; + +use Doctrine\DBAL\Schema\Schema; +use OCP\Migration\ISchemaMigration; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version<version> implements ISchemaMigration { + + public function changeSchema(Schema $schema, array $options) { + // auto-generated - please modify it to your needs + } +} +'; + + private static $_templateSql = + '<?php +namespace <namespace>; + +use OCP\IDBConnection; +use OCP\Migration\ISqlMigration; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version<version> implements ISqlMigration { + + public function sql(IDBConnection $connection) { + // auto-generated - please modify it to your needs + } +} +'; + + /** @var IDBConnection */ + private $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this + ->setName('migrations:generate') + ->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on') + ->addArgument('kind', InputArgument::REQUIRED, 'simple, schema or sql - defines the kind of migration to be generated'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output)); + + $kind = $input->getArgument('kind'); + $version = date('YmdHis'); + $path = $this->generateMigration($ms, $version, $kind); + + $output->writeln("New migration class has been generated to <info>$path</info>"); + + } + + /** + * @param MigrationService $ms + * @param string $version + * @param string $kind + * @return string + */ + private function generateMigration(MigrationService $ms, $version, $kind) { + $placeHolders = [ + '<namespace>', + '<version>', + ]; + $replacements = [ + $ms->getMigrationsNamespace(), + $version, + ]; + $code = str_replace($placeHolders, $replacements, $this->getTemplate($kind)); + $dir = $ms->getMigrationsDirectory(); + $path = $dir . '/Version' . $version . '.php'; + + if (file_put_contents($path, $code) === false) { + throw new RuntimeException('Failed to generate new migration step.'); + } + + return $path; + } + + private function getTemplate($kind) { + if ($kind === 'simple') { + return self::$_templateSimple; + } + if ($kind === 'schema') { + return self::$_templateSchema; + } + if ($kind === 'sql') { + return self::$_templateSql; + } + throw new \InvalidArgumentException('Kind can only be one of the following: simple, schema or sql'); + } + +} diff --git a/core/Command/Db/Migrations/MigrateCommand.php b/core/Command/Db/Migrations/MigrateCommand.php new file mode 100644 index 00000000000..2b0e082acaa --- /dev/null +++ b/core/Command/Db/Migrations/MigrateCommand.php @@ -0,0 +1,64 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Core\Command\Db\Migrations; + + +use OC\DB\MigrationService; +use OC\Migration\ConsoleOutput; +use OCP\IDBConnection; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateCommand extends Command { + + /** @var IDBConnection */ + private $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('migrations:migrate') + ->setDescription('Execute a migration to a specified version or the latest available version.') + ->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on') + ->addArgument('version', InputArgument::OPTIONAL, 'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.', 'latest'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output)); + $version = $input->getArgument('version'); + + $ms->migrate($version); + } + +} diff --git a/core/Command/Db/Migrations/StatusCommand.php b/core/Command/Db/Migrations/StatusCommand.php new file mode 100644 index 00000000000..20172000ee3 --- /dev/null +++ b/core/Command/Db/Migrations/StatusCommand.php @@ -0,0 +1,115 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2017, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Core\Command\Db\Migrations; + +use OC\DB\MigrationService; +use OC\Migration\ConsoleOutput; +use OCP\IDBConnection; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class StatusCommand extends Command { + + /** @var IDBConnection */ + private $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('migrations:status') + ->setDescription('View the status of a set of migrations.') + ->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on'); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output)); + + $infos = $this->getMigrationsInfos($ms); + foreach ($infos as $key => $value) { + $output->writeln(" <comment>>></comment> $key: " . str_repeat(' ', 50 - strlen($key)) . $value); + } + } + + /** + * @param MigrationService $ms + * @return array associative array of human readable info name as key and the actual information as value + */ + public function getMigrationsInfos(MigrationService $ms) { + + $executedMigrations = $ms->getMigratedVersions(); + $availableMigrations = $ms->getAvailableVersions(); + $executedUnavailableMigrations = array_diff($executedMigrations, array_keys($availableMigrations)); + + $numExecutedUnavailableMigrations = count($executedUnavailableMigrations); + $numNewMigrations = count(array_diff(array_keys($availableMigrations), $executedMigrations)); + + $infos = [ + 'App' => $ms->getApp(), + 'Version Table Name' => $ms->getMigrationsTableName(), + 'Migrations Namespace' => $ms->getMigrationsNamespace(), + 'Migrations Directory' => $ms->getMigrationsDirectory(), + 'Previous Version' => $this->getFormattedVersionAlias($ms, 'prev'), + 'Current Version' => $this->getFormattedVersionAlias($ms, 'current'), + 'Next Version' => $this->getFormattedVersionAlias($ms, 'next'), + 'Latest Version' => $this->getFormattedVersionAlias($ms, 'latest'), + 'Executed Migrations' => count($executedMigrations), + 'Executed Unavailable Migrations' => $numExecutedUnavailableMigrations, + 'Available Migrations' => count($availableMigrations), + 'New Migrations' => $numNewMigrations, + ]; + + return $infos; + } + + /** + * @param MigrationService $migrationService + * @param string $alias + * @return mixed|null|string + */ + private function getFormattedVersionAlias(MigrationService $migrationService, $alias) { + $migration = $migrationService->getMigration($alias); + //No version found + if ($migration === null) { + if ($alias === 'next') { + return 'Already at latest migration step'; + } + + if ($alias === 'prev') { + return 'Already at first migration step'; + } + } + + return $migration; + } + + +} |