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');
+ }
}