Unverified Commit 9a82f42a authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3082866 by Mile23, Mixologic, greg.1.anderson, alexpott, rfay: composer...

Issue #3082866 by Mile23, Mixologic, greg.1.anderson, alexpott, rfay: composer complains "Skipped installation of bin bin/composer for package composer/composer: file not found in package"

(cherry picked from commit dd271560)

Issue #3082866 by Mile23, Mixologic, greg.1.anderson, alexpott, rfay: composer complains "Skipped installation of bin bin/composer for package composer/composer: file not found in package"
parent de565ca0
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
    "require": {
        "composer/installers": "^1.0.24",
        "drupal/core": "self.version",
        "drupal/core-vendor-hardening": "self.version",
        "wikimedia/composer-merge-plugin": "^1.4"
    },
    "require-dev": {
@@ -72,9 +73,6 @@
        "pre-install-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion",
        "pre-update-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion",
        "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
        "post-autoload-dump": "Drupal\\Core\\Composer\\Composer::ensureHtaccess",
        "post-package-install": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup",
        "post-package-update": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup",
        "phpcs": "phpcs --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --",
        "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --"
    },
@@ -86,6 +84,10 @@
        {
            "type": "path",
            "url": "core"
        },
        {
            "type": "path",
            "url": "composer/Plugin/VendorHardening"
        }
    ]
}
+32 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "bc036f74a7ef48097e813b50a2d5aea8",
    "content-hash": "63b940ec40ef24930a101dfce6eed82a",
    "packages": [
        {
            "name": "asm89/stack-cors",
@@ -911,6 +911,36 @@
            ],
            "description": "Drupal is an open source content management platform powering millions of websites and applications."
        },
        {
            "name": "drupal/core-vendor-hardening",
            "version": "8.8.x-dev",
            "dist": {
                "type": "path",
                "url": "composer/Plugin/VendorHardening",
                "reference": "2db54f089065dedbe4a040b01f7b527f2bad68f6"
            },
            "require": {
                "composer-plugin-api": "^1.1",
                "php": ">=7.0.8"
            },
            "type": "composer-plugin",
            "extra": {
                "class": "Drupal\\Composer\\Plugin\\VendorHardening\\VendorHardeningPlugin"
            },
            "autoload": {
                "psr-4": {
                    "Drupal\\Composer\\Plugin\\VendorHardening\\": "."
                }
            },
            "license": [
                "GPL-2.0-or-later"
            ],
            "description": "Hardens the vendor directory for when it's in the docroot.",
            "homepage": "https://www.drupal.org/project/drupal",
            "keywords": [
                "drupal"
            ]
        },
        {
            "name": "easyrdf/easyrdf",
            "version": "0.9.1",
@@ -6229,6 +6259,7 @@
    "minimum-stability": "dev",
    "stability-flags": {
        "drupal/core": 20,
        "drupal/core-vendor-hardening": 20,
        "behat/mink": 20,
        "behat/mink-selenium2-driver": 20
    },
+105 −2
Original line number Diff line number Diff line
@@ -5,12 +5,13 @@
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Package\CompletePackage;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Util\Filesystem;
use Composer\Script\Event;
use Composer\Installer\PackageEvents;

/**
 * A Composer plugin to clean out your project's vendor directory.
@@ -70,6 +71,8 @@ public static function getSubscribedEvents() {
      ScriptEvents::POST_AUTOLOAD_DUMP => 'onPostAutoloadDump',
      ScriptEvents::POST_UPDATE_CMD => 'onPostCmd',
      ScriptEvents::POST_INSTALL_CMD => 'onPostCmd',
      PackageEvents::PRE_PACKAGE_INSTALL => 'onPrePackageInstall',
      PackageEvents::PRE_PACKAGE_UPDATE => 'onPrePackageUpdate',
      PackageEvents::POST_PACKAGE_INSTALL => 'onPostPackageInstall',
      PackageEvents::POST_PACKAGE_UPDATE => 'onPostPackageUpdate',
    ];
@@ -95,6 +98,28 @@ public function onPostCmd(Event $event) {
    $this->cleanAllPackages($this->composer->getConfig()->get('vendor-dir'));
  }

  /**
   * PRE_PACKAGE_INSTALL event handler.
   *
   * @param \Composer\Installer\PackageEvent $event
   */
  public function onPrePackageInstall(PackageEvent $event) {
    /** @var \Composer\Package\CompletePackage $package */
    $package = $event->getOperation()->getPackage();
    $this->removeBinBeforeCleanup($package);
  }

  /**
   * PRE_PACKAGE_UPDATE event handler.
   *
   * @param \Composer\Installer\PackageEvent $event
   */
  public function onPrePackageUpdate(PackageEvent $event) {
    /** @var \Composer\Package\CompletePackage $package */
    $package = $event->getOperation()->getTargetPackage();
    $this->removeBinBeforeCleanup($package);
  }

  /**
   * POST_PACKAGE_INSTALL event handler.
   *
@@ -119,6 +144,84 @@ public function onPostPackageUpdate(PackageEvent $event) {
    $this->cleanPackage($this->composer->getConfig()->get('vendor-dir'), $package_name);
  }

  /**
   * Remove bin config for packages that would have the bin file removed.
   *
   * Where the configured bin files are in the directories to be removed, remove
   * the bin config.
   *
   * @param \Composer\Package\CompletePackage $package
   *   The package we're cleaning up.
   */
  protected function removeBinBeforeCleanup(CompletePackage $package) {
    // Only do this if there are binaries and cleanup paths.
    $binaries = $package->getBinaries();
    $clean_paths = $this->config->getPathsForPackage($package->getName());
    if (!$binaries || !$clean_paths) {
      return;
    }
    if ($unset_these_binaries = $this->findBinOverlap($binaries, $clean_paths)) {
      $this->io->writeError(
        sprintf('%sModifying bin config for <info>%s</info> which overlaps with cleanup directories.', str_repeat(' ', 4), $package->getName()),
        TRUE,
        IOInterface::VERBOSE
      );
      $modified_binaries = [];
      foreach ($binaries as $binary) {
        if (!in_array($binary, $unset_these_binaries)) {
          $modified_binaries[] = $binary;
        }
      }
      $package->setBinaries($modified_binaries);
    }
  }

  /**
   * Find bin files which are inside cleanup directories.
   *
   * @param string[] $binaries
   *   'Bin' configuration from the package we're cleaning up.
   * @param string[] $clean_paths
   *   The paths we're cleaning up.
   *
   * @return string[]
   *   Bin files to remove, with the file as both the key and the value.
   */
  protected function findBinOverlap($binaries, $clean_paths) {
    // Make a filesystem model to explore. This is a keyed array that looks like
    // all the places that will be removed by cleanup. 'tests/src' becomes
    // $filesystem['tests']['src'] = TRUE;
    $filesystem = [];
    foreach ($clean_paths as $clean_path) {
      $clean_pieces = explode("/", $clean_path);
      $current = &$filesystem;
      foreach ($clean_pieces as $clean_piece) {
        $current = &$current[$clean_piece];
      }
      $current = TRUE;
    }
    // Explore the filesystem with our bin config.
    $unset_these_binaries = [];
    foreach ($binaries as $binary) {
      $binary_pieces = explode('/', $binary);
      $current = &$filesystem;
      foreach ($binary_pieces as $binary_piece) {
        if (!isset($current[$binary_piece])) {
          break;
        }
        else {
          // Value of TRUE means we're at the end of the path.
          if ($current[$binary_piece] === TRUE) {
            $unset_these_binaries[$binary] = $binary;
            break;
          }
        }
        $current = &$filesystem[$binary_piece];
      }
    }
    return $unset_these_binaries;
  }

  /**
   * Gets a list of all installed packages from Composer.
   *
+63 −0
Original line number Diff line number Diff line
@@ -122,6 +122,9 @@ public function testCleanAllPackages() {
    $this->assertFileNotExists(vfsStream::url('vendor/drupal/package/tests'));
  }

  /**
   * @covers ::writeAccessRestrictionFiles
   */
  public function testWriteAccessRestrictionFiles() {
    $dir = vfsStream::url('vendor');

@@ -148,4 +151,64 @@ public function testWriteAccessRestrictionFiles() {
    $this->assertFileExists($dir . '/web.config');
  }

  public function providerFindBinOverlap() {
    return [
      [
        [],
        ['bin/script'],
        ['tests'],
      ],
      [
        ['bin/composer' => 'bin/composer'],
        ['bin/composer'],
        ['bin', 'tests'],
      ],
      [
        ['bin/composer' => 'bin/composer'],
        ['bin/composer'],
        ['bin/composer'],
      ],
      [
        [],
        ['bin/composer'],
        ['bin/something_else'],
      ],
      [
        [],
        ['test/script'],
        ['test/longer'],
      ],
      [
        ['bin/very/long/path/script' => 'bin/very/long/path/script'],
        ['bin/very/long/path/script'],
        ['bin'],
      ],
      [
        ['bin/bin/bin' => 'bin/bin/bin'],
        ['bin/bin/bin'],
        ['bin/bin'],
      ],
      [
        [],
        ['bin/bin'],
        ['bin/bin/bin'],
      ],
    ];
  }

  /**
   * @covers ::findBinOverlap
   * @dataProvider providerFindBinOverlap
   */
  public function testFindBinOverlap($expected, $binaries, $clean_paths) {
    $plugin = $this->getMockBuilder(VendorHardeningPlugin::class)
      ->disableOriginalConstructor()
      ->getMock();

    $ref_find_bin_overlap = new \ReflectionMethod($plugin, 'findBinOverlap');
    $ref_find_bin_overlap->setAccessible(TRUE);

    $this->assertSame($expected, $ref_find_bin_overlap->invokeArgs($plugin, [$binaries, $clean_paths]));
  }

}