Loading package_manager/package_manager.services.yml +3 −0 Changes for package_manager/package_manager.services.yml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -168,3 +168,6 @@ services: class: Drupal\package_manager\Validator\ComposerPatchesValidator tags: - { name: event_subscriber } package_manager.update_processor: class: Drupal\package_manager\PackageManagerUpdateProcessor arguments: [ '@config.factory', '@queue', '@update.fetcher', '@state', '@private_key', '@keyvalue', '@keyvalue.expirable' ] package_manager/src/PackageManagerUpdateProcessor.php 0 → 100644 +98 −0 Changes for package_manager/src/PackageManagerUpdateProcessor.php: 98 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Drupal\package_manager; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; use Drupal\Core\PrivateKey; use Drupal\Core\Queue\QueueFactory; use Drupal\Core\State\StateInterface; use Drupal\update\UpdateFetcherInterface; use Drupal\update\UpdateProcessor; /** * Extends the Update module's update processor allow fetching any project. * * The Update module's update processor service is intended to only fetch * information for projects in the active codebase. Although it would be * possible to use the Update module's update processor service to fetch * information for projects not in the active code base this would add the * project information to Update module's cache which would result in these * projects being returned from the Update module's global functions such as * update_get_available(). */ class PackageManagerUpdateProcessor extends UpdateProcessor { /** * Constructs an PackageManagerUpdateProcessor object. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. * @param \Drupal\Core\Queue\QueueFactory $queue_factory * The queue factory. * @param \Drupal\update\UpdateFetcherInterface $update_fetcher * The update fetcher service. * @param \Drupal\Core\State\StateInterface $state_store * The state service. * @param \Drupal\Core\PrivateKey $private_key * The private key factory service. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory * The key/value factory. * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory * The expirable key/value factory. */ public function __construct(ConfigFactoryInterface $config_factory, QueueFactory $queue_factory, UpdateFetcherInterface $update_fetcher, StateInterface $state_store, PrivateKey $private_key, KeyValueFactoryInterface $key_value_factory, KeyValueExpirableFactoryInterface $key_value_expirable_factory) { $this->updateFetcher = $update_fetcher; $this->updateSettings = $config_factory->get('update.settings'); $this->fetchQueue = $queue_factory->get('package_manager.update_fetch_tasks'); $this->tempStore = $key_value_expirable_factory->get('package_manager.update'); $this->fetchTaskStore = $key_value_factory->get('package_manager.update_fetch_task'); $this->availableReleasesTempStore = $key_value_expirable_factory->get('package_manager.update_available_releases'); $this->stateStore = $state_store; $this->privateKey = $private_key; $this->fetchTasks = []; $this->failed = []; } /** * Gets the project data by name. * * @param string $name * The project name. * * @return mixed[] * The project data if any is available, otherwise NULL. */ public function getProjectData(string $name): ?array { if ($this->availableReleasesTempStore->has($name)) { return $this->availableReleasesTempStore->get($name); } $project_fetch_data = [ 'name' => $name, 'project_type' => 'unknown', 'includes' => [], ]; $this->createFetchTask($project_fetch_data); if ($this->processFetchTask($project_fetch_data)) { // If the fetch task was successful return the project information. return $this->availableReleasesTempStore->get($name); } return NULL; } /** * {@inheritdoc} */ public function processFetchTask($project) { // The parent method will set 'update.last_check' which will be used to // inform the user when the last time update information was checked. In // order to leave this value unaffected we will reset this to it's previous // value. $last_check = $this->stateStore->get('update.last_check'); $success = parent::processFetchTask($project); $this->stateStore->set('update.last_check', $last_check); return $success; } } package_manager/src/ProjectInfo.php +47 −11 Changes for package_manager/src/ProjectInfo.php: 47 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -74,9 +74,12 @@ final class ProjectInfo { * If data about available updates cannot be retrieved. */ public function getProjectInfo(): ?array { $available_updates = update_get_available(TRUE); $available_updates = $this->getAvailableProjects(); $project_data = update_calculate_project_data($available_updates); return $project_data[$this->name] ?? NULL; if (!isset($project_data[$this->name])) { return $available_updates[$this->name] ?? NULL; } return $project_data[$this->name]; } /** Loading @@ -99,32 +102,40 @@ final class ProjectInfo { if (!$project) { return NULL; } $available_updates = update_get_available()[$this->name]; $available_updates = $this->getAvailableProjects()[$this->name]; if ($available_updates['project_status'] !== 'published') { throw new \RuntimeException("The project '{$this->name}' can not be updated because its status is " . $available_updates['project_status']); } $installed_version = $this->getInstalledVersion(); // If we're already up-to-date, there's nothing else we need to do. if ($installed_version) { // If the project is installed, and we're already up-to-date, there's // nothing else we need to do. if ($project['status'] === UpdateManagerInterface::CURRENT) { return []; } if (empty($available_updates['releases'])) { // If project is not current we should always have at least one release. // If project is installed but not current we should always have at // least one release. throw new \RuntimeException('There was a problem getting update information. Try again later.'); } $installed_version = $this->getInstalledVersion(); } $support_branches = explode(',', $available_updates['supported_branches']); $installable_releases = []; foreach ($available_updates['releases'] as $release_info) { $release = ProjectRelease::createFromArray($release_info); $version = $release->getVersion(); if ($installed_version) { $semantic_version = LegacyVersionUtility::convertToSemanticVersion($version); $semantic_installed_version = LegacyVersionUtility::convertToSemanticVersion($installed_version); if (Comparator::lessThanOrEqualTo($semantic_version, $semantic_installed_version)) { // Stop searching for releases as soon as we find the installed version. // If the project is installed stop searching for releases as soon as // we find the installed version. break; } } if ($this->isInstallable($release, $support_branches)) { $installable_releases[$version] = $release; } Loading @@ -141,12 +152,37 @@ final class ProjectInfo { */ public function getInstalledVersion(): ?string { if ($project_data = $this->getProjectInfo()) { if (empty($project_data['existing_version'])) { throw new \UnexpectedValueException("Project '{$this->name}' does not have 'existing_version' set"); } return $project_data['existing_version']; return $project_data['existing_version'] ?? NULL; } return NULL; } /** * Gets the available projects. * * @return array * The available projects keyed by project machine name in the format * returned by update_get_available(). If the project specified in ::name is * not returned from update_get_available() this project will be explicitly * fetched and added the return value of this function. * * @see \update_get_available() */ private function getAvailableProjects(): array { $available_projects = update_get_available(TRUE); // update_get_available() will only returns projects that are in the active // codebase. If the project specified by ::name is not returned in // $available_projects, it means it is not in the active codebase, therefore // we will retrieve the project information from Package Manager's own // update processor service. if (!isset($available_projects[$this->name])) { /** @var \Drupal\package_manager\PackageManagerUpdateProcessor $update_processor */ $update_processor = \Drupal::service('package_manager.update_processor'); if ($project_data = $update_processor->getProjectData($this->name)) { $available_projects[$this->name] = $project_data; } } return $available_projects; } } package_manager/tests/src/Kernel/ComposerUtilityTest.php +1 −1 Changes for package_manager/tests/src/Kernel/ComposerUtilityTest.php: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ class ComposerUtilityTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['package_manager']; protected static $modules = ['package_manager', 'update']; /** * {@inheritdoc} Loading package_manager/tests/src/Kernel/FileSyncerFactoryTest.php +1 −1 Changes for package_manager/tests/src/Kernel/FileSyncerFactoryTest.php: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ class FileSyncerFactoryTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['package_manager']; protected static $modules = ['package_manager', 'update']; /** * Data provider for testFactory(). Loading Loading
package_manager/package_manager.services.yml +3 −0 Changes for package_manager/package_manager.services.yml: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -168,3 +168,6 @@ services: class: Drupal\package_manager\Validator\ComposerPatchesValidator tags: - { name: event_subscriber } package_manager.update_processor: class: Drupal\package_manager\PackageManagerUpdateProcessor arguments: [ '@config.factory', '@queue', '@update.fetcher', '@state', '@private_key', '@keyvalue', '@keyvalue.expirable' ]
package_manager/src/PackageManagerUpdateProcessor.php 0 → 100644 +98 −0 Changes for package_manager/src/PackageManagerUpdateProcessor.php: 98 added lines, 0 removed lines. Original line number Diff line number Diff line <?php namespace Drupal\package_manager; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; use Drupal\Core\PrivateKey; use Drupal\Core\Queue\QueueFactory; use Drupal\Core\State\StateInterface; use Drupal\update\UpdateFetcherInterface; use Drupal\update\UpdateProcessor; /** * Extends the Update module's update processor allow fetching any project. * * The Update module's update processor service is intended to only fetch * information for projects in the active codebase. Although it would be * possible to use the Update module's update processor service to fetch * information for projects not in the active code base this would add the * project information to Update module's cache which would result in these * projects being returned from the Update module's global functions such as * update_get_available(). */ class PackageManagerUpdateProcessor extends UpdateProcessor { /** * Constructs an PackageManagerUpdateProcessor object. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. * @param \Drupal\Core\Queue\QueueFactory $queue_factory * The queue factory. * @param \Drupal\update\UpdateFetcherInterface $update_fetcher * The update fetcher service. * @param \Drupal\Core\State\StateInterface $state_store * The state service. * @param \Drupal\Core\PrivateKey $private_key * The private key factory service. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory * The key/value factory. * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory * The expirable key/value factory. */ public function __construct(ConfigFactoryInterface $config_factory, QueueFactory $queue_factory, UpdateFetcherInterface $update_fetcher, StateInterface $state_store, PrivateKey $private_key, KeyValueFactoryInterface $key_value_factory, KeyValueExpirableFactoryInterface $key_value_expirable_factory) { $this->updateFetcher = $update_fetcher; $this->updateSettings = $config_factory->get('update.settings'); $this->fetchQueue = $queue_factory->get('package_manager.update_fetch_tasks'); $this->tempStore = $key_value_expirable_factory->get('package_manager.update'); $this->fetchTaskStore = $key_value_factory->get('package_manager.update_fetch_task'); $this->availableReleasesTempStore = $key_value_expirable_factory->get('package_manager.update_available_releases'); $this->stateStore = $state_store; $this->privateKey = $private_key; $this->fetchTasks = []; $this->failed = []; } /** * Gets the project data by name. * * @param string $name * The project name. * * @return mixed[] * The project data if any is available, otherwise NULL. */ public function getProjectData(string $name): ?array { if ($this->availableReleasesTempStore->has($name)) { return $this->availableReleasesTempStore->get($name); } $project_fetch_data = [ 'name' => $name, 'project_type' => 'unknown', 'includes' => [], ]; $this->createFetchTask($project_fetch_data); if ($this->processFetchTask($project_fetch_data)) { // If the fetch task was successful return the project information. return $this->availableReleasesTempStore->get($name); } return NULL; } /** * {@inheritdoc} */ public function processFetchTask($project) { // The parent method will set 'update.last_check' which will be used to // inform the user when the last time update information was checked. In // order to leave this value unaffected we will reset this to it's previous // value. $last_check = $this->stateStore->get('update.last_check'); $success = parent::processFetchTask($project); $this->stateStore->set('update.last_check', $last_check); return $success; } }
package_manager/src/ProjectInfo.php +47 −11 Changes for package_manager/src/ProjectInfo.php: 47 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -74,9 +74,12 @@ final class ProjectInfo { * If data about available updates cannot be retrieved. */ public function getProjectInfo(): ?array { $available_updates = update_get_available(TRUE); $available_updates = $this->getAvailableProjects(); $project_data = update_calculate_project_data($available_updates); return $project_data[$this->name] ?? NULL; if (!isset($project_data[$this->name])) { return $available_updates[$this->name] ?? NULL; } return $project_data[$this->name]; } /** Loading @@ -99,32 +102,40 @@ final class ProjectInfo { if (!$project) { return NULL; } $available_updates = update_get_available()[$this->name]; $available_updates = $this->getAvailableProjects()[$this->name]; if ($available_updates['project_status'] !== 'published') { throw new \RuntimeException("The project '{$this->name}' can not be updated because its status is " . $available_updates['project_status']); } $installed_version = $this->getInstalledVersion(); // If we're already up-to-date, there's nothing else we need to do. if ($installed_version) { // If the project is installed, and we're already up-to-date, there's // nothing else we need to do. if ($project['status'] === UpdateManagerInterface::CURRENT) { return []; } if (empty($available_updates['releases'])) { // If project is not current we should always have at least one release. // If project is installed but not current we should always have at // least one release. throw new \RuntimeException('There was a problem getting update information. Try again later.'); } $installed_version = $this->getInstalledVersion(); } $support_branches = explode(',', $available_updates['supported_branches']); $installable_releases = []; foreach ($available_updates['releases'] as $release_info) { $release = ProjectRelease::createFromArray($release_info); $version = $release->getVersion(); if ($installed_version) { $semantic_version = LegacyVersionUtility::convertToSemanticVersion($version); $semantic_installed_version = LegacyVersionUtility::convertToSemanticVersion($installed_version); if (Comparator::lessThanOrEqualTo($semantic_version, $semantic_installed_version)) { // Stop searching for releases as soon as we find the installed version. // If the project is installed stop searching for releases as soon as // we find the installed version. break; } } if ($this->isInstallable($release, $support_branches)) { $installable_releases[$version] = $release; } Loading @@ -141,12 +152,37 @@ final class ProjectInfo { */ public function getInstalledVersion(): ?string { if ($project_data = $this->getProjectInfo()) { if (empty($project_data['existing_version'])) { throw new \UnexpectedValueException("Project '{$this->name}' does not have 'existing_version' set"); } return $project_data['existing_version']; return $project_data['existing_version'] ?? NULL; } return NULL; } /** * Gets the available projects. * * @return array * The available projects keyed by project machine name in the format * returned by update_get_available(). If the project specified in ::name is * not returned from update_get_available() this project will be explicitly * fetched and added the return value of this function. * * @see \update_get_available() */ private function getAvailableProjects(): array { $available_projects = update_get_available(TRUE); // update_get_available() will only returns projects that are in the active // codebase. If the project specified by ::name is not returned in // $available_projects, it means it is not in the active codebase, therefore // we will retrieve the project information from Package Manager's own // update processor service. if (!isset($available_projects[$this->name])) { /** @var \Drupal\package_manager\PackageManagerUpdateProcessor $update_processor */ $update_processor = \Drupal::service('package_manager.update_processor'); if ($project_data = $update_processor->getProjectData($this->name)) { $available_projects[$this->name] = $project_data; } } return $available_projects; } }
package_manager/tests/src/Kernel/ComposerUtilityTest.php +1 −1 Changes for package_manager/tests/src/Kernel/ComposerUtilityTest.php: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ class ComposerUtilityTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['package_manager']; protected static $modules = ['package_manager', 'update']; /** * {@inheritdoc} Loading
package_manager/tests/src/Kernel/FileSyncerFactoryTest.php +1 −1 Changes for package_manager/tests/src/Kernel/FileSyncerFactoryTest.php: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ class FileSyncerFactoryTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['package_manager']; protected static $modules = ['package_manager', 'update']; /** * Data provider for testFactory(). Loading