Commit f0666e7b authored by catch's avatar catch
Browse files

fix: #3572527 \Drupal\DrupalInstalled::VERSIONS_HASH is not consistent for the same set of packages

By: alexpott
By: chr.fritsch
(cherry picked from commit f905d6de)
(cherry picked from commit 9d6f56e4)
parent c7129627
Loading
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -24,8 +24,14 @@ class DrupalInstalledTemplate {
   *   The PHP code to write to the DrupalInstalled class.
   */
  public static function getCode(PackageInterface $root_package, InstalledRepositoryInterface $repository): string {
    // Ensure the packages are sorted consistently.
    $packages = $repository->getPackages();
    usort($packages, static function (PackageInterface $a, PackageInterface $b) {
      return $a->getUniqueName() <=> $b->getUniqueName();
    });

    // Write out a hash of the version information to a file so we can use it.
    $versions = array_reduce($repository->getPackages(), fn (string $carry, PackageInterface $package) => $carry . $package->getUniqueName() . '-' . $package->getSourceReference() . '|', '');
    $versions = array_reduce($packages, fn (string $carry, PackageInterface $package) => $carry . $package->getUniqueName() . '-' . $package->getSourceReference() . '|', '');
    // Add the root_package package version info so custom code changes and
    // root_package package version changes result in the hash changing.
    $versions .= $root_package->getUniqueName() . '-' . $root_package->getSourceReference();
+86 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\Composer\Plugin\Scaffold\Functional;

use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\Tests\Composer\Plugin\ExecTrait;
use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
use PHPUnit\Framework\Attributes\Group;

/**
 * Tests DrupalInstalled.php hash changes when scaffolding is run.
 */
#[Group('Scaffold')]
#[Group('#slow')]
class DrupalInstalledTest extends BuildTestBase {
  use ExecTrait;

  /**
   * Directory to perform the tests in.
   *
   * @var string
   */
  protected $fixturesDir;

  /**
   * The Fixtures object.
   *
   * @var \Drupal\Tests\Composer\Plugin\Scaffold\Fixtures
   */
  protected $fixtures;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->fixtures = new Fixtures();
    $this->fixtures->createIsolatedComposerCacheDir();
    $this->fixturesDir = $this->fixtures->tmpDir($this->name());
    $replacements = ['SYMLINK' => 'false', 'PROJECT_ROOT' => $this->fixtures->projectRoot()];
    $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements);
  }

  /**
   * {@inheritdoc}
   */
  protected function tearDown(): void {
    // Remove any temporary directories et. al. that were created.
    $this->fixtures->tearDown();

    parent::tearDown();
  }

  /**
   * Tests DrupalInstalled.php hash changes when scaffolding is run.
   */
  public function testDrupalInstalledHash(): void {
    $topLevelProjectDir = 'drupal-installed-fixture';
    $sut = $this->fixturesDir . '/' . $topLevelProjectDir;

    $this->mustExec("composer install --no-ansi", $sut);
    $original_version_hash = sha1_file($sut . '/vendor/drupal/DrupalInstalled.php');

    // Require two fixtures and ensure that the DrupalInstalled.php file is
    // updated.
    $this->mustExec("composer require --no-ansi --no-interaction fixtures/empty-file:dev-main fixtures/scaffold-override-fixture:dev-main", $sut);
    $two_fixtures_hash = sha1_file($sut . '/vendor/drupal/DrupalInstalled.php');
    $this->assertNotEquals($original_version_hash, $two_fixtures_hash);

    // Remove one fixture and ensure the hash is not equal to the original or
    // the hash with two fixtures.
    $this->mustExec("composer remove --no-ansi --no-interaction fixtures/empty-file", $sut);
    $one_fixture_hash = sha1_file($sut . '/vendor/drupal/DrupalInstalled.php');
    $this->assertNotEquals($original_version_hash, $one_fixture_hash);
    $this->assertNotEquals($two_fixtures_hash, $one_fixture_hash);

    // Add the fixture back and ensure the hash is changed and equal to the
    // previous hash for two fixtures.
    $this->mustExec("composer require --no-ansi --no-interaction fixtures/empty-file:dev-main", $sut);
    $this->assertEquals($two_fixtures_hash, sha1_file($sut . '/vendor/drupal/DrupalInstalled.php'));
  }

}
+51 −0
Original line number Diff line number Diff line
{
  "name": "fixtures/drupal-drupal",
  "type": "project",
  "minimum-stability": "dev",
  "prefer-stable": true,
  "repositories": {
    "packagist.org": false,
    "composer-scaffold": {
      "type": "path",
      "url": "__PROJECT_ROOT__",
      "options": {
        "symlink": true
      }
    },
    "empty-file": {
      "type": "path",
      "url": "../empty-file",
      "options": {
        "symlink": true
      }
    },
    "scaffold-override-fixture": {
      "type": "path",
      "url": "../scaffold-override-fixture",
      "options": {
        "symlink": true
      }
    }
  },
  "require": {
    "drupal/core-composer-scaffold": "*"
  },
  "extra": {
    "installer-paths": {
      "core": ["type:drupal-core"],
      "modules/contrib/{$name}": ["type:drupal-module"],
      "modules/custom/{$name}": ["type:drupal-custom-module"],
      "profiles/contrib/{$name}": ["type:drupal-profile"],
      "profiles/custom/{$name}": ["type:drupal-custom-profile"],
      "themes/contrib/{$name}": ["type:drupal-theme"],
      "themes/custom/{$name}": ["type:drupal-custom-theme"],
      "libraries/{$name}": ["type:drupal-library"],
      "drush/Commands/contrib/{$name}": ["type:drupal-drush"]
    }
  },
  "config": {
     "allow-plugins": {
       "drupal/core-composer-scaffold": true
     }
  }
}