From 8ea1e16f933965c93e6083d47a68ad2931b58334 Mon Sep 17 00:00:00 2001 From: Rob Bayliss Date: Tue, 28 Nov 2017 18:22:37 -0500 Subject: [PATCH 1/3] Support passing an associative array of files to includes --- src/FileFetcher.php | 5 +++-- src/PrestissimoFileFetcher.php | 5 +++-- tests/FetcherTest.php | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/FileFetcher.php b/src/FileFetcher.php index 4435306..78e6011 100644 --- a/src/FileFetcher.php +++ b/src/FileFetcher.php @@ -29,8 +29,9 @@ public function __construct(RemoteFilesystem $remoteFilesystem, $source, $filena } public function fetch($version, $destination) { - array_walk($this->filenames, function ($filename) use ($version, $destination) { - $url = $this->getUri($filename, $version); + array_walk($this->filenames, function ($filename, $sourceFilename) use ($version, $destination) { + $sourceFilename = is_numeric($sourceFilename) ? $filename : $sourceFilename; + $url = $this->getUri($sourceFilename, $version); $this->fs->ensureDirectoryExists($destination . '/' . dirname($filename)); $this->remoteFilesystem->copy($url, $url, $destination . '/' . $filename); }); diff --git a/src/PrestissimoFileFetcher.php b/src/PrestissimoFileFetcher.php index a99aa1d..9ff97d4 100644 --- a/src/PrestissimoFileFetcher.php +++ b/src/PrestissimoFileFetcher.php @@ -40,8 +40,9 @@ public function fetch($version, $destination) { protected function fetchWithPrestissimo($version, $destination) { $requests = []; - array_walk($this->filenames, function ($filename) use ($version, $destination, &$requests) { - $url = $this->getUri($filename, $version); + array_walk($this->filenames, function ($filename, $sourceFilename) use ($version, $destination, &$requests) { + $sourceFilename = is_numeric($sourceFilename) ? $filename : $sourceFilename; + $url = $this->getUri($sourceFilename, $version); $this->fs->ensureDirectoryExists($destination . '/' . dirname($filename)); $requests[] = new CopyRequest($url, $destination . '/' . $filename, false, $this->io, $this->config); }); diff --git a/tests/FetcherTest.php b/tests/FetcherTest.php index 39b3e73..64c8acb 100644 --- a/tests/FetcherTest.php +++ b/tests/FetcherTest.php @@ -91,6 +91,12 @@ public function testFetchVersionSpecific() { $this->assertFileNotExists($this->tmpDir . '/.eslintrc'); } + public function testFetchesToSpecificDestination() { + $fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}', ['sites/example.settings.local.php' => 'sites/default/example.settings.local.php']); + $fetcher->fetch('8.2.x', $this->tmpDir); + $this->assertFileExists($this->tmpDir .'/sites/default/example.settings.local.php'); + } + public function testInitialFetch() { $fetcher = new InitialFileFetcher(new RemoteFilesystem(new NullIO()), 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}', ['sites/default/default.settings.php' => 'sites/default/settings.php']); $fetcher->fetch('8.1.1', $this->tmpDir); From ce0bca868e7b98287ffb58b9619bd0a15dbee69b Mon Sep 17 00:00:00 2001 From: Rob Bayliss Date: Wed, 29 Nov 2017 08:37:11 -0500 Subject: [PATCH 2/3] Allow keyed includes to be considered for exclusion based on their source path --- src/Handler.php | 18 +++- tests/HandlerTest.php | 234 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 tests/HandlerTest.php diff --git a/src/Handler.php b/src/Handler.php index b6ca659..dce0389 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -103,7 +103,7 @@ public function downloadScaffold() { // Collect options, excludes and settings files. $options = $this->getOptions(); - $files = array_diff($this->getIncludes(), $this->getExcludes()); + $files = $this->getFiles(); // Call any pre-scaffold scripts that may be defined. $dispatcher = new EventDispatcher($this->composer, $this->io); @@ -240,6 +240,10 @@ protected function getPackage($name) { return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*'); } + protected function getFiles() { + return array_diff_key($this->getIncludes(), $this->getExcludes()); + } + /** * Retrieve excludes from optional "extra" configuration. * @@ -280,7 +284,15 @@ protected function getNamedOptionList($optionName, $defaultFn) { if (empty($options['omit-defaults'])) { $result = $this->$defaultFn(); } - $result = array_merge($result, (array) $options[$optionName]); + foreach ((array)$options[$optionName] as $sourceFile => $destFile) { + // Allow any option list to be specified as a simple array, or an + // associative array specifying source and destination. Convert + // simple arrays to associative arrays. + if(is_numeric($sourceFile)) { + $sourceFile = $destFile; + } + $result[$sourceFile] = $destFile; + } return $result; } @@ -352,7 +364,7 @@ protected function getIncludesDefault() { } sort($common); - return $common; + return array_combine($common, $common); } /** diff --git a/tests/HandlerTest.php b/tests/HandlerTest.php new file mode 100644 index 0000000..bb8c186 --- /dev/null +++ b/tests/HandlerTest.php @@ -0,0 +1,234 @@ + '.csslintrc', + '.editorconfig' => '.editorconfig', + '.eslintignore' => '.eslintignore', + '.eslintrc.json' => '.eslintrc.json', + '.gitattributes' => '.gitattributes', + '.htaccess' => '.htaccess', + 'index.php' => 'index.php', + 'robots.txt' => 'robots.txt', + 'sites/default/default.services.yml' => 'sites/default/default.services.yml', + 'sites/default/default.settings.php' => 'sites/default/default.settings.php', + 'sites/development.services.yml' => 'sites/development.services.yml', + 'sites/example.settings.local.php' => 'sites/example.settings.local.php', + 'sites/example.sites.php' => 'sites/example.sites.php', + 'update.php' => 'update.php', + 'web.config' => 'web.config', + ]; + + private function getComposer($drupalVersion, array $extra = []) { + $package = new RootPackage('test', '1.0.0', '1.0.0'); + $package->setExtra($extra); + $composer = new Composer(); + $composer->setPackage($package); + + $io = new NullIO(); + $config = new Config(false); + + $drupalPackage = new Package('drupal/core', $drupalVersion, $drupalVersion); + $localRepository = new WritableArrayRepository(); + $localRepository->addPackage($drupalPackage); + + $repositoryManager = new RepositoryManager($io, $config); + $repositoryManager->setLocalRepository($localRepository); + $composer->setRepositoryManager($repositoryManager); + + return $composer; + } + + public function getGetIncludesTests() { + return [ + [ + '8.4.2', + [], + self::$eightFourTwoIncludes + ], + [ + '8.4.2', + [ + 'drupal-scaffold' => [ + 'includes' => ['.csslintrc'] + ] + ], + self::$eightFourTwoIncludes + ], + [ + '8.4.2', + [ + 'drupal-scaffold' => [ + 'includes' => ['.csslintrc' => 'foo'] + ] + ], + ['.csslintrc' => 'foo'] + self::$eightFourTwoIncludes + ], + ]; + } + + /** + * @dataProvider getGetIncludesTests + */ + public function testGetIncludes($drupalVersion, $extra, $expected) { + $handler = new DummyHandler($this->getComposer($drupalVersion, $extra), new NullIO()); + $actual = $handler->doGetIncludes(); + $this->assertEquals($expected, $actual); + } + + public function getGetInitialTests() { + return [ + ['8.4.2', []] + ]; + } + + /** + * @dataProvider getGetInitialTests + */ + public function testGetInitial($drupalVersion, $expected) { + $io = $this->prophesize(IOInterface::class); + + $handler = new DummyHandler($this->getComposer($drupalVersion), $io->reveal()); + $actual = $handler->doGetInitial(); + $this->assertEquals($expected, $actual); + } + + public function getGetExcludesTests() { + return [ + ['8.4.2', []] + ]; + } + + /** + * @dataProvider getGetExcludesTests + */ + public function testGetExcludes($drupalVersion, $expected) { + $io = $this->prophesize(IOInterface::class); + + $handler = new DummyHandler($this->getComposer($drupalVersion), $io->reveal()); + $actual = $handler->doGetExcludes(); + $this->assertEquals($expected, $actual); + } + + public function getGetFilesTests() { + return [ + [ + '8.4.2', + [], + self::$eightFourTwoIncludes, + 'Default includes are returned if no excludes are specified.' + ], + [ + '8.4.2', + [ + 'drupal-scaffold' => [ + 'excludes' => ['.csslintrc'] + ] + ], + array_diff_key(self::$eightFourTwoIncludes, ['.csslintrc' => NULL]), + 'Excludes are removed from files when they are specified as a simple array', + ], + [ + '8.4.2', + // Nobody will do this, but we want to make sure it doesn't fail. + [ + 'drupal-scaffold' => [ + 'excludes' => [ + '.csslintrc' => 'foo' + ] + ] + ], + array_diff_key(self::$eightFourTwoIncludes, ['.csslintrc' => '']), + 'Excludes are removed from files when they are specified as an associative array', + ], + [ + '8.4.2', + ['drupal-scaffold' => ['omit-defaults' => true]], + [], + 'Defaults can be omitted.', + ], + [ + '8.4.2', + [ + 'drupal-scaffold' => [ + 'omit-defaults' => true, + 'includes' => [ + 'foo' => 'bar', + ], + ] + ], + ['foo' => 'bar'], + 'New includes will be considered' + ], + [ + '8.4.2', + [ + 'drupal-scaffold' => [ + 'omit-defaults' => true, + 'includes' => [ + 'foo' => 'bar', + ], + 'excludes' => ['foo'], + ] + ], + [], + 'New includes will be considered for exclusion' + ] + ]; + } + + /** + * Test that the getFiles method considers excludes. + * + * @dataProvider getGetFilesTests + */ + public function testGetFiles($drupalVersion, $extra, $expected, $message = '') { + $io = $this->prophesize(IOInterface::class); + + $handler = new DummyHandler($this->getComposer($drupalVersion, $extra), $io->reveal()); + $actual = $handler->doGetFiles(); + $this->assertEquals($expected, $actual, $message); + } + +} + +/** + * Extends handler to expose public methods that can be used for testing + * internal behavior. + */ +class DummyHandler extends Handler { + + public function doGetFiles() { + return $this->getFiles(); + } + + public function doGetIncludes() { + return $this->getIncludes(); + } + + public function doGetExcludes() { + return $this->getExcludes(); + } + + public function doGetInitial() { + return $this->getInitial(); + } +} From 70c008f597a725f2364168cd66fab700904f4f72 Mon Sep 17 00:00:00 2001 From: Rob Bayliss Date: Wed, 29 Nov 2017 08:40:59 -0500 Subject: [PATCH 3/3] Update README with information about specifying destination paths --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 82f7c87..032abe0 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,25 @@ The `initial` hash lists files that should be copied over only if they do not exist in the destination. The key specifies the path to the source file, and the value indicates the path to the destination file. +## Changing destination paths + +By using an associative array for `includes`, destination paths can be specified +that are different than the source paths. For example, if you wanted to install +`example.settings.local.php` into `sites/default/` instead of `sites/`: + +```json +{ + "extra": { + "drupal-scaffold": { + "source": "http://cgit.drupalcode.org/drupal/plain/{path}?h={version}", + "includes": { + "sites/example.settings.local.php": "sites/default/example.settings.local.php" + }, + } + } +} +``` + ## Limitation When using Composer to install or update the Drupal development branch, the