StatusCommand.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2017, ownCloud GmbH
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Robin Appelman <robin@icewind.nl>
  8. *
  9. * @license AGPL-3.0
  10. *
  11. * This code is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License, version 3,
  13. * as published by the Free Software Foundation.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License, version 3,
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>
  22. *
  23. */
  24. namespace OC\Core\Command\Db\Migrations;
  25. use OC\DB\Connection;
  26. use OC\DB\MigrationService;
  27. use OC\Migration\ConsoleOutput;
  28. use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
  29. use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
  30. use Symfony\Component\Console\Command\Command;
  31. use Symfony\Component\Console\Input\InputArgument;
  32. use Symfony\Component\Console\Input\InputInterface;
  33. use Symfony\Component\Console\Output\OutputInterface;
  34. class StatusCommand extends Command implements CompletionAwareInterface {
  35. public function __construct(
  36. private Connection $connection,
  37. ) {
  38. parent::__construct();
  39. }
  40. protected function configure() {
  41. $this
  42. ->setName('migrations:status')
  43. ->setDescription('View the status of a set of migrations.')
  44. ->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on');
  45. }
  46. public function execute(InputInterface $input, OutputInterface $output): int {
  47. $appName = $input->getArgument('app');
  48. $ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output));
  49. $infos = $this->getMigrationsInfos($ms);
  50. foreach ($infos as $key => $value) {
  51. if (is_array($value)) {
  52. $output->writeln(" <comment>>></comment> $key:");
  53. foreach ($value as $subKey => $subValue) {
  54. $output->writeln(" <comment>>></comment> $subKey: " . str_repeat(' ', 46 - strlen($subKey)) . $subValue);
  55. }
  56. } else {
  57. $output->writeln(" <comment>>></comment> $key: " . str_repeat(' ', 50 - strlen($key)) . $value);
  58. }
  59. }
  60. return 0;
  61. }
  62. /**
  63. * @param string $optionName
  64. * @param CompletionContext $context
  65. * @return string[]
  66. */
  67. public function completeOptionValues($optionName, CompletionContext $context) {
  68. return [];
  69. }
  70. /**
  71. * @param string $argumentName
  72. * @param CompletionContext $context
  73. * @return string[]
  74. */
  75. public function completeArgumentValues($argumentName, CompletionContext $context) {
  76. if ($argumentName === 'app') {
  77. $allApps = \OC_App::getAllApps();
  78. return array_diff($allApps, \OC_App::getEnabledApps(true, true));
  79. }
  80. return [];
  81. }
  82. /**
  83. * @param MigrationService $ms
  84. * @return array associative array of human readable info name as key and the actual information as value
  85. */
  86. public function getMigrationsInfos(MigrationService $ms) {
  87. $executedMigrations = $ms->getMigratedVersions();
  88. $availableMigrations = $ms->getAvailableVersions();
  89. $executedUnavailableMigrations = array_diff($executedMigrations, array_keys($availableMigrations));
  90. $numExecutedUnavailableMigrations = count($executedUnavailableMigrations);
  91. $numNewMigrations = count(array_diff(array_keys($availableMigrations), $executedMigrations));
  92. $pending = $ms->describeMigrationStep('lastest');
  93. $infos = [
  94. 'App' => $ms->getApp(),
  95. 'Version Table Name' => $ms->getMigrationsTableName(),
  96. 'Migrations Namespace' => $ms->getMigrationsNamespace(),
  97. 'Migrations Directory' => $ms->getMigrationsDirectory(),
  98. 'Previous Version' => $this->getFormattedVersionAlias($ms, 'prev'),
  99. 'Current Version' => $this->getFormattedVersionAlias($ms, 'current'),
  100. 'Next Version' => $this->getFormattedVersionAlias($ms, 'next'),
  101. 'Latest Version' => $this->getFormattedVersionAlias($ms, 'latest'),
  102. 'Executed Migrations' => count($executedMigrations),
  103. 'Executed Unavailable Migrations' => $numExecutedUnavailableMigrations,
  104. 'Available Migrations' => count($availableMigrations),
  105. 'New Migrations' => $numNewMigrations,
  106. 'Pending Migrations' => count($pending) ? $pending : 'None'
  107. ];
  108. return $infos;
  109. }
  110. /**
  111. * @param MigrationService $migrationService
  112. * @param string $alias
  113. * @return mixed|null|string
  114. */
  115. private function getFormattedVersionAlias(MigrationService $migrationService, $alias) {
  116. $migration = $migrationService->getMigration($alias);
  117. //No version found
  118. if ($migration === null) {
  119. if ($alias === 'next') {
  120. return 'Already at latest migration step';
  121. }
  122. if ($alias === 'prev') {
  123. return 'Already at first migration step';
  124. }
  125. }
  126. return $migration;
  127. }
  128. }