├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── CommandProvider.php ├── ComposerPlugin.php └── DrupalInitCommand.php └── tests └── DrupalInitCommandTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | composer.phar 3 | /vendor/ 4 | /build/ 5 | 6 | /.idea/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | # - 5.5 5 | # - 5.6 6 | # - 7.0 7 | - 7.1 8 | 9 | # This triggers builds to run on the new TravisCI infrastructure. 10 | # See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 11 | sudo: false 12 | 13 | before_script: 14 | - travis_retry composer self-update 15 | - travis_retry composer install --no-interaction --prefer-dist --dev 16 | 17 | script: 18 | - composer test 19 | # - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 hussainweb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drupal-composer-init 2 | 3 | [![Latest Version](https://img.shields.io/github/release/hussainweb/drupal-composer-init.svg?style=flat-square)](https://github.com/hussainweb/drupal-composer-init/releases) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Build Status](https://img.shields.io/travis/hussainweb/drupal-composer-init/master.svg?style=flat-square)](https://travis-ci.org/hussainweb/drupal-composer-init) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/hussainweb/drupal-composer-init.svg?style=flat-square)](https://packagist.org/packages/hussainweb/drupal-composer-init) 7 | 8 | This plugin provides a new composer command (`drupal-init`) which helps in creating new Drupal installations based on composer. Most of the options are very similar to the default `composer init` command. There are additional options to specify a Drupal core or distro to use and the docroot. 9 | 10 | This plugin also supports Drupal 7 installation but this option needs to be passed in from command line. 11 | 12 | The composer.json setup that this plugin generates is inspired by [drupal-composer/drupal-project](https://github.com/drupal-composer/drupal-project) (both D7 and D8 versions). One important difference is that the composer.json generated by this command does not need several scripts which are part of the drupal-composer project. All the functionality is moved to [hussainweb/drupal-composer-helper](https://github.com/hussainweb/drupal-composer-helper). There are additional benefits like a simpler `extra` section and a more maintainable installation. For more details, see the [drupal-composer-helper](https://github.com/hussainweb/drupal-composer-helper) plugin. 13 | 14 | ## Installation 15 | 16 | Since this plugin provides a new composer command that should be used in an empty directory (or at least a directory without a composer.json setup), this has to be installed globally. 17 | 18 | ``` 19 | composer global require hussainweb/drupal-composer-init:~1.0 20 | ``` 21 | 22 | ## Updating 23 | 24 | ``` 25 | composer global update hussainweb/drupal-composer-init --with-dependencies 26 | ``` 27 | 28 | ## Usage 29 | 30 | In a fresh directory, run this command to get started. 31 | 32 | ``` 33 | composer drupal-init 34 | ``` 35 | 36 | To intialise a Drupal 7 website, run the following command: 37 | 38 | ``` 39 | composer drupal-init --drupal-7 40 | ``` 41 | 42 | ### Specifying extensions (modules, themes, profiles) 43 | 44 | Drupal extensions such as modules, themes, and profiles can be specified in the regular `require` or `require-dev` section of composer.json schema. 45 | 46 | All Drupal extensions can be specified in the regular packages section with their drupal.org name prefixed by '`drupal/`'. For example, if you want to require panels module, specify `drupal/panels`. 47 | 48 | ``` 49 | Define your dependencies. 50 | 51 | Would you like to define your dependencies (require) now [yes]? 52 | Search for a package: drupal/panels 53 | Enter the version constraint to require (or leave blank to use the latest version): 54 | Using version ^4.2 for drupal/panels 55 | Search for a package: drupal/redirect 56 | Enter the version constraint to require (or leave blank to use the latest version): 57 | Using version ^1.0@beta for drupal/redirect 58 | Search for a package: 59 | ``` 60 | 61 | ### Additional Help 62 | 63 | Run `composer help drupal-init` for more options. 64 | 65 | ``` 66 | Usage: 67 | drupal-init [options] 68 | 69 | Options: 70 | --name=NAME Name of the package 71 | --description=DESCRIPTION Description of package 72 | --author=AUTHOR Author name of package 73 | --type[=TYPE] Type of package (e.g. library, project, metapackage, composer-plugin) [default: "project"] 74 | --homepage=HOMEPAGE Homepage of package 75 | --require=REQUIRE Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0" (multiple values allowed) 76 | --require-dev=REQUIRE-DEV Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0" (multiple values allowed) 77 | -c, --core=CORE Drupal Core or distribution, e.g. drupal/core or acquia/lightning [default: "drupal/core"] 78 | -s, --stability=STABILITY Minimum stability (empty or one of: stable, RC, beta, alpha, dev) 79 | -l, --license=LICENSE License of package 80 | --repository=REPOSITORY Add custom repositories, either by URL or using JSON arrays (multiple values allowed) 81 | -w, --web-dir=WEB-DIR Specify the docroot (defaults to web) [default: "web"] 82 | --drupal-7 Use Drupal 7 packagist instead of Drupal 8 83 | -h, --help Display this help message 84 | -q, --quiet Do not output any message 85 | -V, --version Display this application version 86 | --ansi Force ANSI output 87 | --no-ansi Disable ANSI output 88 | -n, --no-interaction Do not ask any interactive question 89 | --profile Display timing and memory usage information 90 | --no-plugins Whether to disable plugins. 91 | -d, --working-dir=WORKING-DIR If specified, use the given directory as working directory. 92 | -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 93 | 94 | Help: 95 | The drupal-init command creates a composer.json file 96 | usable for Drupal projects in the current directory. 97 | 98 | php composer.phar drupal-init 99 | ``` 100 | 101 | ## Contributing 102 | 103 | Contributions are welcome. Please use the issue queue to describe the problem. Pull requests are welcome. 104 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hussainweb/drupal-composer-init", 3 | "description": "Initialise a Drupal composer project setup", 4 | "type": "composer-plugin", 5 | "keywords": [ 6 | "drupal" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "hussainweb", 12 | "email": "hussainweb@gmail.com", 13 | "homepage": "http://hussainweb.me", 14 | "role": "Developer" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.5.9", 19 | "composer-plugin-api": "^1.1" 20 | }, 21 | "require-dev": { 22 | "composer/composer": "~1.0", 23 | "phpunit/phpunit": "^5.7", 24 | "squizlabs/php_codesniffer": "~2.3" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Hussainweb\\DrupalComposerInit\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Hussainweb\\DrupalComposerInit\\": "tests/" 34 | } 35 | }, 36 | "config": { 37 | "optimize-autoloader": true, 38 | "sort-packages": true 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-master": "1.x-dev" 43 | }, 44 | "class": "Hussainweb\\DrupalComposerInit\\ComposerPlugin" 45 | }, 46 | "scripts": { 47 | "test": [ 48 | "composer validate --no-interaction", 49 | "phpcs --standard=psr2 src/ tests/", 50 | "phpunit tests/" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/CommandProvider.php: -------------------------------------------------------------------------------- 1 | 'Hussainweb\DrupalComposerInit\CommandProvider', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DrupalInitCommand.php: -------------------------------------------------------------------------------- 1 | setName('drupal-init') 26 | ->setDescription('Creates a Drupal composer.json file in current directory.') 27 | ->setDefinition([ 28 | new InputOption('name', null, InputOption::VALUE_REQUIRED, 'Name of the package'), 29 | new InputOption('description', null, InputOption::VALUE_REQUIRED, 'Description of package'), 30 | new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'), 31 | // new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'), 32 | new InputOption('type', null, InputOption::VALUE_OPTIONAL, 'Type of package (e.g. library, project, metapackage, composer-plugin)', 'project'), 33 | new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'), 34 | new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), 35 | new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'), 36 | new InputOption('core', 'c', InputOption::VALUE_REQUIRED, 'Drupal Core or distribution, e.g. drupal/core or acquia/lightning', 'drupal/core'), 37 | new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'), 38 | new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'), 39 | new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'), 40 | new InputOption('web-dir', 'w', InputOption::VALUE_REQUIRED, 'Specify the docroot (defaults to web)', 'web'), 41 | new InputOption('drupal-7', null, InputOption::VALUE_NONE, 'Use Drupal 7 packagist instead of Drupal 8'), 42 | // new InputOption('extensions', 'm', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Extensions (such as modules or themes) to require with a version constraint, e.g. panels:^4.0'), 43 | // new InputOption('extensions-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Extensions (such as modules or themes) to require for development with a version constraint, e.g. panels:^4.0'), 44 | ]) 45 | ->setHelp(<<drupal-init command creates a composer.json file 47 | usable for Drupal projects in the current directory. 48 | 49 | php composer.phar drupal-init 50 | 51 | EOT 52 | ); 53 | // @codingStandardsIgnoreEnd 54 | } 55 | 56 | protected function isDrupal7(InputInterface $input) 57 | { 58 | return $input->getOption('drupal-7'); 59 | } 60 | 61 | protected function getExtraRequires(InputInterface $input) 62 | { 63 | $requires = [ 64 | 'd7' => [ 65 | 'php >=5.2.5', 66 | 'ext-gd *', 67 | 'ext-xml *', 68 | 'ext-json *', 69 | 'ext-openssl *', 70 | 'ext-curl *', 71 | 'ext-pdo *', 72 | 'ext-pdo_mysql *', 73 | 'cweagans/composer-patches ^1.6.0', 74 | 'hussainweb/drupal-composer-helper ^1.0-beta3', 75 | 'drupal-composer/preserve-paths ^0.1', 76 | $input->getOption('core'), 77 | 'drupal/composer_autoloader ^1.0', 78 | 'drush/drush ~8.0', 79 | ], 80 | 'd8' => [ 81 | 'cweagans/composer-patches ^1.6.0', 82 | 'hussainweb/drupal-composer-helper ^1.0', 83 | $input->getOption('core'), 84 | 'drupal/console ^1.0.2', 85 | 'drush/drush ~8.0|^9.0.0-beta8', 86 | ], 87 | ]; 88 | return $this->isDrupal7($input) ? $requires['d7'] : $requires['d8']; 89 | } 90 | 91 | protected function getExtraRequireDevs(InputInterface $input) 92 | { 93 | $require_dev = [ 94 | 'behat/mink ~1.7', 95 | 'behat/mink-goutte-driver ~1.2', 96 | 'jcalderonzumba/gastonjs ~1.0.2', 97 | 'jcalderonzumba/mink-phantomjs-driver ~0.3.1', 98 | 'mikey179/vfsstream ~1.2', 99 | 'phpunit/phpunit >=4.8.28 <5', 100 | 'symfony/css-selector ~2.8', 101 | ]; 102 | return $this->isDrupal7($input) ? [] : $require_dev; 103 | } 104 | 105 | protected function execute(InputInterface $input, OutputInterface $output) 106 | { 107 | $options = $input->getOptions(); 108 | 109 | $options['require'] = array_merge($this->getExtraRequires($input), $options['require']); 110 | 111 | $options['require-dev'] = array_merge($this->getExtraRequireDevs($input), $options['require-dev']); 112 | 113 | $input->setOption('require', $options['require']); 114 | $input->setOption('require-dev', $options['require-dev']); 115 | $input->setOption('type', 'project'); 116 | 117 | $status = parent::execute($input, $output); 118 | 119 | if (!$status) { 120 | $web_prefix = $input->getOption('web-dir'); 121 | 122 | // Write extra to composer.json file. 123 | $file = new JsonFile(Factory::getComposerFile()); 124 | $options = $file->read(); 125 | $options['extra'] = [ 126 | 'drupal-composer-helper' => [ 127 | 'web-prefix' => $web_prefix, 128 | ], 129 | 'enable-patching' => true, 130 | 'installer-paths' => [ 131 | $web_prefix . '/core' => [ 132 | 'type:drupal-core', 133 | ], 134 | ], 135 | ]; 136 | $options['prefer-stable'] = true; 137 | $options['conflict'] = [ 138 | 'drupal/drupal' => '*', 139 | ]; 140 | $options['config'] = [ 141 | 'sort-packages' => true, 142 | 'optimize-autoloader' => true, 143 | ]; 144 | 145 | if ($this->isDrupal7($input)) { 146 | $extra = &$options['extra']; 147 | $options['conflict'] = [ 148 | 'drupal/core' => '8.*', 149 | ]; 150 | 151 | $extra['drupal-composer-helper']['set-d7-paths'] = true; 152 | $extra['preserve-paths'] = [ 153 | $web_prefix . '/sites/all/libraries', 154 | $web_prefix . '/sites/all/modules/custom', 155 | $web_prefix . '/sites/all/modules/features', 156 | $web_prefix . '/sites/all/themes/custom', 157 | $web_prefix . '/sites/all/translations', 158 | $web_prefix . '/sites/default', 159 | ]; 160 | $extra['installer-paths'][$web_prefix . '/'] = $extra['installer-paths'][$web_prefix . '/core']; 161 | unset($extra['installer-paths'][$web_prefix . '/core']); 162 | } 163 | 164 | $file->write($options); 165 | } 166 | 167 | return $status; 168 | } 169 | 170 | private function getRepositories(InputInterface $input) 171 | { 172 | $io = $this->getIO(); 173 | // initialize repos if configured 174 | $config = Factory::createConfig($io); 175 | $repos = [new PlatformRepository()]; 176 | 177 | $repositories = $input->getOption('repository'); 178 | if ($repositories) { 179 | foreach ($repositories as $repo) { 180 | $repos[] = RepositoryFactory::fromString($io, $config, $repo); 181 | } 182 | } 183 | 184 | $packagist_url = 'https://packages.drupal.org/' . ($this->isDrupal7($input) ? '7' : '8'); 185 | $repos[] = RepositoryFactory::createRepo($io, $config, [ 186 | 'type' => 'composer', 187 | 'url' => $packagist_url, 188 | ]); 189 | 190 | // Add our Drupal packagist URL to the repositories so that it appears 191 | // in the composer.json file. 192 | $repositories[] = $packagist_url; 193 | $input->setOption('repository', $repositories); 194 | 195 | $repos[] = RepositoryFactory::createRepo($io, $config, [ 196 | 'type' => 'composer', 197 | 'url' => 'https://packagist.org', 198 | ]); 199 | 200 | return new CompositeRepository($repos); 201 | } 202 | 203 | protected function interact(InputInterface $input, OutputInterface $output) 204 | { 205 | $git = $this->getGitConfig(); 206 | $io = $this->getIO(); 207 | /** @var \Symfony\Component\Console\Helper\FormatterHelper $formatter */ 208 | $formatter = $this->getHelperSet()->get('formatter'); 209 | 210 | $this->repos = $this->getRepositories($input); 211 | 212 | $io->writeError([ 213 | '', 214 | $formatter->formatBlock('Welcome to the Drupal Composer config generator', 'bg=blue;fg=white', true), 215 | '', 216 | ]); 217 | 218 | // namespace 219 | $io->writeError([ 220 | '', 221 | 'This command will guide you through creating your composer.json config for a Drupal setup.', 222 | '', 223 | ]); 224 | 225 | $cwd = realpath("."); 226 | 227 | if (!$name = $input->getOption('name')) { 228 | $name = basename($cwd); 229 | $name = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $name); 230 | $name = strtolower($name); 231 | if (isset($git['github.user'])) { 232 | $name = $git['github.user'] . '/' . $name; 233 | } elseif (!empty($_SERVER['USERNAME'])) { 234 | $name = $_SERVER['USERNAME'] . '/' . $name; 235 | } elseif (!empty($_SERVER['USER'])) { 236 | $name = $_SERVER['USER'] . '/' . $name; 237 | } elseif (get_current_user()) { 238 | $name = get_current_user() . '/' . $name; 239 | } else { 240 | // package names must be in the format foo/bar 241 | $name = $name . '/' . $name; 242 | } 243 | $name = strtolower($name); 244 | } else { 245 | if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $name)) { 246 | throw new \InvalidArgumentException( 247 | 'The package name '.$name.' is invalid, it should be lowercase and have a 248 | vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+' 249 | ); 250 | } 251 | } 252 | 253 | $name = $io->askAndValidate( 254 | 'Package name (/) ['.$name.']: ', 255 | function ($value) use ($name) { 256 | if (null === $value) { 257 | return $name; 258 | } 259 | 260 | if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $value)) { 261 | throw new \InvalidArgumentException( 262 | 'The package name '.$value.' is invalid, it should be lowercase and have a 263 | vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+' 264 | ); 265 | } 266 | 267 | return $value; 268 | }, 269 | null, 270 | $name 271 | ); 272 | $input->setOption('name', $name); 273 | 274 | $description = $input->getOption('description') ?: false; 275 | $description = $io->ask( 276 | 'Description ['.$description.']: ', 277 | $description 278 | ); 279 | $input->setOption('description', $description); 280 | 281 | if (null === $author = $input->getOption('author')) { 282 | if (isset($git['user.name']) && isset($git['user.email'])) { 283 | $author = sprintf('%s <%s>', $git['user.name'], $git['user.email']); 284 | } 285 | } 286 | 287 | $self = $this; 288 | $author = $io->askAndValidate( 289 | 'Author ['.$author.', n to skip]: ', 290 | function ($value) use ($self, $author) { 291 | if ($value === 'n' || $value === 'no') { 292 | return; 293 | } 294 | $value = $value ?: $author; 295 | $author = $self->parseAuthorString($value); 296 | 297 | return sprintf('%s <%s>', $author['name'], $author['email']); 298 | }, 299 | null, 300 | $author 301 | ); 302 | $input->setOption('author', $author); 303 | 304 | $minimumStability = $input->getOption('stability') ?: 'dev'; 305 | $minimumStability = $io->askAndValidate( 306 | 'Minimum Stability ['.$minimumStability.']: ', 307 | function ($value) use ($self, $minimumStability) { 308 | if (null === $value) { 309 | return $minimumStability; 310 | } 311 | 312 | if (!isset(BasePackage::$stabilities[$value])) { 313 | throw new \InvalidArgumentException( 314 | 'Invalid minimum stability "'.$value.'". Must be empty or one of: '. 315 | implode(', ', array_keys(BasePackage::$stabilities)) 316 | ); 317 | } 318 | 319 | return $value; 320 | }, 321 | null, 322 | $minimumStability 323 | ); 324 | $input->setOption('stability', $minimumStability); 325 | 326 | $license = $input->getOption('license') ?: false; 327 | $license = $io->ask( 328 | 'License ['.$license.']: ', 329 | $license 330 | ); 331 | $input->setOption('license', $license); 332 | 333 | $web_dir = $input->getOption('web-dir') ?: false; 334 | $web_dir = $io->ask( 335 | 'Public web directory ['.$web_dir.']: ', 336 | $web_dir 337 | ); 338 | $input->setOption('web-dir', $web_dir); 339 | 340 | $input->setOption('core', $this->getCore($input)); 341 | 342 | $io->writeError(['', 'Define your dependencies.', '']); 343 | 344 | $question = 'Would you like to define your dependencies (require) now [yes]? '; 345 | $require = $input->getOption('require'); 346 | $requirements = []; 347 | if ($require || $io->askConfirmation($question, true)) { 348 | $requirements = $this->determineRequirements($input, $output, $require); 349 | } 350 | $input->setOption('require', $requirements); 351 | 352 | $question = 'Would you like to define your dev dependencies (require-dev) now [yes]? '; 353 | $requireDev = $input->getOption('require-dev'); 354 | $devRequirements = []; 355 | if ($requireDev || $io->askConfirmation($question, true)) { 356 | $devRequirements = $this->determineRequirements($input, $output, $requireDev); 357 | } 358 | $input->setOption('require-dev', $devRequirements); 359 | } 360 | 361 | protected function getCore(InputInterface $input) 362 | { 363 | $io = $this->getIO(); 364 | 365 | $core = $input->getOption('core'); 366 | if ($this->isDrupal7($input) && $core == 'drupal/core') { 367 | $core = 'drupal/drupal'; 368 | } 369 | $name_version_pair = $this->normalizeRequirements((array) $core); 370 | $core_package = $name_version_pair[0]['name']; 371 | 372 | $core_package = $io->askAndValidate( 373 | 'Drupal core or distribution ['.$core_package.']: ', 374 | function ($value) { 375 | $packages = $this->findPackages($value); 376 | foreach ($packages as $package) { 377 | if ($package['name'] == $value) { 378 | return $value; 379 | } 380 | } 381 | throw new \Exception('Package not found'); 382 | }, 383 | null, 384 | $core_package 385 | ); 386 | 387 | $pool = new Pool($input->getOption('stability')); 388 | $pool->addRepository($this->getRepos()); 389 | $versionSelector = new VersionSelector($pool); 390 | $package = $versionSelector->findBestCandidate($core_package); 391 | $core_version = $package ? $versionSelector->findRecommendedRequireVersion($package) : ''; 392 | 393 | $core_version = $io->ask( 394 | 'Version for '.$core_package.' ['.$core_version.']: ', 395 | $core_version 396 | ); 397 | 398 | return $core_package.' '.$core_version; 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /tests/DrupalInitCommandTest.php: -------------------------------------------------------------------------------- 1 | cwd = getcwd(); 26 | chdir('/tmp'); 27 | } 28 | 29 | protected function tearDown() 30 | { 31 | chdir($this->cwd); 32 | gc_collect_cycles(); 33 | } 34 | 35 | protected function getHelperSet() 36 | { 37 | if ($this->helperSet === null) { 38 | $this->helperSet = new HelperSet([ 39 | new FormatterHelper(), 40 | new QuestionHelper(), 41 | ]); 42 | } 43 | return $this->helperSet; 44 | } 45 | 46 | public function testDefaultInit() 47 | { 48 | $command = $this->getMockBuilder(DrupalInitCommand::class) 49 | ->setMethods([ 50 | 'getCore', 51 | 'getHelperSet', 52 | ]) 53 | ->getMock(); 54 | $command->method('getCore')->willReturn('drupal/core ^8.4'); 55 | $command->method('getHelperSet')->willReturn($this->getHelperSet()); 56 | 57 | $tester = new CommandTester($command); 58 | $tester->execute([ 59 | '--name' => 'test/test', 60 | '--author' => 'Test User ', 61 | '--description' => 'Test Description', 62 | '--core' => 'drupal/core ^8.4', 63 | '--stability' => 'dev', 64 | ]); 65 | 66 | $json = json_decode(file_get_contents('./composer.json'), true); 67 | 68 | $this->assertSame('test/test', $json['name']); 69 | $this->assertCount(1, $json['authors']); 70 | $this->assertSame('Test User', $json['authors'][0]['name']); 71 | $this->assertSame('testuser@example.com', $json['authors'][0]['email']); 72 | $this->assertSame('Test Description', $json['description']); 73 | $this->assertSame('project', $json['type']); 74 | $this->assertSame('dev', $json['minimum-stability']); 75 | $this->assertSame('composer', $json['repositories'][0]['type']); 76 | $this->assertSame('https://packages.drupal.org/8', $json['repositories'][0]['url']); 77 | 78 | $this->assertSame('web', $json['extra']['drupal-composer-helper']['web-prefix']); 79 | 80 | // @todo: Remove this test once the workaround for drupal-finder is fixed. 81 | $this->assertSame('type:drupal-core', $json['extra']['installer-paths']['web/core'][0]); 82 | 83 | $this->assertArrayNotHasKey('preserve-paths', $json['extra']); 84 | 85 | $this->assertArraySubset([ 86 | 'hussainweb/drupal-composer-helper' => '^1.0', 87 | 'drupal/core' => '^8.4', 88 | ], $json['require']); 89 | 90 | unlink('./composer.json'); 91 | } 92 | 93 | public function testDrupal7Mode() 94 | { 95 | $command = $this->getMockBuilder(DrupalInitCommand::class) 96 | ->setMethods([ 97 | 'getCore', 98 | 'getHelperSet', 99 | ]) 100 | ->getMock(); 101 | $command->method('getCore')->willReturn('drupal/drupal ~7.0'); 102 | $command->method('getHelperSet')->willReturn($this->getHelperSet()); 103 | 104 | $tester = new CommandTester($command); 105 | $tester->execute([ 106 | '--name' => 'test/test', 107 | '--author' => 'Test User ', 108 | '--description' => 'Test Description', 109 | '--core' => 'drupal/drupal ~7.0', 110 | '--stability' => 'dev', 111 | '--drupal-7' => true, 112 | ]); 113 | 114 | $json = json_decode(file_get_contents('./composer.json'), true); 115 | 116 | $this->assertSame('test/test', $json['name']); 117 | $this->assertCount(1, $json['authors']); 118 | $this->assertSame('Test User', $json['authors'][0]['name']); 119 | $this->assertSame('testuser@example.com', $json['authors'][0]['email']); 120 | $this->assertSame('Test Description', $json['description']); 121 | $this->assertSame('project', $json['type']); 122 | $this->assertSame('dev', $json['minimum-stability']); 123 | $this->assertSame('composer', $json['repositories'][0]['type']); 124 | $this->assertSame('https://packages.drupal.org/7', $json['repositories'][0]['url']); 125 | 126 | $this->assertSame('web', $json['extra']['drupal-composer-helper']['web-prefix']); 127 | 128 | // @todo: Remove this test once the workaround for drupal-finder is fixed. 129 | $this->assertSame('type:drupal-core', $json['extra']['installer-paths']['web/'][0]); 130 | $this->assertArrayNotHasKey('web/core', $json['extra']['installer-paths']); 131 | 132 | $this->assertArrayHasKey('preserve-paths', $json['extra']); 133 | 134 | $this->assertArrayNotHasKey('drupal/core', $json['require']); 135 | $this->assertArraySubset([ 136 | 'drupal/drupal' => '~7.0', 137 | ], $json['require']); 138 | 139 | unlink('./composer.json'); 140 | } 141 | } 142 | --------------------------------------------------------------------------------