Add drupal scaffold

This commit is contained in:
Rob Davies 2017-03-16 15:11:34 +00:00
parent 6fa31ad086
commit 3e6a5cbed2
15 changed files with 2748 additions and 1 deletions

2
web/.gitignore vendored
View file

@ -57,3 +57,5 @@ Thumbs.db
# Things in the core directory that Drupal 8 commits in the repository.
!core/**/*.gz
# Ignore any extra git folders.
*/.git/

@ -1 +0,0 @@
Subproject commit 3ad465ac853c2e52e6a808f5529859917662c256

View file

@ -0,0 +1,17 @@
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{composer.json,composer.lock}]
indent_size = 4

View file

@ -0,0 +1 @@
vendor

View file

@ -0,0 +1,25 @@
language: php
php:
- 5.5
- 5.6
- 7.0
sudo: false
git:
depth: 10000
before_install:
- phpenv config-rm xdebug.ini
- composer --verbose self-update
- composer --version
install:
- composer --verbose install
before_script:
- git config --global user.email "travisci@example.com"
- git config --global user.name "Travis CI Test"
script:
- ./vendor/bin/phpunit

View file

@ -0,0 +1,120 @@
# drupal-scaffold
[![Build Status](https://travis-ci.org/drupal-composer/drupal-scaffold.svg?branch=master)](https://travis-ci.org/drupal-composer/drupal-scaffold)
Composer plugin for automatically downloading Drupal scaffold files (like
`index.php`, `update.php`, …) when using `drupal/core` via Composer.
It is recommended that the vendor directory be placed in its standard location
at the project root, outside of the Drupal root; however, the location of the
vendor directory and the name of the Drupal root may be placed in whatever
location suits the project. Drupal-scaffold will generate the autoload.php
file at the Drupal root to require the Composer-generated autoload file in the
vendor directory.
## Usage
Run `composer require drupal-composer/drupal-scaffold:dev-master` in your composer
project before installing or updating `drupal/core`.
Once drupal-scaffold is required by your project, it will automatically update
your scaffold files whenever `composer update` changes the version of
`drupal/core` installed.
## Configuration
You can configure the plugin with providing some settings in the `extra` section
of your root `composer.json`.
```json
{
"extra": {
"drupal-scaffold": {
"source": "http://cgit.drupalcode.org/drupal/plain/{path}?h={version}",
"excludes": [
"google123.html",
"robots.txt"
],
"includes": [
"sites/default/example.settings.my.php"
],
"initial": {
"sites/default/default.services.yml": "sites/default/services.yml",
"sites/default/default.settings.php": "sites/default/settings.php"
},
"omit-defaults": false
}
}
}
```
The `source` option may be used to specify the URL to download the
scaffold files from; the default source is drupal.org. The literal string
`{version}` in the `source` option is replaced with the current version of
Drupal core being updated prior to download.
With the `drupal-scaffold` option `excludes`, you can provide additional paths
that should not be copied or overwritten. The plugin provides no excludes by
default.
Default includes are provided by the plugin:
```
.csslintrc
.editorconfig
.eslintignore
.eslintrc (Drupal <= 8.2.x)
.eslintrc.json (Drupal >= 8.3.x)
.gitattributes
.htaccess
index.php
robots.txt
sites/default/default.settings.php
sites/default/default.services.yml
sites/development.services.yml
sites/example.settings.local.php
sites/example.sites.php
update.php
web.config
```
When setting `omit-defaults` to `true`, neither the default excludes nor the
default includes will be provided; in this instance, only those files explicitly
listed in the `excludes` and `includes` options will be considered. If
`omit-defaults` is `false` (the default), then any items listed in `excludes`
or `includes` will be in addition to the usual defaults.
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.
## Limitation
When using Composer to install or update the Drupal development branch, the
scaffold files are always taken from the HEAD of the branch (or, more
specifically, from the most recent development .tar.gz archive). This might
not be what you want when using an old development version (e.g. when the
version is fixed via composer.lock). To avoid problems, always commit your
scaffold files to the repository any time that composer.lock is committed.
Note that the correct scaffold files are retrieved when using a tagged release
of `drupal/core` (recommended).
## Custom command
The plugin by default is only downloading the scaffold files when installing or
updating `drupal/core`. If you want to call it manually, you have to add the
command callback to the `scripts`-section of your root `composer.json`, like this:
```json
{
"scripts": {
"drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold"
}
}
```
After that you can manually download the scaffold files according to your
configuration by using `composer drupal-scaffold`.
It is assumed that the scaffold files will be committed to the repository, to
ensure that the correct files are used on the CI server (see **Limitation**,
above). After running `composer install` for the first time commit the scaffold
files to your repository.

View file

@ -0,0 +1,25 @@
{
"name": "drupal-composer/drupal-scaffold",
"description": "Composer Plugin for updating the Drupal scaffold files when using drupal/core",
"type": "composer-plugin",
"license": "GPL-2.0+",
"require": {
"php": ">=5.4.5",
"composer-plugin-api": "^1.0.0"
},
"autoload": {
"psr-4": {
"DrupalComposer\\DrupalScaffold\\": "src/"
}
},
"extra": {
"class": "DrupalComposer\\DrupalScaffold\\Plugin",
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"require-dev": {
"composer/composer": "dev-master",
"phpunit/phpunit": "^4.4.0"
}
}

1742
web/vendor/drupal-composer/drupal-scaffold/composer.lock generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
verbose="true"
>
<testsuites>
<testsuite name="drupal-scaffold">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\FileFetcher.
*/
namespace DrupalComposer\DrupalScaffold;
use Composer\Util\Filesystem;
use Composer\Util\RemoteFilesystem;
class FileFetcher {
/**
* @var \Composer\Util\RemoteFilesystem
*/
protected $remoteFilesystem;
protected $source;
protected $filenames;
protected $fs;
public function __construct(RemoteFilesystem $remoteFilesystem, $source, $filenames = []) {
$this->remoteFilesystem = $remoteFilesystem;
$this->source = $source;
$this->filenames = $filenames;
$this->fs = new Filesystem();
}
public function fetch($version, $destination) {
array_walk($this->filenames, function ($filename) use ($version, $destination) {
$url = $this->getUri($filename, $version);
$this->fs->ensureDirectoryExists($destination . '/' . dirname($filename));
$this->remoteFilesystem->copy($url, $url, $destination . '/' . $filename);
});
}
protected function getUri($filename, $version) {
$map = [
'{path}' => $filename,
'{version}' => $version
];
return str_replace(array_keys($map), array_values($map), $this->source);
}
}

View file

@ -0,0 +1,365 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\Handler.
*/
namespace DrupalComposer\DrupalScaffold;
use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\Filesystem;
use Composer\Util\RemoteFilesystem;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
class Handler {
const PRE_DRUPAL_SCAFFOLD_CMD = 'pre-drupal-scaffold-cmd';
const POST_DRUPAL_SCAFFOLD_CMD = 'post-drupal-scaffold-cmd';
/**
* @var \Composer\Composer
*/
protected $composer;
/**
* @var \Composer\IO\IOInterface
*/
protected $io;
/**
* @var \Composer\Package\PackageInterface
*/
protected $drupalCorePackage;
/**
* Handler constructor.
*
* @param Composer $composer
* @param IOInterface $io
*/
public function __construct(Composer $composer, IOInterface $io) {
$this->composer = $composer;
$this->io = $io;
}
/**
* @param $operation
* @return mixed
*/
protected function getCorePackage($operation) {
if ($operation instanceof InstallOperation) {
$package = $operation->getPackage();
}
elseif ($operation instanceof UpdateOperation) {
$package = $operation->getTargetPackage();
}
if (isset($package) && $package instanceof PackageInterface && $package->getName() == 'drupal/core') {
return $package;
}
return NULL;
}
/**
* Marks scaffolding to be processed after an install or update command.
*
* @param \Composer\Installer\PackageEvent $event
*/
public function onPostPackageEvent(\Composer\Installer\PackageEvent $event){
$package = $this->getCorePackage($event->getOperation());
if ($package) {
// By explicitly setting the core package, the onPostCmdEvent() will
// process the scaffolding automatically.
$this->drupalCorePackage = $package;
}
}
/**
* Post install command event to execute the scaffolding.
*
* @param \Composer\Script\Event $event
*/
public function onPostCmdEvent(\Composer\Script\Event $event) {
// Only install the scaffolding if drupal/core was installed,
// AND there are no scaffolding files present.
if (isset($this->drupalCorePackage)) {
$this->downloadScaffold();
// Generate the autoload.php file after generating the scaffold files.
$this->generateAutoload();
}
}
/**
* Downloads drupal scaffold files for the current process.
*/
public function downloadScaffold() {
$drupalCorePackage = $this->getDrupalCorePackage();
$webroot = realpath($this->getWebRoot());
// Collect options, excludes and settings files.
$options = $this->getOptions();
$files = array_diff($this->getIncludes(), $this->getExcludes());
// Call any pre-scaffold scripts that may be defined.
$dispatcher = new EventDispatcher($this->composer, $this->io);
$dispatcher->dispatch(self::PRE_DRUPAL_SCAFFOLD_CMD);
$version = $this->getDrupalCoreVersion($drupalCorePackage);
$remoteFs = new RemoteFilesystem($this->io);
$fetcher = new FileFetcher($remoteFs, $options['source'], $files);
$fetcher->fetch($version, $webroot);
$initialFileFetcher = new InitialFileFetcher($remoteFs, $options['source'], $this->getInitial());
$initialFileFetcher->fetch($version, $webroot);
// Call post-scaffold scripts.
$dispatcher->dispatch(self::POST_DRUPAL_SCAFFOLD_CMD);
}
/**
* Generate the autoload file at the project root. Include the
* autoload file that Composer generated.
*/
public function generateAutoload() {
$vendorPath = $this->getVendorPath();
$webroot = $this->getWebRoot();
// Calculate the relative path from the webroot (location of the
// project autoload.php) to the vendor directory.
$fs = new SymfonyFilesystem();
$relativeVendorPath = $fs->makePathRelative($vendorPath, realpath($webroot));
$fs->dumpFile($webroot . "/autoload.php", $this->autoLoadContents($relativeVendorPath));
}
/**
* Build the contents of the autoload file.
*
* @return string
*/
protected function autoLoadContents($relativeVendorPath) {
$relativeVendorPath = rtrim($relativeVendorPath, '/');
$autoloadContents = <<<EOF
<?php
/**
* @file
* Includes the autoloader created by Composer.
*
* This file was generated by drupal-composer/drupal-scaffold.
* https://github.com/drupal-composer/drupal-scaffold
*
* @see composer.json
* @see index.php
* @see core/install.php
* @see core/rebuild.php
* @see core/modules/statistics/statistics.php
*/
return require __DIR__ . '/$relativeVendorPath/autoload.php';
EOF;
return $autoloadContents;
}
/**
* Get the path to the 'vendor' directory.
*
* @return string
*/
public function getVendorPath() {
$config = $this->composer->getConfig();
$filesystem = new Filesystem();
$filesystem->ensureDirectoryExists($config->get('vendor-dir'));
$vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
return $vendorPath;
}
/**
* Look up the Drupal core package object, or return it from where we cached
* it in the $drupalCorePackage field.
*
* @return PackageInterface
*/
public function getDrupalCorePackage() {
if (!isset($this->drupalCorePackage)) {
$this->drupalCorePackage = $this->getPackage('drupal/core');
}
return $this->drupalCorePackage;
}
/**
* Returns the Drupal core version for the given package.
*
* @param \Composer\Package\PackageInterface $drupalCorePackage
*
* @return string
*/
protected function getDrupalCoreVersion(PackageInterface $drupalCorePackage) {
$version = $drupalCorePackage->getPrettyVersion();
if ($drupalCorePackage->getStability() == 'dev' && substr($version, -4) == '-dev') {
$version = substr($version, 0, -4);
return $version;
}
return $version;
}
/**
* Retrieve the path to the web root.
*
* @return string
*/
public function getWebRoot() {
$drupalCorePackage = $this->getDrupalCorePackage();
$installationManager = $this->composer->getInstallationManager();
$corePath = $installationManager->getInstallPath($drupalCorePackage);
// Webroot is the parent path of the drupal core installation path.
$webroot = dirname($corePath);
return $webroot;
}
/**
* Retrieve a package from the current composer process.
*
* @param string $name
* Name of the package to get from the current composer installation.
*
* @return PackageInterface
*/
protected function getPackage($name) {
return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*');
}
/**
* Retrieve excludes from optional "extra" configuration.
*
* @return array
*/
protected function getExcludes() {
return $this->getNamedOptionList('excludes', 'getExcludesDefault');
}
/**
* Retrieve list of additional settings files from optional "extra" configuration.
*
* @return array
*/
protected function getIncludes() {
return $this->getNamedOptionList('includes', 'getIncludesDefault');
}
/**
* Retrieve list of initial files from optional "extra" configuration.
*
* @return array
*/
protected function getInitial() {
return $this->getNamedOptionList('initial', 'getInitialDefault');
}
/**
* Retrieve a named list of options from optional "extra" configuration.
* Respects 'omit-defaults', and either includes or does not include the
* default values, as requested.
*
* @return array
*/
protected function getNamedOptionList($optionName, $defaultFn) {
$options = $this->getOptions($this->composer);
$result = array();
if (empty($options['omit-defaults'])) {
$result = $this->$defaultFn();
}
$result = array_merge($result, (array) $options[$optionName]);
return $result;
}
/**
* Retrieve excludes from optional "extra" configuration.
*
* @return array
*/
protected function getOptions() {
$extra = $this->composer->getPackage()->getExtra() + ['drupal-scaffold' => []];
$options = $extra['drupal-scaffold'] + [
'omit-defaults' => FALSE,
'excludes' => [],
'includes' => [],
'initial' => [],
'source' => 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}',
// Github: https://raw.githubusercontent.com/drupal/drupal/{version}/{path}
];
return $options;
}
/**
* Holds default excludes.
*/
protected function getExcludesDefault() {
return [];
}
/**
* Holds default settings files list.
*/
protected function getIncludesDefault() {
$version = $this->getDrupalCoreVersion($this->getDrupalCorePackage());
list($major, $minor) = explode('.', $version, 3);
$version = "$major.$minor";
/**
* Files from 8.3.x
*
* @see http://cgit.drupalcode.org/drupal/tree/?h=8.3.x
*/
$common = [
'.csslintrc',
'.editorconfig',
'.eslintignore',
'.eslintrc.json',
'.gitattributes',
'.htaccess',
'index.php',
'robots.txt',
'sites/default/default.settings.php',
'sites/default/default.services.yml',
'sites/development.services.yml',
'sites/example.settings.local.php',
'sites/example.sites.php',
'update.php',
'web.config'
];
// Version specific variations.
switch ($version) {
case '8.0':
case '8.1':
case '8.2':
$common[] = '.eslintrc';
$common = array_diff($common, ['.eslintrc.json']);
break;
}
sort($common);
return $common;
}
/**
* Holds default initial files.
*/
protected function getInitialDefault() {
return [];
}
}

View file

@ -0,0 +1,24 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\FileFetcher.
*/
namespace DrupalComposer\DrupalScaffold;
use Composer\Util\Filesystem;
use Composer\Util\RemoteFilesystem;
class InitialFileFetcher extends FileFetcher {
public function fetch($version, $destination) {
array_walk($this->filenames, function ($filename, $sourceFilename) use ($version, $destination) {
$target = "$destination/$filename";
if (!file_exists($target)) {
$url = $this->getUri($sourceFilename, $version);
$this->fs->ensureDirectoryExists($destination . '/' . dirname($filename));
$this->remoteFilesystem->copy($url, $url, $target);
}
});
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* @file
* Contains DrupalComposer\DrupalScaffold\Plugin.
*/
namespace DrupalComposer\DrupalScaffold;
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Installer\PackageEvent;
use Composer\Script\ScriptEvents;
/**
* Composer plugin for handling drupal scaffold.
*/
class Plugin implements PluginInterface, EventSubscriberInterface {
/**
* @var \DrupalComposer\DrupalScaffold\Handler
*/
protected $handler;
/**
* {@inheritdoc}
*/
public function activate(Composer $composer, IOInterface $io) {
// We use a separate PluginScripts object. This way we separate
// functionality and also avoid some debug issues with the plugin being
// copied on initialisation.
// @see \Composer\Plugin\PluginManager::registerPackage()
$this->handler = new Handler($composer, $io);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return array(
PackageEvents::POST_PACKAGE_INSTALL => 'postPackage',
PackageEvents::POST_PACKAGE_UPDATE => 'postPackage',
//PackageEvents::POST_PACKAGE_UNINSTALL => 'postPackage',
//ScriptEvents::POST_INSTALL_CMD => 'postCmd',
ScriptEvents::POST_UPDATE_CMD => 'postCmd',
);
}
/**
* Post package event behaviour.
*
* @param \Composer\Installer\PackageEvent $event
*/
public function postPackage(PackageEvent $event) {
$this->handler->onPostPackageEvent($event);
}
/**
* Post command event callback.
*
* @param \Composer\Script\Event $event
*/
public function postCmd(\Composer\Script\Event $event) {
$this->handler->onPostCmdEvent($event);
}
/**
* Script callback for putting in composer scripts to download the
* scaffold files.
*
* @param \Composer\Script\Event $event
*/
public static function scaffold(\Composer\Script\Event $event) {
$handler = new Handler($event->getComposer(), $event->getIO());
$handler->downloadScaffold();
// Generate the autoload.php file after generating the scaffold files.
$handler->generateAutoload();
}
}

View file

@ -0,0 +1,99 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\Tests\FetcherTest.
*/
namespace DrupalComposer\DrupalScaffold\Tests;
use Composer\Downloader\TransportException;
use Composer\IO\NullIO;
use Composer\Util\Filesystem;
use Composer\Util\RemoteFilesystem;
use DrupalComposer\DrupalScaffold\FileFetcher;
use DrupalComposer\DrupalScaffold\InitialFileFetcher;
class FetcherTest extends \PHPUnit_Framework_TestCase {
/**
* @var \Composer\Util\Filesystem
*/
protected $fs;
/**
* @var string
*/
protected $tmpDir;
/**
* @var string
*/
protected $rootDir;
/**
* @var string
*/
protected $tmpReleaseTag;
/**
* SetUp test
*/
public function setUp() {
$this->rootDir = realpath(realpath(__DIR__ . '/..'));
// Prepare temp directory.
$this->fs = new Filesystem();
$this->tmpDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'drupal-scaffold';
$this->ensureDirectoryExistsAndClear($this->tmpDir);
chdir($this->tmpDir);
}
/**
* Makes sure the given directory exists and has no content.
*
* @param string $directory
*/
protected function ensureDirectoryExistsAndClear($directory) {
if (is_dir($directory)) {
$this->fs->removeDirectory($directory);
}
mkdir($directory, 0777, true);
}
public function testFetch() {
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}', ['.htaccess', 'sites/default/default.settings.php']);
$fetcher->fetch('8.1.1', $this->tmpDir);
$this->assertFileExists($this->tmpDir . '/.htaccess');
$this->assertFileExists($this->tmpDir . '/sites/default/default.settings.php');
}
/**
* Tests version specific files.
*/
public function testFetchVersionSpecific() {
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), 'http://cgit.drupalcode.org/drupal/plain/{path}?h={version}', ['.eslintrc', '.eslintrc.json']);
$this->setExpectedException(TransportException::class);
$fetcher->fetch('8.2.x', $this->tmpDir);
$this->assertFileExists($this->tmpDir . '/.eslintrc');
$this->assertFileNotExists($this->tmpDir . '/.eslintrc.json');
// Remove downloaded files to retest with 8.3.x.
@unlink($this->tmpDir . '/.eslintrc');
$this->setExpectedException(TransportException::class);
$fetcher->fetch('8.3.x', $this->tmpDir);
$this->assertFileExists($this->tmpDir . '/.eslintrc.json');
$this->assertFileNotExists($this->tmpDir . '/.eslintrc');
}
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);
$this->assertFileExists($this->tmpDir . '/sites/default/settings.php');
}
}

