Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow destination paths for includes to be specified #69

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/FileFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
18 changes: 15 additions & 3 deletions src/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -352,7 +364,7 @@ protected function getIncludesDefault() {
}

sort($common);
return $common;
return array_combine($common, $common);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/PrestissimoFileFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
6 changes: 6 additions & 0 deletions tests/FetcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
234 changes: 234 additions & 0 deletions tests/HandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\Tests\HandlerTest.
*/

namespace DrupalComposer\DrupalScaffold\Tests;


use Composer\Composer;
use Composer\Config;
use Composer\IO\IOInterface;
use Composer\IO\NullIO;
use Composer\Package\Package;
use Composer\Package\RootPackage;
use Composer\Repository\RepositoryManager;
use Composer\Repository\WritableArrayRepository;
use DrupalComposer\DrupalScaffold\Handler;

class HandlerTest extends \PHPUnit_Framework_TestCase {

private static $eightFourTwoIncludes = [
'.csslintrc' => '.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();
}
}