diff --git a/automatic_updates_extensions/src/Validator/PackagesInstalledWithComposerValidator.php b/automatic_updates_extensions/src/Validator/PackagesInstalledWithComposerValidator.php index 82ac5d8897e14be47e8f16bfa7e9ecaa05a0fba1..54af7e4dc49fc49fdb3576f1346e57a76846fc66 100644 --- a/automatic_updates_extensions/src/Validator/PackagesInstalledWithComposerValidator.php +++ b/automatic_updates_extensions/src/Validator/PackagesInstalledWithComposerValidator.php @@ -36,51 +36,16 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface */ public function checkPackagesInstalledWithComposer(PreOperationStageEvent $event): void { $stage = $event->getStage(); - if ($stage instanceof ExtensionUpdater) { - $active_composer = $stage->getActiveComposer(); - $installed_packages = $active_composer->getInstalledPackages(); - $missing_packages = []; - if ($event instanceof PreCreateEvent) { - $package_versions = $stage->getPackageVersions(); - foreach (['production', 'dev'] as $package_type) { - $missing_packages = array_merge($missing_packages, array_diff_key($package_versions[$package_type], $installed_packages)); - } - } - else { - $missing_packages = $stage->getStageComposer() - ->getPackagesNotIn($active_composer); - // For new dependency added in the stage will are only concerned with - // ones that are Drupal projects that have Update XML from Drupal.org - // Since the Update module does allow use to check any of these projects - // if they don't exist in the active code base. Other types of projects - // even if they are in the 'drupal/' namespace they would not have - // Update XML on Drupal.org so it doesn't matter if they are in the - // active codebase or not. - $types = [ - 'drupal-module', - 'drupal-theme', - 'drupal-profile', - ]; - $filter = function (PackageInterface $package) use ($types): bool { - return in_array($package->getType(), $types); - }; - $missing_packages = array_filter($missing_packages, $filter); - // Saving only the packages whose name starts with drupal/. - $missing_packages = array_filter($missing_packages, function (string $key) { - return strpos($key, 'drupal/') === 0; - }, ARRAY_FILTER_USE_KEY); - } - if ($missing_packages) { - $missing_projects = []; - foreach ($missing_packages as $package => $version) { - // Removing drupal/ from package name for better user presentation. - $project = str_replace('drupal/', '', $package); - $missing_projects[] = $project; - } - if ($missing_projects) { - $event->addError($missing_projects, $this->t('Automatic Updates can only update projects that were installed via Composer. The following packages are not installed through composer:')); - } - } + + if (!$stage instanceof ExtensionUpdater) { + return; + } + + $missing_packages = $this->getPackagesNotInstalledWithComposer($event); + if ($missing_packages) { + // Removing drupal/ from package names for better user presentation. + $missing_projects = str_replace('drupal/', '', array_keys($missing_packages)); + $event->addError($missing_projects, $this->t('Automatic Updates can only update projects that were installed via Composer. The following packages are not installed through composer:')); } } @@ -94,4 +59,55 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface ]; } + /** + * Gets the packages which aren't installed via composer. + * + * @param \Drupal\package_manager\Event\PreOperationStageEvent $event + * The event object. + * + * @return \Composer\Package\PackageInterface[] + * Packages not installed via composer. + */ + protected function getPackagesNotInstalledWithComposer(PreOperationStageEvent $event): array { + $stage = $event->getStage(); + $active_composer = $stage->getActiveComposer(); + $installed_packages = $active_composer->getInstalledPackages(); + + $missing_packages = []; + // During pre-create there is no stage directory, so missing packages can be + // found using package versions that will be required during the update, + // while during pre-apply there is stage directory which will be used to + // find missing packages. + if ($event instanceof PreCreateEvent) { + $package_versions = $stage->getPackageVersions(); + foreach (['production', 'dev'] as $package_group) { + $missing_packages = array_merge($missing_packages, array_diff_key($package_versions[$package_group], $installed_packages)); + } + } + else { + $missing_packages = $stage->getStageComposer()->getPackagesNotIn($active_composer); + + // The core update system can only fetch release information for modules, + // themes, or profiles that are in the active code base (whether they're + // installed or not). If a package is not one of those types, ignore it + // even if its vendor namespace is `drupal`. + $types = [ + 'drupal-module', + 'drupal-theme', + 'drupal-profile', + ]; + $filter = function (PackageInterface $package) use ($types): bool { + return in_array($package->getType(), $types, TRUE); + }; + $missing_packages = array_filter($missing_packages, $filter); + + // The core update system can only fetch release information for drupal + // projects, so saving only the packages whose name starts with drupal/. + $missing_packages = array_filter($missing_packages, function (string $key) { + return str_starts_with($key, 'drupal/'); + }, ARRAY_FILTER_USE_KEY); + } + return $missing_packages; + } + }