From 6a8173f4bb4f4c3f41091f80ea660d242558c7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCrk?= <stefan@buerk.tech> Date: Sun, 21 Apr 2024 01:43:54 +0200 Subject: [PATCH] [BUGFIX] Mitigate invalid exit code for UpgradeWizard without updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wizards not marked as done, but not having updates, display a notice `No changes applied, marking wizard as done.` when using the CLI command `bin/typo3 upgrade:run <wizard-identifier>` and return an error exit code. This also happen when not specifying a single wizard, but running all available ones. The GUI based upgrade execution behaves different, as it does not return an exit code and does not display information about `no changes applied`, like its CLI counterpart. This change: * Adds new concrete exception classes for the `upgrade:run` command. * Modifies `UpgradeWizardCommand::getUpgradeWizard()` to throw now one of the new concrete exception or return a wizard instance to run. * Skips available upgrade wizards not eligible to be executed. Resolves: #103447 Releases: main, 12.4 Change-Id: Ib6eab07919bb2f6e11bac774ecfd333572c2205f Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83944 Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Stefan Bürk <stefan@buerk.tech> Reviewed-by: Stefan Bürk <stefan@buerk.tech> Tested-by: core-ci <typo3@b13.com> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> --- ...izardDoesNotNeedToMakeChangesException.php | 27 +++++++ .../Exception/WizardMarkedAsDoneException.php | 27 +++++++ .../Exception/WizardNotFoundException.php | 27 +++++++ .../Command/UpgradeWizardRunCommand.php | 77 +++++++++++-------- 4 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 typo3/sysext/install/Classes/Command/Exception/WizardDoesNotNeedToMakeChangesException.php create mode 100644 typo3/sysext/install/Classes/Command/Exception/WizardMarkedAsDoneException.php create mode 100644 typo3/sysext/install/Classes/Command/Exception/WizardNotFoundException.php diff --git a/typo3/sysext/install/Classes/Command/Exception/WizardDoesNotNeedToMakeChangesException.php b/typo3/sysext/install/Classes/Command/Exception/WizardDoesNotNeedToMakeChangesException.php new file mode 100644 index 000000000000..063a7d2d5fb4 --- /dev/null +++ b/typo3/sysext/install/Classes/Command/Exception/WizardDoesNotNeedToMakeChangesException.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Install\Command\Exception; + +use TYPO3\CMS\Install\Exception as Typo3InstallException; + +/** + * This exception is thrown in UpgradeWizardRunCommand, if a requested wizard exists but does not need to make any changes. + * + * @internal for use in UpgradeWizardRunCommand only and not part of public API. + */ +final class WizardDoesNotNeedToMakeChangesException extends Typo3InstallException {} diff --git a/typo3/sysext/install/Classes/Command/Exception/WizardMarkedAsDoneException.php b/typo3/sysext/install/Classes/Command/Exception/WizardMarkedAsDoneException.php new file mode 100644 index 000000000000..5cff7de6f81b --- /dev/null +++ b/typo3/sysext/install/Classes/Command/Exception/WizardMarkedAsDoneException.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Install\Command\Exception; + +use TYPO3\CMS\Install\Exception as Typo3InstallException; + +/** + * This exception is thrown in the UpgradeWizardRunCommand, if a requested wizard is already marked as done. + * + * @internal for use in UpgradeWizardRunCommand only and not part of public API. + */ +final class WizardMarkedAsDoneException extends Typo3InstallException {} diff --git a/typo3/sysext/install/Classes/Command/Exception/WizardNotFoundException.php b/typo3/sysext/install/Classes/Command/Exception/WizardNotFoundException.php new file mode 100644 index 000000000000..42e0f424a2a4 --- /dev/null +++ b/typo3/sysext/install/Classes/Command/Exception/WizardNotFoundException.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Install\Command\Exception; + +use TYPO3\CMS\Install\Exception as Typo3InstallException; + +/** + * This exception is thrown in UpgradeWizardRunCommand, if a requested wizard could not be found. + * + * @internal for use in UpgradeWizardRunCommand only and not part of public API. + */ +final class WizardNotFoundException extends Typo3InstallException {} diff --git a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php index 72713372d98f..289a6125a669 100644 --- a/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php +++ b/typo3/sysext/install/Classes/Command/UpgradeWizardRunCommand.php @@ -28,6 +28,9 @@ use TYPO3\CMS\Core\Authentication\CommandLineUserAuthentication; use TYPO3\CMS\Core\Configuration\Exception\SettingsWriteException; use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Install\Command\Exception\WizardDoesNotNeedToMakeChangesException; +use TYPO3\CMS\Install\Command\Exception\WizardMarkedAsDoneException; +use TYPO3\CMS\Install\Command\Exception\WizardNotFoundException; use TYPO3\CMS\Install\Service\DatabaseUpgradeWizardsService; use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException; use TYPO3\CMS\Install\Service\Exception\SilentConfigurationUpgradeReadonlyException; @@ -128,60 +131,67 @@ class UpgradeWizardRunCommand extends Command $this->output = new SymfonyStyle($input, $output); $this->input = $input; $this->bootstrap(); + $wizardToExecute = (string)$input->getArgument('wizardName'); + if ($wizardToExecute === '') { + return $this->runAllWizards(); + } - if ($input->getArgument('wizardName')) { - $wizardToExecute = $input->getArgument('wizardName'); - $wizardToExecute = is_string($wizardToExecute) ? $wizardToExecute : ''; - if ($this->upgradeWizardsService->isWizardDone($wizardToExecute)) { - $this->output->note(sprintf('Wizard %s marked as done. Skipped.', $wizardToExecute)); - $result = Command::SUCCESS; - } elseif (($upgradeWizard = $this->getWizard($wizardToExecute)) !== null) { - $prerequisitesFulfilled = $this->handlePrerequisites([$upgradeWizard]); - if ($prerequisitesFulfilled === true) { - $result = $this->runSingleWizard($upgradeWizard); - } else { - $result = Command::FAILURE; - } - } else { - $this->output->error('No such wizard: ' . $wizardToExecute); - $result = Command::FAILURE; - } - } else { - $result = $this->runAllWizards(); + try { + $upgradeWizard = $this->getWizard($wizardToExecute); + } catch (WizardMarkedAsDoneException|WizardDoesNotNeedToMakeChangesException $e) { + $this->output->note($e->getMessage()); + return Command::SUCCESS; + } catch (WizardNotFoundException $e) { + $this->output->error($e->getMessage()); + return Command::FAILURE; } - return $result; + + $prerequisitesFulfilled = $this->handlePrerequisites([$upgradeWizard]); + if ($prerequisitesFulfilled === true) { + return $this->runSingleWizard($upgradeWizard); + } + return Command::FAILURE; } /** * Get Wizard instance by class name and identifier * Returns null if wizard is already done */ - protected function getWizard(string $identifier): ?UpgradeWizardInterface + protected function getWizard(string $identifier): UpgradeWizardInterface { // already done if ($this->upgradeWizardsService->isWizardDone($identifier)) { - return null; + throw new WizardMarkedAsDoneException( + sprintf('Wizard %s already marked as done', $identifier), + 1713880347 + ); } - $wizard = $this->upgradeWizardsService->getUpgradeWizard($identifier); if ($wizard === null) { - return null; + throw new WizardNotFoundException( + sprintf('No such wizard: %s', $identifier), + 1713880629 + ); } if ($wizard instanceof ChattyInterface) { $wizard->setOutput($this->output); } - if ($wizard->updateNecessary()) { return $wizard; } - if ($wizard instanceof RepeatableInterface) { - $this->output->note('Wizard ' . $identifier . ' does not need to make changes.'); - } else { - $this->output->note('Wizard ' . $identifier . ' does not need to make changes. Marking wizard as done.'); + + if (!($wizard instanceof RepeatableInterface)) { $this->upgradeWizardsService->markWizardAsDone($wizard); + throw new WizardMarkedAsDoneException( + sprintf('Wizard %s does not need to make changes. Marking wizard as done.', $identifier), + 1713880485 + ); } - return null; + throw new WizardDoesNotNeedToMakeChangesException( + sprintf('Wizard %s does not need to make changes.', $identifier), + 1713880493 + ); } /** @@ -279,9 +289,12 @@ class UpgradeWizardRunCommand extends Command $returnCode = Command::SUCCESS; $wizardInstances = []; foreach ($this->upgradeWizardsService->getUpgradeWizardIdentifiers() as $identifier) { - $wizardInstances[] = $this->getWizard($identifier); + try { + $wizardInstances[] = $this->getWizard($identifier); + } catch (WizardMarkedAsDoneException|WizardDoesNotNeedToMakeChangesException|WizardNotFoundException) { + // NOOP + } } - $wizardInstances = array_filter($wizardInstances); if (count($wizardInstances) > 0) { $prerequisitesResult = $this->handlePrerequisites($wizardInstances); if ($prerequisitesResult === false) { -- GitLab