diff --git a/README.md b/README.md index 2f4886556..9ebc7ae89 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,18 @@ You can see all available parameters and options directly from the console by us For more information on using commands, please refer to the [PrestaShop developer documentation](https://devdocs.prestashop-project.org/8/basics/keeping-up-to-date/upgrade-module/upgrade-cli/#rollback-cli) +## Check Modules Compatibility + +A command is available to verify module compatibility and detect available updates before performing a store update. + +Run the command from the root directory of the module: + +``` +php bin/console update:check-modules +``` + +This command helps you review module compatibility before launching an update, giving you clearer visibility on potential issues or available updates. + ## Channels There are 3 channels available for an update: @@ -200,6 +212,8 @@ impact. | `backup:create` | `PS_AUTOUP_KEEP_IMAGES` | `--include-images` | `true` (default), `false`, `'true'`, `'false'`, `'1'`, `'0'`, `1`, `0`, `'on'`, `'off'` | If enabled, retains all images in the backup. This operation can take a long time depending on the storage of your images | | `backup:restore` | no option available | `--backup` | Valid file name | Specify the backup name to restore. The allowed values can be found with backup:list command) | | `backup:delete` | no option available | `--backup` | Valid file name | Specify the backup name to delete. The allowed values can be found with backup:list command) | +| `update:check-modules` | no option available | `--channel` | `online`, `online_recommended` (default), `local` | Selects the update channel for module compatibility checks. The `local` channel requires specific files to be placed in the download folder. | +| `update:check-modules` | no option available | `--zip` | Valid file name | Sets the ZIP archive in local mode for module checking. | | | | | | | ### Back Office Environment Variables diff --git a/bin/console b/bin/console index 70f74e90f..01dd4ae29 100755 --- a/bin/console +++ b/bin/console @@ -26,6 +26,7 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) */ +use PrestaShop\Module\AutoUpgrade\Commands\CheckModulesCommand; use PrestaShop\Module\AutoUpgrade\Commands\CheckNewVersionCommand; use PrestaShop\Module\AutoUpgrade\Commands\CheckRequirementsCommand; use PrestaShop\Module\AutoUpgrade\Commands\CreateBackupCommand; @@ -61,6 +62,7 @@ $application->add(new CheckNewVersionCommand()); $application->add(new CreateBackupCommand()); $application->add(new ListBackupCommand()); $application->add(new DeleteBackupCommand()); +$application->add(new CheckModulesCommand()); $input = new ArgvInput(); $output = new ConsoleOutput(); diff --git a/classes/Commands/CheckModulesCommand.php b/classes/Commands/CheckModulesCommand.php new file mode 100644 index 000000000..dfbb97d05 --- /dev/null +++ b/classes/Commands/CheckModulesCommand.php @@ -0,0 +1,207 @@ + + * @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 PrestaShop\Module\AutoUpgrade\Exceptions\MarketplaceApiException; +use PrestaShop\Module\AutoUpgrade\Models\Module\Marketplace\Module; +use PrestaShop\Module\AutoUpgrade\Models\Module\Marketplace\Release; +use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration; +use PrestaShop\Module\AutoUpgrade\Task\ExitCode; +use PrestaShop\Module\AutoUpgrade\UpgradeContainer; +use Symfony\Component\Console\Helper\ProgressIndicator; +use Symfony\Component\Console\Helper\Table; +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 CheckModulesCommand extends AbstractCommand +{ + /** @var string */ + protected static $defaultName = 'update:check-modules'; + + protected function configure(): void + { + $this + ->setDescription('Check module compatibility and updates.') + ->addOption( + 'channel', + null, + InputOption::VALUE_REQUIRED, + "Select which update channel to use ('" . UpgradeConfiguration::CHANNEL_LOCAL . "' / '" . UpgradeConfiguration::CHANNEL_ONLINE_RECOMMENDED . "' / '" . UpgradeConfiguration::CHANNEL_ONLINE . "')" + ) + ->addOption('zip', null, InputOption::VALUE_REQUIRED, 'Sets the archive zip file for a local channel.') + ->setHelp('This command checks the installed modules for compatibility with the target PrestaShop version and lists available updates.') + ->addArgument( + 'admin-dir', + InputArgument::REQUIRED, + 'Name of the admin directory.' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): ?int + { + try { + $this->setupEnvironment($input, $output); + $config[UpgradeConfiguration::CHANNEL] = $input->getOption('channel') ?? UpgradeConfiguration::CHANNEL_ONLINE_RECOMMENDED; + + $this->upgradeContainer->getConfigurationValidator()->validate($config); + $this->upgradeContainer->initPrestaShopAutoloader(); + $this->upgradeContainer->initPrestaShopCore(); + $channel = $config[UpgradeConfiguration::CHANNEL]; + + if ($channel === UpgradeConfiguration::CHANNEL_ONLINE_RECOMMENDED || $channel === UpgradeConfiguration::CHANNEL_ONLINE) { + $targetPsVersion = $this->upgradeContainer->getUpgrader()->getOnlineDestinationVersionForChannel($channel); + } else { + $zip = $input->getOption('zip'); + + if (empty($zip)) { + $output->writeln(' ✗ Please specify the destination zip file using the zip option..'); + + return ExitCode::FAIL; + } + + $fullFilePath = $this->upgradeContainer->getProperty(UpgradeContainer::DOWNLOAD_PATH) . DIRECTORY_SEPARATOR . $zip; + try { + $targetPsVersion = $this->upgradeContainer->getPrestashopVersionService()->extractPrestashopVersionFromZip($fullFilePath); + } catch (Exception $exception) { + $output->writeln(' ✗ We couldn\'t find a PrestaShop version in the .zip file that was uploaded in your local archive. Please try again.'); + + return ExitCode::FAIL; + } + } + + if ($targetPsVersion === null || version_compare($this->upgradeContainer->getCurrentPrestaShopVersion(), $targetPsVersion, '>=')) { + $output->writeln(' ✗ You are already running a PrestaShop version equal to or higher than the latest available for update.'); + + return ExitCode::FAIL; + } + + $modulesInstalled = $this->upgradeContainer->getModuleAdapter()->listModulesPresentInFolderAndInstalled(); + $marketplaceService = $this->upgradeContainer->getMarketplaceService(); + + if (!empty($modulesInstalled)) { + $progressIndicator = new ProgressIndicator($output); + $output->writeln(sprintf('Prestashop version: %s', $targetPsVersion)); + $progressIndicator->start('Retrieving modules informations, please wait...'); + + $table = new Table($output); + $table->setHeaders([ + 'Module', + 'Compatible', + 'Update available', + 'Local version', + 'Update version available', + ]); + + foreach ($modulesInstalled as $localModule) { + $progressIndicator->advance(); + $localModuleName = $localModule['name']; + $localVersion = $localModule['currentVersion']; + + try { + $moduleDetails = $marketplaceService->getModuleDetail($localModuleName); + } catch (MarketplaceApiException $e) { + $table->addRow([ + $localModuleName, + ' ✗ Unable to retrieve module informations', + ]); + continue; + } + + $analysis = $this->analyzeModuleReleases( + $moduleDetails, + $targetPsVersion, + $localVersion + ); + + $table->addRow([ + $localModuleName, + $analysis['compatible'] ? '✓ Yes' : '✗ No', + $analysis['update_available'] ? '✓ Yes' : '✗ No', + $localVersion, + $analysis['compatible'] ? $analysis['compatible_release']->productVersion : '-', + ]); + } + $progressIndicator->finish('Result:'); + $table->render(); + } + + return ExitCode::SUCCESS; + } catch (Exception $e) { + $this->logger->error("An error occurred during the check process:\n" . $e); + throw $e; + } + } + + /** + * @return array{ + * compatible: bool, + * update_available: ?bool, + * compatible_release: ?Release, + * latest_release: Release + * } $data + */ + private function analyzeModuleReleases( + Module $module, + string $targetVersion, + string $localVersion + ): array { + $releases = $module->technicalInfo->releases; + + $compatibleReleases = []; + $latestRelease = null; + + foreach ($releases as $release) { + if (!$latestRelease || version_compare($release->productVersion, $latestRelease->productVersion, '>')) { + $latestRelease = $release; + } + + if (version_compare($targetVersion, $release->compatibleFrom, '>=') && + version_compare($targetVersion, $release->compatibleTo, '<=')) { + $compatibleReleases[] = $release; + } + } + + if (empty($compatibleReleases)) { + return [ + 'compatible' => false, + 'update_available' => false, + 'compatible_release' => null, + 'latest_release' => $latestRelease, + ]; + } + + usort($compatibleReleases, function ($a, $b) { + return version_compare($b->productVersion, $a->productVersion); + }); + $bestCompatible = $compatibleReleases[0]; + + return [ + 'compatible' => true, + 'update_available' => version_compare($bestCompatible->productVersion, $localVersion, '>'), + 'compatible_release' => $bestCompatible, + 'latest_release' => $latestRelease, + ]; + } +} diff --git a/classes/Exceptions/MarketplaceApiException.php b/classes/Exceptions/MarketplaceApiException.php new file mode 100644 index 000000000..eb8de66d4 --- /dev/null +++ b/classes/Exceptions/MarketplaceApiException.php @@ -0,0 +1,31 @@ + + * @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\Exceptions; + +use Exception; + +class MarketplaceApiException extends Exception +{ + const API_NOT_CALLABLE_CODE = 0; + const VERSION_NOT_FOUND_CODE = 1; + const EMPTY_DATA_CODE = 2; +} diff --git a/classes/Models/Module/Marketplace/Attributes.php b/classes/Models/Module/Marketplace/Attributes.php new file mode 100644 index 000000000..99d1879d8 --- /dev/null +++ b/classes/Models/Module/Marketplace/Attributes.php @@ -0,0 +1,44 @@ + + * @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\Models\Module\Marketplace; + +class Attributes +{ + /** @var string[] */ + public $groups = []; + /** @var string[] */ + public $combinations = []; + + /** + * @param array{ + * groups?: array, + * combinations?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + $obj->groups = $data['groups'] ?? []; + $obj->combinations = $data['combinations'] ?? []; + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Compatibility.php b/classes/Models/Module/Marketplace/Compatibility.php new file mode 100644 index 000000000..82fec1cc3 --- /dev/null +++ b/classes/Models/Module/Marketplace/Compatibility.php @@ -0,0 +1,49 @@ + + * @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\Models\Module\Marketplace; + +class Compatibility +{ + /** @var bool */ + public $checked = false; + /** @var string[] */ + public $versions = []; + /** @var string[] */ + public $defaultVersions = []; + + /** + * @param array{ + * compliance_checked?: bool, + * versions?: array, + * default_versions?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->checked = (bool) ($data['compliance_checked'] ?? false); + $obj->versions = $data['versions'] ?? []; + $obj->defaultVersions = $data['default_versions'] ?? []; + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Compliance.php b/classes/Models/Module/Marketplace/Compliance.php new file mode 100644 index 000000000..6acdecf77 --- /dev/null +++ b/classes/Models/Module/Marketplace/Compliance.php @@ -0,0 +1,45 @@ + + * @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\Models\Module\Marketplace; + +class Compliance +{ + /** @var Compatibility */ + public $compatibility; + /** @var Overrides */ + public $overrides; + + /** + * @param array{ + * compatibility?: array, + * overrides?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->compatibility = Compatibility::fromArray($data['compatibility'] ?? []); + $obj->overrides = Overrides::fromArray($data['overrides'] ?? []); + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Module.php b/classes/Models/Module/Marketplace/Module.php new file mode 100644 index 000000000..93e914ff3 --- /dev/null +++ b/classes/Models/Module/Marketplace/Module.php @@ -0,0 +1,53 @@ + + * @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\Models\Module\Marketplace; + +class Module +{ + /** @var Attributes */ + public $attributes; + /** @var Compliance */ + public $compliance; + /** @var Product */ + public $product; + /** @var TechnicalInfo */ + public $technicalInfo; + + /** + * @param array{ + * attributes?: array, + * compliance?: array, + * product?: array, + * technical_info?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->attributes = Attributes::fromArray($data['attributes'] ?? []); + $obj->compliance = Compliance::fromArray($data['compliance'] ?? []); + $obj->product = Product::fromArray($data['product'] ?? []); + $obj->technicalInfo = TechnicalInfo::fromArray($data['technical_info'] ?? []); + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Overrides.php b/classes/Models/Module/Marketplace/Overrides.php new file mode 100644 index 000000000..dc5612996 --- /dev/null +++ b/classes/Models/Module/Marketplace/Overrides.php @@ -0,0 +1,40 @@ + + * @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\Models\Module\Marketplace; + +class Overrides +{ + /** @var bool */ + public $complianceChecked = false; + + /** + * @param array{ + * compliance_checked?: bool + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + $obj->complianceChecked = (bool) ($data['compliance_checked'] ?? false); + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Product.php b/classes/Models/Module/Marketplace/Product.php new file mode 100644 index 000000000..90f59ef32 --- /dev/null +++ b/classes/Models/Module/Marketplace/Product.php @@ -0,0 +1,57 @@ + + * @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\Models\Module\Marketplace; + +class Product +{ + /** @var int */ + public $id; + /** @var string */ + public $productType = ''; + /** @var bool */ + public $isNative = false; + /** @var bool */ + public $downloadable = false; + /** @var bool */ + public $isActive = false; + + /** + * @param array{ + * id_product?: int, + * product_type?: string, + * is_native?: bool, + * downloadable?: bool, + * is_active?: bool + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->id = (int) ($data['id_product'] ?? 0); + $obj->productType = $data['product_type'] ?? ''; + $obj->isNative = (bool) ($data['is_native'] ?? false); + $obj->downloadable = (bool) ($data['downloadable'] ?? false); + $obj->isActive = (bool) ($data['is_active'] ?? false); + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/Release.php b/classes/Models/Module/Marketplace/Release.php new file mode 100644 index 000000000..96e714289 --- /dev/null +++ b/classes/Models/Module/Marketplace/Release.php @@ -0,0 +1,57 @@ + + * @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\Models\Module\Marketplace; + +class Release +{ + /** @var string */ + public $compatibleFrom = ''; + /** @var string */ + public $compatibleTo = ''; + /** @var string */ + public $productVersion = ''; + /** @var string */ + public $releaseDate = ''; + /** @var array */ + public $changeLogs = []; + + /** + * @param array{ + * compatible_from?: string, + * compatible_to?: string, + * product_version?: string, + * release_date?: string, + * change_logs?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->compatibleFrom = $data['compatible_from'] ?? ''; + $obj->compatibleTo = $data['compatible_to'] ?? ''; + $obj->productVersion = $data['product_version'] ?? ''; + $obj->releaseDate = $data['release_date'] ?? ''; + $obj->changeLogs = $data['change_logs'] ?? []; + + return $obj; + } +} diff --git a/classes/Models/Module/Marketplace/TechnicalInfo.php b/classes/Models/Module/Marketplace/TechnicalInfo.php new file mode 100644 index 000000000..7b55ce082 --- /dev/null +++ b/classes/Models/Module/Marketplace/TechnicalInfo.php @@ -0,0 +1,58 @@ + + * @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\Models\Module\Marketplace; + +class TechnicalInfo +{ + /** @var string */ + public $publicationDate = ''; + /** @var Release[] */ + public $releases = []; + /** @var ?Release */ + public $lastRelease = null; + + /** + * @param array{ + * publication_date?: string, + * releases?: array, + * last_release?: array + * } $data + */ + public static function fromArray(array $data): self + { + $obj = new self(); + + $obj->publicationDate = $data['publication_date'] ?? ''; + + $obj->releases = []; + if (!empty($data['releases'])) { + foreach ($data['releases'] as $releaseData) { + $obj->releases[] = Release::fromArray($releaseData); + } + } + + $obj->lastRelease = !empty($data['last_release']) + ? Release::fromArray($data['last_release']) + : null; + + return $obj; + } +} diff --git a/classes/Services/MarketplaceService.php b/classes/Services/MarketplaceService.php new file mode 100644 index 000000000..01ab952af --- /dev/null +++ b/classes/Services/MarketplaceService.php @@ -0,0 +1,62 @@ + + * @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\Services; + +use PrestaShop\Module\AutoUpgrade\Exceptions\MarketplaceApiException; +use PrestaShop\Module\AutoUpgrade\Models\Module\Marketplace\Module; +use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator; + +class MarketplaceService +{ + /** @var Translator */ + private $translator; + + const ADDONS_API_URL = 'https://api.addons.prestashop.com'; + + /** + * @param Translator $translator + */ + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + /** + * @return Module|null + */ + public function getModuleDetail(string $module) + { + $response = @file_get_contents(self::ADDONS_API_URL . '/v2/products/' . $module); + + if (!$response) { + throw new MarketplaceApiException($this->translator->trans('Error when retrieving data from Distribution API'), MarketplaceApiException::API_NOT_CALLABLE_CODE); + } + + $data = json_decode($response, true); + + if (!$data || !is_array($data)) { + return null; + } + + return Module::fromArray($data); + } +} diff --git a/classes/UpgradeContainer.php b/classes/UpgradeContainer.php index 7d76fb6d1..3a448a8c7 100644 --- a/classes/UpgradeContainer.php +++ b/classes/UpgradeContainer.php @@ -42,6 +42,7 @@ use PrestaShop\Module\AutoUpgrade\Services\DownloadService; use PrestaShop\Module\AutoUpgrade\Services\LocalVersionFilesService; use PrestaShop\Module\AutoUpgrade\Services\LogsService; +use PrestaShop\Module\AutoUpgrade\Services\MarketplaceService; use PrestaShop\Module\AutoUpgrade\Services\PhpVersionResolverService; use PrestaShop\Module\AutoUpgrade\Services\PrestashopVersionService; use PrestaShop\Module\AutoUpgrade\Services\UpdateNotificationService; @@ -248,6 +249,9 @@ class UpgradeContainer /** @var DownloadService */ private $downloadService; + /** @var MarketplaceService */ + private $marketplaceService; + /** @var UrlGenerator */ private $urlGenerator; @@ -1021,6 +1025,18 @@ public function getDownloadService(): DownloadService return $this->downloadService = new DownloadService($this->getTranslator(), $this->getLogger()); } + /** + * @return MarketplaceService + */ + public function getMarketplaceService(): MarketplaceService + { + if (null !== $this->marketplaceService) { + return $this->marketplaceService; + } + + return $this->marketplaceService = new MarketplaceService($this->getTranslator()); + } + /** * @return LocalVersionFilesService */ diff --git a/classes/UpgradeTools/Module/Source/Provider/MarketplaceSourceProvider.php b/classes/UpgradeTools/Module/Source/Provider/MarketplaceSourceProvider.php index 1faa66e8c..6e8f055e7 100644 --- a/classes/UpgradeTools/Module/Source/Provider/MarketplaceSourceProvider.php +++ b/classes/UpgradeTools/Module/Source/Provider/MarketplaceSourceProvider.php @@ -23,6 +23,7 @@ use PrestaShop\Module\AutoUpgrade\Parameters\FileStorage; use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeFileNames; +use PrestaShop\Module\AutoUpgrade\Services\MarketplaceService; use PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\Source\ModuleSource; use PrestaShop\Module\AutoUpgrade\Xml\FileLoader; @@ -31,8 +32,6 @@ */ class MarketplaceSourceProvider extends AbstractModuleSourceProvider { - const ADDONS_API_URL = 'https://api.addons.prestashop.com'; - /** @var FileLoader */ private $fileLoader; @@ -70,7 +69,7 @@ public function warmUp(): void $xml = $this->fileLoader->getXmlFile( $this->prestashopRootFolder . '/config/xml/modules_native_addons.xml', - self::ADDONS_API_URL . '/?' . $postData + MarketplaceService::ADDONS_API_URL . '/?' . $postData ); if ($xml === false) { @@ -83,7 +82,7 @@ public function warmUp(): void $this->localModuleZips[] = new ModuleSource( (string) $moduleInXml->name, (string) $moduleInXml->version, - self::ADDONS_API_URL . '/?' . http_build_query([ + MarketplaceService::ADDONS_API_URL . '/?' . http_build_query([ 'id_module' => (string) $moduleInXml->id, 'method' => 'module', 'version' => $this->targetVersionOfPrestaShop, diff --git a/classes/Upgrader.php b/classes/Upgrader.php index 357d6c7bc..3fd2c7afa 100755 --- a/classes/Upgrader.php +++ b/classes/Upgrader.php @@ -177,6 +177,20 @@ public function getDestinationVersion(): ?string return null; } + /** + * @throws UpgradeException + */ + public function getOnlineDestinationVersionForChannel(string $channel): ?string + { + if ($channel === UpgradeConfiguration::CHANNEL_ONLINE) { + return $this->getOnlineMaxDestinationRelease() ? $this->getOnlineMaxDestinationRelease()->getVersion() : null; + } elseif ($channel === UpgradeConfiguration::CHANNEL_ONLINE_RECOMMENDED) { + return $this->getOnlineRecommendedDestinationRelease() ? $this->getOnlineRecommendedDestinationRelease()->getVersion() : null; + } + + throw new UpgradeException(sprintf('Channel accepted: %s, %s', UpgradeConfiguration::CHANNEL_ONLINE, UpgradeConfiguration::CHANNEL_ONLINE_RECOMMENDED)); + } + /** * @throws DistributionApiException * @throws UpgradeException