Skip to content
Snippets Groups Projects
Commit 4646e6fc authored by Adam G-H's avatar Adam G-H
Browse files

Issue #3261847 by phenaproxima, tedbow: Add helpful methods to compute the...

Issue #3261847 by phenaproxima, tedbow: Add helpful methods to compute the difference between two ComposerUtility objects
parent 00796f61
No related branches found
No related tags found
1 merge request!192Issue #3261847: Add helpful methods to compute the difference between two ComposerUtility objects
...@@ -6,6 +6,7 @@ use Composer\Composer; ...@@ -6,6 +6,7 @@ use Composer\Composer;
use Composer\Factory; use Composer\Factory;
use Composer\IO\NullIO; use Composer\IO\NullIO;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Semver\Comparator;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
/** /**
...@@ -133,35 +134,13 @@ class ComposerUtility { ...@@ -133,35 +134,13 @@ class ComposerUtility {
return array_values($core_packages); return array_values($core_packages);
} }
/**
* Returns all Drupal extension packages in the lock file.
*
* The following package types are considered Drupal extension packages:
* drupal-module, drupal-theme, drupal-custom-module, and drupal-custom-theme.
*
* @return \Composer\Package\PackageInterface[]
* All Drupal extension packages in the lock file, keyed by name.
*/
public function getDrupalExtensionPackages(): array {
$filter = function (PackageInterface $package): bool {
$drupal_package_types = [
'drupal-module',
'drupal-theme',
'drupal-custom-module',
'drupal-custom-theme',
];
return in_array($package->getType(), $drupal_package_types, TRUE);
};
return array_filter($this->getInstalledPackages(), $filter);
}
/** /**
* Returns information on all installed packages. * Returns information on all installed packages.
* *
* @return \Composer\Package\PackageInterface[] * @return \Composer\Package\PackageInterface[]
* All installed packages, keyed by name. * All installed packages, keyed by name.
*/ */
protected function getInstalledPackages(): array { public function getInstalledPackages(): array {
$installed_packages = $this->getComposer() $installed_packages = $this->getComposer()
->getRepositoryManager() ->getRepositoryManager()
->getLocalRepository() ->getLocalRepository()
...@@ -175,4 +154,43 @@ class ComposerUtility { ...@@ -175,4 +154,43 @@ class ComposerUtility {
return $packages; return $packages;
} }
/**
* Returns the packages that are in the current project, but not in another.
*
* @param self $other
* A Composer utility wrapper around a different directory.
*
* @return \Composer\Package\PackageInterface[]
* The packages which are installed in the current project, but not in the
* other one, keyed by name.
*/
public function getPackagesNotIn(self $other): array {
return array_diff_key($this->getInstalledPackages(), $other->getInstalledPackages());
}
/**
* Returns the packages which have a different version in another project.
*
* This compares the current project with another one, and returns packages
* which are present in both, but in different versions.
*
* @param self $other
* A Composer utility wrapper around a different directory.
*
* @return \Composer\Package\PackageInterface[]
* The packages which are present in both the current project and the other
* one, but in different versions, keyed by name.
*/
public function getPackagesWithDifferentVersionsIn(self $other): array {
$theirs = $other->getInstalledPackages();
// Only compare packages that are both here and there.
$packages = array_intersect_key($this->getInstalledPackages(), $theirs);
$filter = function (PackageInterface $package, string $name) use ($theirs): bool {
return Comparator::notEqualTo($package->getVersion(), $theirs[$name]->getVersion());
};
return array_filter($packages, $filter, ARRAY_FILTER_USE_BOTH);
}
} }
{}
{
"packages": [
{
"name": "drupal/existing",
"version": "1.0.0"
},
{
"name": "drupal/updated",
"version": "1.0.0"
},
{
"name": "drupal/removed",
"version": "1.0.0"
}
]
}
{}
{
"packages": [
{
"name": "drupal/existing",
"version": "1.0.0"
},
{
"name": "drupal/updated",
"version": "1.1.0"
},
{
"name": "drupal/added",
"version": "1.0.0"
}
]
}
...@@ -72,4 +72,23 @@ class ComposerUtilityTest extends KernelTestBase { ...@@ -72,4 +72,23 @@ class ComposerUtilityTest extends KernelTestBase {
$this->assertSame($expected_packages, $packages); $this->assertSame($expected_packages, $packages);
} }
/**
* @covers ::getPackagesNotIn
* @covers ::getPackagesWithDifferentVersionsIn
*/
public function testPackageComparison(): void {
$fixture_dir = __DIR__ . '/../../fixtures/packages_comparison';
$active = ComposerUtility::createForDirectory($fixture_dir . '/active');
$staged = ComposerUtility::createForDirectory($fixture_dir . '/stage');
$added = $staged->getPackagesNotIn($active);
$this->assertSame(['drupal/added'], array_keys($added));
$removed = $active->getPackagesNotIn($staged);
$this->assertSame(['drupal/removed'], array_keys($removed));
$updated = $active->getPackagesWithDifferentVersionsIn($staged);
$this->assertSame(['drupal/updated'], array_keys($updated));
}
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\automatic_updates\Validator; namespace Drupal\automatic_updates\Validator;
use Composer\Package\PackageInterface;
use Drupal\automatic_updates\Updater; use Drupal\automatic_updates\Updater;
use Drupal\package_manager\Event\PreApplyEvent; use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
...@@ -39,8 +40,8 @@ final class StagedProjectsValidator implements EventSubscriberInterface { ...@@ -39,8 +40,8 @@ final class StagedProjectsValidator implements EventSubscriberInterface {
} }
try { try {
$active_packages = $stage->getActiveComposer()->getDrupalExtensionPackages(); $active = $stage->getActiveComposer();
$staged_packages = $stage->getStageComposer()->getDrupalExtensionPackages(); $stage = $stage->getStageComposer();
} }
catch (\Throwable $e) { catch (\Throwable $e) {
$event->addError([ $event->addError([
...@@ -55,8 +56,15 @@ final class StagedProjectsValidator implements EventSubscriberInterface { ...@@ -55,8 +56,15 @@ final class StagedProjectsValidator implements EventSubscriberInterface {
'drupal-theme' => $this->t('theme'), 'drupal-theme' => $this->t('theme'),
'drupal-custom-theme' => $this->t('custom theme'), 'drupal-custom-theme' => $this->t('custom theme'),
]; ];
$filter = function (PackageInterface $package) use ($type_map): bool {
return array_key_exists($package->getType(), $type_map);
};
$new_packages = $stage->getPackagesNotIn($active);
$removed_packages = $active->getPackagesNotIn($stage);
$updated_packages = $active->getPackagesWithDifferentVersionsIn($stage);
// Check if any new Drupal projects were installed. // Check if any new Drupal projects were installed.
if ($new_packages = array_diff_key($staged_packages, $active_packages)) { if ($new_packages = array_filter($new_packages, $filter)) {
$new_packages_messages = []; $new_packages_messages = [];
foreach ($new_packages as $new_package) { foreach ($new_packages as $new_package) {
...@@ -77,7 +85,7 @@ final class StagedProjectsValidator implements EventSubscriberInterface { ...@@ -77,7 +85,7 @@ final class StagedProjectsValidator implements EventSubscriberInterface {
} }
// Check if any Drupal projects were removed. // Check if any Drupal projects were removed.
if ($removed_packages = array_diff_key($active_packages, $staged_packages)) { if ($removed_packages = array_filter($removed_packages, $filter)) {
$removed_packages_messages = []; $removed_packages_messages = [];
foreach ($removed_packages as $removed_package) { foreach ($removed_packages as $removed_package) {
$removed_packages_messages[] = $this->t( $removed_packages_messages[] = $this->t(
...@@ -96,22 +104,21 @@ final class StagedProjectsValidator implements EventSubscriberInterface { ...@@ -96,22 +104,21 @@ final class StagedProjectsValidator implements EventSubscriberInterface {
$event->addError($removed_packages_messages, $removed_packages_summary); $event->addError($removed_packages_messages, $removed_packages_summary);
} }
// Get all the packages that are neither newly installed or removed to // Check if any Drupal projects were neither installed or removed, but had
// check if their version numbers changed. // their version numbers changed.
if ($pre_existing_packages = array_diff_key($staged_packages, $removed_packages, $new_packages)) { if ($updated_packages = array_filter($updated_packages, $filter)) {
foreach ($pre_existing_packages as $package_name => $staged_existing_package) { $staged_packages = $stage->getInstalledPackages();
$active_package = $active_packages[$package_name];
if ($staged_existing_package->getVersion() !== $active_package->getVersion()) { foreach ($updated_packages as $name => $updated_package) {
$version_change_messages[] = $this->t( $version_change_messages[] = $this->t(
"@type '@name' from @active_version to @staged_version.", "@type '@name' from @active_version to @staged_version.",
[ [
'@type' => $type_map[$active_package->getType()], '@type' => $type_map[$updated_package->getType()],
'@name' => $active_package->getName(), '@name' => $updated_package->getName(),
'@staged_version' => $staged_existing_package->getPrettyVersion(), '@staged_version' => $staged_packages[$name]->getPrettyVersion(),
'@active_version' => $active_package->getPrettyVersion(), '@active_version' => $updated_package->getPrettyVersion(),
] ]
); );
}
} }
if (!empty($version_change_messages)) { if (!empty($version_change_messages)) {
$version_change_summary = $this->formatPlural( $version_change_summary = $this->formatPlural(
......
...@@ -199,8 +199,8 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase { ...@@ -199,8 +199,8 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase {
"$fixtures_folder/version_changed", "$fixtures_folder/version_changed",
'The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.', 'The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.',
[ [
"module 'drupal/test_module' from 1.3.0 to 1.3.1.", "module 'drupal/test_module' from 1.3.0 to 1.3.1.",
"module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.", "module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.",
], ],
], ],
]; ];
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment