Verified Commit 892b90d0 authored by godotislate's avatar godotislate
Browse files

fix: #3582229 Package Manager blows up when it encounters already-unpacked recipes

By: juc1
By: phenaproxima
By: catch
By: godotislate
By: alexpott
(cherry picked from commit 607b8329)
parent 4845e672
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
parameters:
  package_manager.skip_procedural_hook_scan: true
  package_manager.allow_overwrite: ['|^/recipes/.|']

services:
  _defaults:
+15 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
@@ -22,6 +23,12 @@
 * https://packages.drupal.org/8 currently uses the `metapackage` type for
 * submodules of Drupal projects.
 *
 * This validator can accept an optional list of regular expressions matching
 * paths which are allowed to be overwritten. This is most useful when a recipe
 * is installed, since recipes are normally removed from Composer's awareness by
 * the drupal/core-recipe-unpack plugin and can therefore run afoul of this
 * validator.
 *
 * @internal
 *   This is an internal part of Package Manager and may be changed or removed
 *   at any time without warning. External code should not interact with this
@@ -36,6 +43,8 @@ final class OverwriteExistingPackagesValidator implements EventSubscriberInterfa
  public function __construct(
    private readonly PathLocator $pathLocator,
    private readonly ComposerInspector $composerInspector,
    #[Autowire(param: 'package_manager.allow_overwrite')]
    private readonly array $allowOverwrite = [],
  ) {}

  /**
@@ -57,6 +66,12 @@ public function validate(PreApplyEvent $event): void {
        continue;
      }
      $relative_path = str_replace($stage_dir, '', $package->path);

      foreach ($this->allowOverwrite as $pattern) {
        if (preg_match($pattern, $relative_path)) {
          continue 2;
        }
      }
      if (is_dir($active_dir . DIRECTORY_SEPARATOR . $relative_path)) {
        $event->addError([
          $this->t('The new package @package will be installed in the directory @path, which already exists but is not managed by Composer.', [
+29 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

namespace Drupal\Tests\package_manager\Kernel;

use Drupal\Core\Recipe\Recipe;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\fixture_manipulator\ActiveFixtureManipulator;
use Drupal\package_manager\ComposerInspector;
@@ -165,4 +166,32 @@ public function testNewPackagesOverwriteExisting(): void {
    $this->assertResults($expected_results, PreApplyEvent::class);
  }

  /**
   * Tests that things in the `recipes` directory can be overwritten.
   */
  public function testRecipeOverwriteIsAllowed(): void {
    (new ActiveFixtureManipulator())
      ->addProjectAtPath('recipes/test_recipe', file_name: 'recipe.yml')
      ->commitChanges();
    $stage_manipulator = $this->getStageFixtureManipulator();

    // This should not raise an error because, even though it's going to
    // overwrite an existing directory, it's at a path which specifically allows
    // that.
    $stage_manipulator->addPackage(
      [
        'name' => 'drupal/test_recipe',
        'version' => '1.0.0',
        'type' => Recipe::COMPOSER_PROJECT_TYPE,
      ],
      FALSE,
      TRUE
    );
    $installer_paths = [
      'recipes/test_recipe' => ['drupal/test_recipe'],
    ];
    $this->setInstallerPaths($installer_paths, $this->container->get(PathLocator::class)->getProjectRoot());
    $this->assertResults([], PreApplyEvent::class);
  }

}