Skip to content
Snippets Groups Projects

Resolve #3408488 "Make unknownpathexcluder accept"

All threads resolved!
Compare and Show latest version
1 file
+ 42
29
Compare changes
  • Side-by-side
  • Inline
@@ -24,6 +24,12 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
*
* If web root and project root are the same, nothing is excluded.
*
* This excluder can be bypassed by changing the config setting
* `package_manager.settings:include_unknown_files` to TRUE. This may be needed
* for sites that have files outside the web root which are nonetheless needed
* in order for Composer to assemble the code base correctly; a classic example
* would be a directory of patch files used by `cweagans/composer-patches`.
*
* @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
@@ -70,57 +76,65 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
return;
}
$project_root = $this->pathLocator->getProjectRoot();
$web_root = $project_root . DIRECTORY_SEPARATOR . $this->pathLocator->getWebRoot();
if (realpath($web_root) === $project_root) {
// If the project root and web root are the same, there's nothing to do.
$web_root = $this->pathLocator->getWebRoot();
if (empty($web_root)) {
return;
}
// To determine the scaffold files to exclude, the installed packages must
// be known, and that requires Composer commands to be able to run. This
// intentionally does not catch exceptions: failed Composer validation in
// the project root implies that this excluder cannot function correctly.
// Note: the call to ComposerInspector::getInstalledPackagesList() would
// To determine the files to include, the installed packages must be known,
// and that requires Composer commands to be able to run. This intentionally
// does not catch exceptions: failed Composer validation in the project root
// implies that this excluder cannot function correctly.
// Note: the call to ComposerInspector::getConfig() would
// also have triggered this, but explicitness is preferred here.
// @see \Drupal\package_manager\StatusCheckTrait::runStatusCheck()
$project_root = $this->pathLocator->getProjectRoot();
$this->composerInspector->validate($project_root);
$vendor_dir = $this->pathLocator->getVendorDirectory();
$scaffold_files_paths = $this->getScaffoldFiles();
// The vendor directory and web root are always included in staging
// operations, along with `composer.json`, `composer.lock`, and any scaffold
// files provided by Drupal core.
$always_include = [
$this->composerInspector->getConfig('vendor-dir', $project_root),
$web_root,
'composer.json',
'composer.lock',
];
foreach ($this->getScaffoldFiles() as $scaffold_file_path) {
// The web root is always included in staging operations, so we don't need
// to do anything special for scaffold files that live in it.
if (str_starts_with($scaffold_file_path, '[web-root]')) {
continue;
}
$always_include[] = ltrim($scaffold_file_path, '/');
}
// Search for all files (including hidden ones) in the project root. We need
// to use readdir() and friends here, rather than glob(), since certain
// glob() flags aren't supported on all systems. We also can't use
// \Drupal\Core\File\FileSystemInterface::scanDirectory(), because it
// unconditionally ignores hidden files and directories.
$paths_in_project_root = [];
$handle = opendir($project_root);
if (empty($handle)) {
throw new \RuntimeException("Could not scan for files in the project root.");
}
while (($entry = readdir($handle)) !== FALSE) {
if ($entry === '.' || $entry === '..') {
while ($entry = readdir($handle)) {
if ($entry === '.' || $entry === '..' || in_array($entry, $always_include, TRUE)) {
continue;
}
$paths_in_project_root[] = $project_root . DIRECTORY_SEPARATOR . $entry;
// We can add the path as-is; it's already relative to the project root.
$event->add($entry);
}
closedir($handle);
$paths = [];
$known_paths = array_merge([$vendor_dir, $web_root, "$project_root/composer.json", "$project_root/composer.lock"], $scaffold_files_paths);
foreach ($paths_in_project_root as $path_in_project_root) {
if (!in_array($path_in_project_root, $known_paths, TRUE)) {
$paths[] = $path_in_project_root;
}
}
$event->addPathsRelativeToProjectRoot($paths);
}
/**
* Gets the path of scaffold files, for example 'index.php' and 'robots.txt'.
*
* @return array
* The array of scaffold file paths.
* @return string[]
* The paths of scaffold files provided by `drupal/core`, relative to the
* project root.
*
* @todo Intelligently load scaffold files in https://drupal.org/i/3343802.
*/
@@ -128,10 +142,9 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
$project_root = $this->pathLocator->getProjectRoot();
$packages = $this->composerInspector->getInstalledPackagesList($project_root);
$extra = Json::decode($this->composerInspector->getConfig('extra', $packages['drupal/core']->path . '/composer.json'));
if (isset($extra['drupal-scaffold']['file-mapping'])) {
return array_keys($extra['drupal-scaffold']['file-mapping']);
}
return [];
$scaffold_files = array_keys($extra['drupal-scaffold']['file-mapping'] ?? []);
return str_replace('[project-root]', '', $scaffold_files);
}
}
Loading