View file

@ -0,0 +1,185 @@
<?php
/**
* @file
* Contains \DrupalComposer\DrupalScaffold\Tests\PluginTest.
*/
namespace DrupalComposer\DrupalScaffold\Tests;
use Composer\Util\Filesystem;
/**
* Tests composer plugin functionality.
*/
class PluginTest extends \PHPUnit_Framework_TestCase {
/**
* @var \Composer\Util\Filesystem
*/
protected $fs;
/**
* @var string
*/
protected $tmpDir;
/**
* @var string
*/
protected $rootDir;
/**
* @var string
*/
protected $tmpReleaseTag;
/**
* SetUp test
*/
public function setUp() {
$this->rootDir = realpath(realpath(__DIR__ . '/..'));
// Prepare temp directory.
$this->fs = new Filesystem();
$this->tmpDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'drupal-scaffold';
$this->ensureDirectoryExistsAndClear($this->tmpDir);
$this->writeTestReleaseTag();
$this->writeComposerJSON();
chdir($this->tmpDir);
}
/**
* tearDown
*
* @return void
*/
public function tearDown()
{
$this->fs->removeDirectory($this->tmpDir);
$this->git(sprintf('tag -d "%s"', $this->tmpReleaseTag));
}
/**
* Tests a simple composer install without core, but adding core later.
*/
public function testComposerInstallAndUpdate() {
$exampleScaffoldFile = $this->tmpDir . DIRECTORY_SEPARATOR . 'index.php';
$this->assertFileNotExists($exampleScaffoldFile, 'Scaffold file should not be exist.');
$this->composer('install');
$this->assertFileExists($this->tmpDir . DIRECTORY_SEPARATOR . 'core', 'Drupal core is installed.');
$this->assertFileExists($exampleScaffoldFile, 'Scaffold file should be automatically installed.');
$this->fs->remove($exampleScaffoldFile);
$this->assertFileNotExists($exampleScaffoldFile, 'Scaffold file should not be exist.');
$this->composer('drupal-scaffold');
$this->assertFileExists($exampleScaffoldFile, 'Scaffold file should be installed by "drupal-scaffold" command.');
foreach (['8.0.1', '8.1.x-dev'] as $version) {
// We touch a scaffold file, so we can check the file was modified after
// the scaffold update.
touch($exampleScaffoldFile);
$mtime_touched = filemtime($exampleScaffoldFile);
// Requiring a newer version triggers "composer update"
$this->composer('require --update-with-dependencies drupal/core:"' . $version .'"');
clearstatcache();
$mtime_after = filemtime($exampleScaffoldFile);
$this->assertNotEquals($mtime_after, $mtime_touched, 'Scaffold file was modified by composer update. (' . $version . ')');
}
// We touch a scaffold file, so we can check the file was modified after
// the custom commandscaffold update.
touch($exampleScaffoldFile);
clearstatcache();
$mtime_touched = filemtime($exampleScaffoldFile);
$this->composer('drupal-scaffold');
clearstatcache();
$mtime_after = filemtime($exampleScaffoldFile);
$this->assertNotEquals($mtime_after, $mtime_touched, 'Scaffold file was modified by custom command.');
}
/**
* Writes the default composer json to the temp direcoty.
*/
protected function writeComposerJSON() {
$json = json_encode($this->composerJSONDefaults(), JSON_PRETTY_PRINT);
// Write composer.json.
file_put_contents($this->tmpDir . '/composer.json', $json);
}
/**
* Writes a tag for the current commit, so we can reference it directly in the
* composer.json.
*/
protected function writeTestReleaseTag() {
// Tag the current state.
$this->tmpReleaseTag = '999.0.' . time();
$this->git(sprintf('tag -a "%s" -m "%s"', $this->tmpReleaseTag, 'Tag for testing this exact commit'));
}
/**
* Provides the default composer.json data.
*
* @return array
*/
protected function composerJSONDefaults() {
return array(
'repositories' => array(
array(
'type' => 'vcs',
'url' => $this->rootDir,
)
),
'require' => array(
'drupal-composer/drupal-scaffold' => $this->tmpReleaseTag,
'composer/installers' => '^1.0.20',
'drupal/core' => '8.0.0',
),
'scripts' => array(
'drupal-scaffold' => 'DrupalComposer\\DrupalScaffold\\Plugin::scaffold'
),
'minimum-stability' => 'dev',
);
}
/**
* Wrapper for the composer command.
*
* @param string $command
* Composer command name, arguments and/or options
*/
protected function composer($command) {
chdir($this->tmpDir);
passthru(escapeshellcmd($this->rootDir . '/vendor/bin/composer ' . $command), $exit_code);
if ($exit_code !== 0) {
throw new \Exception('Composer returned a non-zero exit code');
}
}
/**
* Wrapper for git command in the root directory.
*
* @param $command
* Git command name, arguments and/or options.
*/
protected function git($command) {
chdir($this->rootDir);
passthru(escapeshellcmd('git ' . $command), $exit_code);
if ($exit_code !== 0) {
throw new \Exception('Git returned a non-zero exit code');
}
}
/**
* Makes sure the given directory exists and has no content.
*
* @param string $directory
*/
protected function ensureDirectoryExistsAndClear($directory) {
if (is_dir($directory)) {
$this->fs->removeDirectory($directory);
}
mkdir($directory, 0777, true);
}
}