Commit 718fa096 authored by catch's avatar catch
Browse files

Issue #3077703 by longwave, catch, xjm: Remove pre-8.7.7 core compatibility...

Issue #3077703 by longwave, catch, xjm: Remove pre-8.7.7 core compatibility checks in extension parsing
parent 8de366f6
Loading
Loading
Loading
Loading
+7 −107
Original line number Diff line number Diff line
@@ -18,11 +18,6 @@ class InfoParserDynamic implements InfoParserInterface {
   */
  protected $root;

  /**
   * The earliest Drupal version that supports the 'core_version_requirement'.
   */
  const FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION = '8.7.7';

  /**
   * InfoParserDynamic constructor.
   *
@@ -68,42 +63,20 @@ public function parse($filename) {
          // easier for contrib to use test modules.
          $parsed_info['core_version_requirement'] = \Drupal::VERSION;
        }
        elseif (!isset($parsed_info['core'])) {
        else {
          // Non-core extensions must specify core compatibility.
          throw new InfoParserException("The 'core_version_requirement' key must be present in " . $filename);
        }
      }
      if (isset($parsed_info['core_version_requirement'])) {

      // Determine if the extension is compatible with the current version of
      // Drupal core.
      try {
          $supports_pre_core_version_requirement_version = static::isConstraintSatisfiedByPreviousVersion($parsed_info['core_version_requirement'], static::FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION);
        $parsed_info['core_incompatible'] = !Semver::satisfies(\Drupal::VERSION, $parsed_info['core_version_requirement']);
      }
        catch (\UnexpectedValueException $e) {
      catch (\UnexpectedValueException $exception) {
        throw new InfoParserException("The 'core_version_requirement' constraint ({$parsed_info['core_version_requirement']}) is not a valid value in $filename");
      }
        // If the 'core_version_requirement' constraint does not satisfy any
        // Drupal 8 versions before 8.7.7 then 'core' cannot be set or it will
        // effectively support all versions of Drupal 8 because
        // 'core_version_requirement' will be ignored in previous versions.
        if (!$supports_pre_core_version_requirement_version && isset($parsed_info['core'])) {
          throw new InfoParserException("The 'core_version_requirement' constraint ({$parsed_info['core_version_requirement']}) requires the 'core' key not be set in " . $filename);
        }
        // 'core_version_requirement' can not be used to specify Drupal 8
        // versions before 8.7.7 because these versions do not use the
        // 'core_version_requirement' key. Do not throw the exception if the
        // constraint also is satisfied by 8.0.0-alpha1 to allow constraints
        // such as '^8' or '^8 || ^9'.
        if ($supports_pre_core_version_requirement_version && !Semver::satisfies('8.0.0-alpha1', $parsed_info['core_version_requirement'])) {
          throw new InfoParserException("The 'core_version_requirement' can not be used to specify compatibility for a specific version before " . static::FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION . " in $filename");
        }
      }
      if (isset($parsed_info['core']) && $parsed_info['core'] !== '8.x') {
        throw new InfoParserException("'core: {$parsed_info['core']}' is not supported. Use 'core_version_requirement' to specify core compatibility. Only 'core: 8.x' is supported to provide backwards compatibility for Drupal 8 when needed in $filename");
      }

      // Determine if the extension is compatible with the current version of
      // Drupal core.
      $core_version_constraint = $parsed_info['core_version_requirement'] ?? $parsed_info['core'];
      $parsed_info['core_incompatible'] = !Semver::satisfies(\Drupal::VERSION, $core_version_constraint);
      if (isset($parsed_info['version']) && $parsed_info['version'] === 'VERSION') {
        $parsed_info['version'] = \Drupal::VERSION;
      }
@@ -140,77 +113,4 @@ protected function getRequiredKeys() {
    return ['type', 'name'];
  }

  /**
   * Determines if a constraint is satisfied by earlier versions of Drupal 8.
   *
   * @param string $constraint
   *   A core semantic version constraint.
   * @param string $version
   *   A core version.
   *
   * @return bool
   *   TRUE if the constraint is satisfied by a core version prior to the
   *   provided version.
   */
  protected static function isConstraintSatisfiedByPreviousVersion($constraint, $version) {
    static $evaluated_constraints = [];
    // Any particular constraint and version combination only needs to be
    // evaluated once.
    if (!isset($evaluated_constraints[$constraint][$version])) {
      $evaluated_constraints[$constraint][$version] = FALSE;
      foreach (static::getAllPreviousCoreVersions($version) as $previous_version) {
        if (Semver::satisfies($previous_version, $constraint)) {
          $evaluated_constraints[$constraint][$version] = TRUE;
          // The constraint only has to satisfy one previous version so break
          // when the first one is found.
          break;
        }
      }
    }
    return $evaluated_constraints[$constraint][$version];
  }

  /**
   * Gets all the versions of Drupal 8 before a specific version.
   *
   * @param string $version
   *   The version to get versions before.
   *
   * @return array
   *   All of the applicable Drupal 8 releases.
   */
  protected static function getAllPreviousCoreVersions($version) {
    static $versions_lists = [];
    // Check if list of previous versions for the specified version has already
    // been created.
    if (empty($versions_lists[$version])) {
      // Loop through all minor versions including 8.7.
      foreach (range(0, 7) as $minor) {
        // The largest patch number in a release was 17 in 8.6.17. Use 27 to
        // leave room for future security releases.
        foreach (range(0, 27) as $patch) {
          $patch_version = "8.$minor.$patch";
          if ($patch_version === $version) {
            // Reverse the order of the versions so that they will be evaluated
            // from the most recent versions first.
            $versions_lists[$version] = array_reverse($versions_lists[$version]);
            return $versions_lists[$version];
          }
          if ($patch === 0) {
            // If this is a '0' patch release like '8.1.0' first create the
            // pre-release versions such as '8.1.0-alpha1' and '8.1.0-rc1'.
            foreach (['alpha', 'beta', 'rc'] as $prerelease) {
              // The largest prerelease number was  in 8.0.0-beta16.
              foreach (range(0, 16) as $prerelease_number) {
                $versions_lists[$version][] = "$patch_version-$prerelease$prerelease_number";
              }
            }
          }
          $versions_lists[$version][] = $patch_version;
        }
      }
    }
    return $versions_lists[$version];
  }

}
+30 −65
Original line number Diff line number Diff line
@@ -89,48 +89,25 @@ public function testModulesListFormStatusMessage() {
  }

  /**
   * Tests the module form with modules with invalid info.yml files.
   * Tests the module form with a module with an invalid info.yml file.
   */
  public function testModulesListFormWithInvalidInfoFile() {
    $path = \Drupal::getContainer()->getParameter('site.path') . "/modules/broken";
    mkdir($path, 0777, TRUE);
    $file_path = "$path/broken.info.yml";

    $broken_infos = [
      [
        'yml' => <<<BROKEN
name: Module with no core_version_requirement or core
    $yml = <<<BROKEN
name: Module with no core_version_requirement
type: module
BROKEN,
        'expected_error' => "The 'core_version_requirement' key must be present in $file_path",
      ],
      [
        'yml' => <<<BROKEN
name: Module no core_version_requirement and invalid core
type: module
core: 9.x
BROKEN,
        'expected_error' => "'core: 9.x' is not supported. Use 'core_version_requirement' to specify core compatibility. Only 'core: 8.x' is supported to provide backwards compatibility for Drupal 8 when needed in $file_path",
      ],
      [
        'yml' => <<<BROKEN
name: Module with core_version_requirement and invalid core
type: module
core: 9.x
core_version_requirement: ^8 || ^9
BROKEN,
        'expected_error' => "'core: 9.x' is not supported. Use 'core_version_requirement' to specify core compatibility. Only 'core: 8.x' is supported to provide backwards compatibility for Drupal 8 when needed in $file_path",
      ],
    ];
BROKEN;

    foreach ($broken_infos as $broken_info) {
      file_put_contents($file_path, $broken_info['yml']);
    file_put_contents($file_path, $yml);

    $this->drupalGet('admin/modules');
    $this->assertSession()->statusCodeEquals(200);

    $this->assertSession()
        ->pageTextContains('Modules could not be listed due to an error: ' . $broken_info['expected_error']);
      ->pageTextContains("Modules could not be listed due to an error: The 'core_version_requirement' key must be present in $file_path");

    // Check that the module filter text box is available.
    $this->assertSession()->elementExists('xpath', '//input[@name="text"]');
@@ -143,7 +120,6 @@ public function testModulesListFormWithInvalidInfoFile() {
    $this->assertSession()->elementExists('xpath', '//input[@name="text"]');
    $this->assertSession()->pageTextNotContains('Modules could not be listed due to an error');
  }
  }

  /**
   * Confirm that module 'Required By' descriptions include dependent themes.
@@ -171,6 +147,7 @@ public function testInstalledIncompatibleModule() {
      'type' => 'module',
    ];
    $compatible_info = $info + ['core_version_requirement' => '*'];
    $incompatible_info = $info + ['core_version_requirement' => '^1'];

    file_put_contents($file_path, Yaml::encode($compatible_info));
    $edit = ['modules[changing_module][enable]' => 'changing_module'];
@@ -178,16 +155,6 @@ public function testInstalledIncompatibleModule() {
    $this->submitForm($edit, 'Install');
    $this->assertSession()->pageTextContains('Module Module that changes has been enabled.');

    $incompatible_updates = [
      [
        'core_version_requirement' => '^1',
      ],
      [
        'core' => '8.x',
      ],
    ];
    foreach ($incompatible_updates as $incompatible_update) {
      $incompatible_info = $info + $incompatible_update;
    file_put_contents($file_path, Yaml::encode($incompatible_info));
    $this->drupalGet('admin/modules');
    $this->assertSession()->pageTextContains($incompatible_modules_message);
@@ -195,7 +162,7 @@ public function testInstalledIncompatibleModule() {
    file_put_contents($file_path, Yaml::encode($compatible_info));
    $this->drupalGet('admin/modules');
    $this->assertSession()->pageTextNotContains($incompatible_modules_message);
    }

    // Uninstall the module and ensure that incompatible modules message is not
    // displayed for modules that are not installed.
    $edit = ['uninstall[changing_module]' => 'changing_module'];
@@ -203,12 +170,10 @@ public function testInstalledIncompatibleModule() {
    $this->submitForm($edit, 'Uninstall');
    $this->submitForm([], 'Uninstall');
    $this->assertSession()->pageTextContains('The selected modules have been uninstalled.');
    foreach ($incompatible_updates as $incompatible_update) {
      $incompatible_info = $info + $incompatible_update;

    file_put_contents($file_path, Yaml::encode($incompatible_info));
    $this->drupalGet('admin/modules');
    $this->assertSession()->pageTextNotContains($incompatible_modules_message);
  }
  }

}
+13 −24
Original line number Diff line number Diff line
@@ -342,6 +342,7 @@ public function testInstalledIncompatibleTheme() {
    ];

    $compatible_info = $info + ['core_version_requirement' => '*'];
    $incompatible_info = $info + ['core_version_requirement' => '^1'];

    file_put_contents($file_path, Yaml::encode($compatible_info));
    $this->drupalGet('admin/appearance');
@@ -350,16 +351,6 @@ public function testInstalledIncompatibleTheme() {
    $assert_session->addressEquals('admin/appearance');
    $assert_session->pageTextContains("The $theme_name theme has been installed");

    $incompatible_updates = [
      [
        'core_version_requirement' => '^1',
      ],
      [
        'core' => '8.x',
      ],
    ];
    foreach ($incompatible_updates as $incompatible_update) {
      $incompatible_info = $info + $incompatible_update;
    file_put_contents($file_path, Yaml::encode($incompatible_info));
    $this->drupalGet('admin/appearance');
    $this->assertSession()->pageTextContains($incompatible_themes_message);
@@ -367,16 +358,14 @@ public function testInstalledIncompatibleTheme() {
    file_put_contents($file_path, Yaml::encode($compatible_info));
    $this->drupalGet('admin/appearance');
    $this->assertSession()->pageTextNotContains($incompatible_themes_message);
    }

    // Uninstall the theme and ensure that incompatible themes message is not
    // displayed for themes that are not installed.
    $this->uninstallTheme($theme_name);
    foreach ($incompatible_updates as $incompatible_update) {
      $incompatible_info = $info + $incompatible_update;

    file_put_contents($file_path, Yaml::encode($incompatible_info));
    $this->drupalGet('admin/appearance');
    $this->assertSession()->pageTextNotContains($incompatible_themes_message);
  }
  }

}
+0 −22
Original line number Diff line number Diff line
@@ -325,28 +325,6 @@ public function providerExtensionCompatibilityChange() {
        ],
        'The following theme is installed, but it is incompatible with PHP ' . phpversion() . ":",
      ],
      'module: core_version_requirement key missing' => [
        [
          'core_version_requirement' => '>= 8',
          'type' => 'module',
        ],
        [
          'core' => '8.x',
          'type' => 'module',
        ],
        $incompatible_module_message,
      ],
      'theme: core_version_requirement key missing' => [
        [
          'core_version_requirement' => '>= 8',
          'type' => 'theme',
        ],
        [
          'core' => '8.x',
          'type' => 'theme',
        ],
        $incompatible_theme_message,
      ],
    ];
  }

+20 −341

File changed.

Preview size limit exceeded, changes collapsed.