Current File : /var/www/vinorea/modules/autoupgrade/classes/Commands/UpdateCommand.php |
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\AutoUpgrade\Commands;
use Exception;
use InvalidArgumentException;
use PrestaShop\Module\AutoUpgrade\DocumentationLinks;
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration;
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeFileNames;
use PrestaShop\Module\AutoUpgrade\Task\ExitCode;
use PrestaShop\Module\AutoUpgrade\Task\Runner\AllUpdateTasks;
use PrestaShop\Module\AutoUpgrade\Task\TaskName;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class UpdateCommand extends AbstractCommand
{
/**
* @var string
*/
protected static $defaultName = 'update:start';
protected function configure(): void
{
$this
->setDescription('Update your store.')
->setHelp(
'This command allows you to start the update process. ' .
'Advanced users can refer to the ' . DocumentationLinks::DEV_DOC_UPGRADE_CLI_URL . ' for further details on available actions'
)
->addArgument('admin-dir', InputArgument::REQUIRED, 'The admin directory name.')
->addOption('chain', null, InputOption::VALUE_NONE, 'True by default. Allows you to chain update commands automatically. The command will continue executing subsequent tasks without requiring manual intervention to restart the process.')
->addOption('no-chain', null, InputOption::VALUE_NONE, 'Prevents chaining of update commands. The command will execute a task and then stop, logging the next command that needs to be run. You will need to manually restart the process to continue with the next step.')
->addOption('channel', null, InputOption::VALUE_REQUIRED, "Selects what update to run ('" . UpgradeConfiguration::CHANNEL_LOCAL . "' / '" . UpgradeConfiguration::CHANNEL_ONLINE . "')")
->addOption('zip', null, InputOption::VALUE_REQUIRED, 'Sets the archive zip file for a local update')
->addOption('xml', null, InputOption::VALUE_REQUIRED, 'Sets the archive xml file for a local update')
->addOption('disable-non-native-modules', null, InputOption::VALUE_REQUIRED, 'Disable all modules installed after the store creation (1 for yes, 0 for no)')
->addOption('regenerate-email-templates', null, InputOption::VALUE_REQUIRED, "Regenerate email templates. If you've customized email templates, your changes will be lost if you activate this option (1 for yes, 0 for no)")
->addOption('disable-all-overrides', null, InputOption::VALUE_REQUIRED, 'Overriding is a way to replace business behaviors (class files and controller files) to target only one method or as many as you need. This option disables all classes & controllers overrides, allowing you to avoid conflicts during and after updates (1 for yes, 0 for no)')
->addOption('config-file-path', null, InputOption::VALUE_REQUIRED, 'Configuration file location for update.')
->addOption('action', null, InputOption::VALUE_REQUIRED, 'Advanced users only. Sets the step you want to start from. Only the "' . TaskName::TASK_UPDATE_INITIALIZATION . '" task updates the configuration. (Default: ' . TaskName::TASK_UPDATE_INITIALIZATION . ', see ' . DocumentationLinks::DEV_DOC_UPGRADE_CLI_URL . ' for other values available)');
}
/**
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): ?int
{
$chainMode = $input->getOption('chain');
$noChainMode = $input->getOption('no-chain');
if ($chainMode && $noChainMode) {
throw new InvalidArgumentException('The chain and no-chain options cannot be active at the same time');
}
try {
$this->setupEnvironment($input, $output);
$action = $input->getOption('action');
// if we are in the 1st step of the update, we update the configuration
if ($action === null || $action === TaskName::TASK_UPDATE_INITIALIZATION) {
$this->logger->debug('Cleaning previous configuration file.');
$this->upgradeContainer->getFileStorage()->clean(UpgradeFileNames::UPDATE_CONFIG_FILENAME);
$this->processConsoleInputConfiguration($input);
$configPath = $input->getOption('config-file-path');
$exitCode = $this->loadConfiguration($configPath);
if ($exitCode !== ExitCode::SUCCESS) {
return $exitCode;
}
} else {
$updateState = $this->upgradeContainer->getUpdateState();
// In the special case the user inits the process from a specific task that is not the initialization,
// we need to initialize the state manually.
if (!$updateState->isInitialized()) {
$updateState->initDefault($this->upgradeContainer->getProperty(UpgradeContainer::PS_VERSION), $this->upgradeContainer->getUpgrader(), $this->upgradeContainer->getUpdateConfiguration());
}
}
$this->logger->debug('Configuration loaded successfully.');
$this->logger->debug('Starting the update process.');
$controller = new AllUpdateTasks($this->upgradeContainer);
$controller->setOptions([
'action' => $action,
]);
$controller->init();
$exitCode = $controller->run();
$this->logger->debug('Process completed with exit code: ' . $exitCode);
if ($noChainMode || $exitCode !== ExitCode::SUCCESS) {
return $exitCode;
}
return $this->chainCommand($output);
} catch (Exception $e) {
$this->logger->error("An error occurred during the update process:\n" . $e);
throw $e;
}
}
/**
* @throws Exception
*/
private function chainCommand(OutputInterface $output): int
{
$lastInfo = $this->logger->getLastInfo();
if (!$lastInfo) {
return ExitCode::SUCCESS;
}
if (strpos($lastInfo, self::$defaultName) !== false) {
if (preg_match('/--action=(\S+)/', $lastInfo, $actionMatches)) {
$action = $actionMatches[1];
$this->logger->debug('Action parameter found: ' . $action);
} else {
throw new InvalidArgumentException('The command does not contain the necessary information to continue the update process.');
}
$newCommand = str_replace('INFO - $ ', '', $lastInfo);
$decorationParam = $output->isDecorated() ? ' --ansi' : '';
system('php ' . $newCommand . $decorationParam, $exitCode);
return $exitCode;
}
return ExitCode::SUCCESS;
}
private function processConsoleInputConfiguration(InputInterface $input): void
{
$options = [
UpgradeConfiguration::CHANNEL => 'channel',
UpgradeConfiguration::ARCHIVE_ZIP => 'zip',
UpgradeConfiguration::ARCHIVE_XML => 'xml',
UpgradeConfiguration::PS_AUTOUP_CUSTOM_MOD_DESACT => 'disable-non-native-modules',
UpgradeConfiguration::PS_AUTOUP_REGEN_EMAIL => 'regenerate-email-templates',
UpgradeConfiguration::PS_DISABLE_OVERRIDES => 'disable-all-overrides',
];
foreach ($options as $configKey => $optionName) {
$optionValue = $input->getOption($optionName);
if ($optionValue !== null) {
$this->consoleInputConfiguration[$configKey] = $optionValue;
}
}
}
}