Commit 77b86bc8 authored by Ted Bowman's avatar Ted Bowman
Browse files

Issue #3305773 by omkar.podey, tedbow: Updates ExtensionUpdater to do correct...

Issue #3305773 by omkar.podey, tedbow: Updates ExtensionUpdater to do correct project to package conversion
parent e4efc8b8
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -18,14 +18,6 @@ services:
      - '@string_translation'
    tags:
      - { name: event_subscriber }
  automatic_updates_extensions.validator.packages_type:
    class: Drupal\automatic_updates_extensions\Validator\UpdatePackagesTypeValidator
    arguments:
      - '@string_translation'
      - '@extension.list.module'
      - '@extension.list.theme'
    tags:
      - { name: event_subscriber }
  automatic_updates_extensions.validator.target_release:
    class: Drupal\automatic_updates_extensions\Validator\UpdateReleaseValidator
    tags:
+4 −1
Original line number Diff line number Diff line
@@ -43,7 +43,10 @@ class ExtensionUpdater extends Stage {
      ->getPackage()
      ->getDevRequires();
    foreach ($project_versions as $project_name => $version) {
      $package = "drupal/$project_name";
      $package = $composer->getPackageForProject($project_name);
      if (empty($package)) {
        throw new \InvalidArgumentException("The project $project_name is not a Drupal project known to Composer and cannot be updated.");
      }
      $group = array_key_exists($package, $require_dev) ? 'dev' : 'production';
      $package_versions[$group][$package] = LegacyVersionUtility::convertToSemanticVersion($version);
    }
+25 −40
Original line number Diff line number Diff line
@@ -7,12 +7,12 @@ use Drupal\automatic_updates_extensions\ExtensionUpdater;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Validates packages are installed via Composer.
 *
 * @todo Remove this validator completely in https://www.drupal.org/i/3303900.
 */
class PackagesInstalledWithComposerValidator implements EventSubscriberInterface {

@@ -31,10 +31,10 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface
  /**
   * Validates that packages are installed with composer or not.
   *
   * @param \Drupal\package_manager\Event\PreOperationStageEvent $event
   * @param \Drupal\package_manager\Event\PreApplyEvent $event
   *   The event object.
   */
  public function checkPackagesInstalledWithComposer(PreOperationStageEvent $event): void {
  public function checkPackagesInstalledWithComposer(PreApplyEvent $event): void {
    $stage = $event->getStage();

    if (!$stage instanceof ExtensionUpdater) {
@@ -54,7 +54,6 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface
   */
  public static function getSubscribedEvents() {
    return [
      PreCreateEvent::class => 'checkPackagesInstalledWithComposer',
      PreApplyEvent::class => 'checkPackagesInstalledWithComposer',
    ];
  }
@@ -62,29 +61,16 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface
  /**
   * Gets the packages which aren't installed via composer.
   *
   * @param \Drupal\package_manager\Event\PreOperationStageEvent $event
   * @param \Drupal\package_manager\Event\PreApplyEvent $event
   *   The event object.
   *
   * @return \Composer\Package\PackageInterface[]
   *   Packages not installed via composer.
   */
  protected function getPackagesNotInstalledWithComposer(PreOperationStageEvent $event): array {
  protected function getPackagesNotInstalledWithComposer(PreApplyEvent $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,
@@ -106,7 +92,6 @@ class PackagesInstalledWithComposerValidator implements EventSubscriberInterface
    $missing_packages = array_filter($missing_packages, function (string $key) {
      return str_starts_with($key, 'drupal/');
    }, ARRAY_FILTER_USE_KEY);
    }
    return $missing_packages;
  }

+0 −103
Original line number Diff line number Diff line
<?php

namespace Drupal\automatic_updates_extensions\Validator;

use Drupal\automatic_updates_extensions\ExtensionUpdater;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Utility\ProjectInfo;
use Drupal\package_manager\Event\PreCreateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Validates the type of updated packages.
 */
class UpdatePackagesTypeValidator implements EventSubscriberInterface {

  use StringTranslationTrait;

  /**
   * The module list service.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleList;

  /**
   * The theme list service.
   *
   * @var \Drupal\Core\Extension\ThemeExtensionList
   */
  protected $themeList;

  /**
   * Constructs a UpdatePackagesTypeValidator object.
   *
   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
   *   The translation service.
   * @param \Drupal\Core\Extension\ModuleExtensionList $module_list
   *   The module list service.
   * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list
   *   The theme list service.
   */
  public function __construct(TranslationInterface $translation, ModuleExtensionList $module_list, ThemeExtensionList $theme_list) {
    $this->setStringTranslation($translation);
    $this->moduleList = $module_list;
    $this->themeList = $theme_list;
  }

  /**
   * Validates that updated packages are only modules or themes.
   *
   * @param \Drupal\package_manager\Event\PreCreateEvent $event
   *   The event object.
   */
  public function checkPackagesAreOnlyThemesOrModules(PreCreateEvent $event): void {
    $stage = $event->getStage();
    if (!$stage instanceof ExtensionUpdater) {
      return;
    }

    $invalid_projects = [];
    $all_projects = $this->getInstalledProjectNames();

    foreach ($stage->getPackageVersions() as $group) {
      foreach (array_keys($group) as $package) {
        // @todo Use
        //   \Drupal\package_manager\ComposerUtility::getProjectForPackage() to
        //   determine the project name in https://www.drupal.org/i/3304142.
        $update_project = str_replace('drupal/', '', $package);
        if ($update_project === 'drupal' || !in_array($update_project, $all_projects, TRUE)) {
          $invalid_projects[] = $update_project;
        }
      }
    }
    if ($invalid_projects) {
      $event->addError($invalid_projects, $this->t('The following projects cannot be updated because they are not Drupal modules or themes:'));
    }
  }

  /**
   * Returns a list of all available modules and themes' project names.
   *
   * @return string[]
   *   The project names of all available modules and themes.
   */
  private function getInstalledProjectNames(): array {
    $extension_list = array_merge($this->themeList->getList(), $this->moduleList->getList());
    $map = \Closure::fromCallable([new ProjectInfo(), 'getProjectName']);
    return array_map($map, $extension_list);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      PreCreateEvent::class => 'checkPackagesAreOnlyThemesOrModules',
    ];
  }

}
+4 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 * @internal
 *   This class is an internal part of the module's update handling and
 *   should not be used by external code.
 *
 * @todo Remove this validator completely in https://www.drupal.org/i/3307369.
 */
final class UpdateReleaseValidator implements EventSubscriberInterface {

@@ -79,7 +81,7 @@ final class UpdateReleaseValidator implements EventSubscriberInterface {
        ['drupal-module', 'drupal-theme'], TRUE)) {
        continue;
      }
      [, $project_name] = explode('/', $staged_package->getName());
      $project_name = $staged->getProjectForPackage($staged_package->getName());
      $semantic_version = $staged_package->getPrettyVersion();
      if (!$this->isSupportedRelease($project_name, $semantic_version)) {
        $messages[] = $this->t('Project @project_name to version @version', [
@@ -115,8 +117,7 @@ final class UpdateReleaseValidator implements EventSubscriberInterface {
    $messages = [];
    foreach (['production', 'dev'] as $package_type) {
      foreach ($all_versions[$package_type] as $package_name => $sematic_version) {
        $package_parts = explode('/', $package_name);
        $project_name = $package_parts[1];
        $project_name = $stage->getActiveComposer()->getProjectForPackage($package_name);
        // If the version isn't in the list of installable releases, then it
        // isn't secure and supported and the user should receive an error.
        if (!$this->isSupportedRelease($project_name, $sematic_version)) {
Loading