diff --git a/doc/06-config.md b/doc/06-config.md index 276e40911..6afbce1aa 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -63,6 +63,19 @@ optionally be a hash of patterns for more granular install preferences. > configuration in global and package configurations the string notation > is translated to a `*` package pattern. +## use-parent-dir + +When running Composer in a directory where there is no composer.json, if there +is one present in a directory above Composer will by default ask you whether +you want to use that directory's composer.json instead. + +If you always want to answer yes to this prompt, you can set this config value +to `true`. To never be prompted set it to `false`. The default is `"prompt"`. + +> **Note:** This config must be set in your global user-wide config for it +> to work. Use for example `php composer.phar config --global use-parent-dir true` +> to set it. + ## store-auths What to do after prompting for authentication, one of: `true` (always store), diff --git a/res/composer-schema.json b/res/composer-schema.json index e2e32cafa..02d529777 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -296,6 +296,10 @@ "type": "boolean", "description": "If true, the Composer autoloader will also look for classes in the PHP include path." }, + "use-parent-dir": { + "type": ["string", "boolean"], + "description": "When running Composer in a directory where there is no composer.json, if there is one present in a directory above Composer will by default ask you whether you want to use that directory's composer.json instead. One of: true (always use parent if needed), false (never ask or use it) or \"prompt\" (ask every time), defaults to prompt." + }, "preferred-install": { "type": ["string", "object"], "description": "The install method Composer will prefer to use, defaults to auto and can be any of source, dist, auto, or a hash of {\"pattern\": \"preference\"}." diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index ecaa76324..49479a6ba 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -454,6 +454,18 @@ EOT return 'php-only'; } + return $val !== 'false' && (bool)$val; + }, + ), + 'use-parent-dir' => array( + function ($val) { + return in_array($val, array('true', 'false', 'prompt'), true); + }, + function ($val) { + if ('prompt' === $val) { + return 'prompt'; + } + return $val !== 'false' && (bool) $val; }, ), @@ -595,7 +607,7 @@ EOT ); if ($input->getOption('global') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]) || strpos($settingKey, 'extra.') === 0)) { - throw new \InvalidArgumentException('The '.$settingKey.' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json'); + throw new \InvalidArgumentException('The ' . $settingKey . ' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json'); } if ($input->getOption('unset') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]))) { $this->configSource->removeProperty($settingKey); diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 00707bbad..ddf46a499 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -33,6 +33,7 @@ class Config public static $defaultConfig = array( 'process-timeout' => 300, 'use-include-path' => false, + 'use-parent-dir' => 'prompt', 'preferred-install' => 'dist', 'notify-on-install' => true, 'github-protocols' => array('https', 'ssh', 'git'), diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index e68321e29..2701e118f 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -172,14 +172,19 @@ class Application extends BaseApplication } // prompt user for dir change if no composer.json is present in current dir - if ($io->isInteractive() && !$newWorkDir && !in_array($commandName, array('', 'list', 'init', 'about', 'help', 'diagnose', 'self-update', 'global', 'create-project', 'outdated'), true) && !file_exists(Factory::getComposerFile())) { + if ($io->isInteractive() && !$newWorkDir && !in_array($commandName, array('', 'list', 'init', 'about', 'help', 'diagnose', 'self-update', 'global', 'create-project', 'outdated'), true) && !file_exists(Factory::getComposerFile()) && ($useParentDirIfNoJsonAvailable = $this->getUseParentDirConfigValue()) !== false) { $dir = dirname(getcwd()); $home = realpath(Platform::getEnv('HOME') ?: Platform::getEnv('USERPROFILE') ?: '/'); // abort when we reach the home dir or top of the filesystem while (dirname($dir) !== $dir && $dir !== $home) { if (file_exists($dir.'/'.Factory::getComposerFile())) { - if ($io->askConfirmation('No composer.json in current directory, do you want to use the one at '.$dir.'? [Y,n]? ')) { + if ($useParentDirIfNoJsonAvailable === true || $io->askConfirmation('No composer.json in current directory, do you want to use the one at '.$dir.'? [Y,n]? ')) { + if ($useParentDirIfNoJsonAvailable === true) { + $io->writeError('No composer.json in current directory, changing working directory to '.$dir.''); + } else { + $io->writeError('Always want to use the parent dir? Use "composer config --global use-parent-dir true" to change the default.'); + } $oldWorkingDir = getcwd(); chdir($dir); } @@ -593,4 +598,14 @@ class Application extends BaseApplication { return $this->initialWorkingDirectory; } + + /** + * @return 'prompt'|bool + */ + private function getUseParentDirConfigValue() + { + $config = Factory::createConfig($this->io); + + return $config->get('use-parent-dir'); + } }