From 626ca8018c37751b5c3e9eea21db3f4b2e3df88f Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 10:23:47 -0700 Subject: [PATCH 01/13] Update aliases example file. --- examples/example.aliases.yml | 151 +++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index 056599c02f..6248d89449 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -1,4 +1,4 @@ - +# # Example of valid statements for an alias file. # # Use this file as a guide to creating your own aliases. @@ -6,10 +6,18 @@ # Aliases are commonly used to define short names for # local or remote Drupal installations; however, an alias # is really nothing more than a collection of options. -# A canonical alias named "dev" that points to a local +# +# Drush site aliases always contain one or more environments; +# for example, a site may have "dev", "test" and "live" +# environments. The default environment is "dev"; the dev +# environment of a site named "example" may therefore be +# referred to as either "@example.dev" or "@example". +# +# A canonical alias named "example" that points to a local # Drupal site named "http://example.com" looks like this: # # @code +# File: example.alias.yml # dev: # root: /path/to/drupal # uri: http://example.com @@ -18,13 +26,13 @@ # With this alias definition, then the following commands # are equivalent: # -# $ drush @dev status +# $ drush @example status # $ drush --root=/path/to/drupal --uri=http://example.com status # # See the --uri option documentation below for hints on setting its value. # # Any option that can be placed on the Drush commandline -# can also appear in an alias definition. +# can also appear in an alias definition inside an 'optons' section. # # There are several ways to create alias files. # @@ -43,9 +51,9 @@ # a. The /drush and /sites/all/drush folders for the current Drupal site # b. The /drush folder in the directory above the current Drupal site # -# These locations are searched recursively. If there is a folder called -# 'site-aliases' in any search path, then Drush will search for site aliases -# only inside that directory. +# These locations are no longer searched recursively; alias files musst +# appear directly inside one of the search locations, or in a directory called +# 'site-aliases' in a search location, or it will not be found. # # The preferred locations for alias files, then, are: # @@ -60,29 +68,40 @@ # "drush rsync" and "drush sql-sync". # # Alias groups (aliases stored together in files called -# GROUPNAME.aliases.yml, as mentioned above) create an implicit -# namespace that is named after the group name. +# GROUPNAME.aliases.yml can be used to collect related sites +# together. # # For example: # # @code -# # File: mysite.aliases.yml -# dev: -# root: /path/to/drupal -# uri: https://dev.example.com -# live: -# root: /other/path/to/drupal -# uri: https://live.example.com +# # File: elements.aliases.yml +# sites: +# earth: +# dev: +# root: /path/to/drupal-earth +# uri: https://dev.earth.com +# live: +# root: /other/path/to/drupal-earth +# uri: https://earth.com +# wind: +# dev: +# root: /path/to/drupal-wind +# uri: https://dev.wind.com +# live: +# root: /other/path/to/drupal-wind +# uri: https://earth.com # @endcode # # Then the following aliases are defined: -# - @mysite.dev -# - @mysite.live +# - @elements.earth.dev +# - @elements.earth.live +# - @elements.wind.dev +# - @elements.wind.live # # To see an example alias definition for the current bootstrapped -# site, use the "site-alias" command with the built-in alias "@self": +# site, use the "site:alias" command with the built-in alias "@self": # -# $ drush site-alias @self +# $ drush site:alias @self # # Drush also supports *remote* site aliases. When a site alias is # defined for a remote site, Drush will use the ssh command to run @@ -90,9 +109,10 @@ # alias looks like this: # # @code +# # File: remote.alias.yml # live: -# remote-host: server.domain.com -# remote-user: www-admin +# host: server.domain.com +# user: www-admin # @endcode # # The form above requires that Drush be installed on the remote machine, @@ -100,16 +120,19 @@ # machine. The remote alias should define the 'root' and 'uri' elements, # as shown in the initial example at the top of this file. # +# TODO: I am not sure the feature above is working in Drush 9. Test and fix. +# # If you do not wish to maintain site aliases on the remote machine, # then you may define an alias that contains all of the elements -# 'remote-host', 'remote-user', 'root' and 'uri'. If you do this, then +# 'host', 'user', 'root' and 'uri'. If you do this, then # Drush will make the remote call using the --root and --uri options # to identify the site, so no site alias is required on the remote server. # # @code +# # File: remote.alias.yml # live: -# remote-host: server.domain.com -# remote-user: www-admin +# host: server.domain.com +# user: www-admin # root: /other/path/to/drupal # uri: http://example.com # @endcode @@ -119,7 +142,7 @@ # # $ drush @none status # -# See `drush help site-alias` for more options for displaying site +# See `drush help site:alias` for more options for displaying site # aliases. See `drush topic docs-bastion` for instructions on configuring # remote access to a Drupal site behind a firewall via a bastion server. # @@ -129,12 +152,12 @@ # - 'uri': The value of --uri should always be the same as # when the site is being accessed from a web browser (e.g. http://example.com) # - 'root': The Drupal root; must not be specified as a relative path. -# - 'remote-host': The fully-qualified domain name of the remote system +# - 'host': The fully-qualified domain name of the remote system # hosting the Drupal instance. **Important Note: The remote-host option # must be omitted for local sites, as this option controls various # operations, such as whether or not rsync parameters are for local or # remote machines, and so on. @see hook_drush_sitealias_alter() in drush.api.php -# - 'remote-user': The username to log in as when using ssh or rsync. +# - 'user': The username to log in as when using ssh or rsync. # - 'os': The operating system of the remote server. Valid values # are 'Windows' and 'Linux'. Be sure to set this value for all remote # aliases because the default value is PHP_OS if 'remote-host' @@ -146,57 +169,49 @@ # standard port, alternative identity file, or alternative # authentication method, ssh-options can contain a string of extra # options that are used with the ssh command, eg "-p 100" -# - 'path-aliases': An array of aliases for common rsync targets. +# - 'paths': An array of aliases for common rsync targets. # Relative aliases are always taken from the Drupal root. -# - '%drush-script': The path to the 'drush' script, or to 'drush.php'. -# This is used by backend invoke when drush -# runs a drush command. The default is 'drush' on remote machines, or -# the full path to drush.php on the local machine. -# - '%drush': A read-only property: points to the folder that the drush -# script is stored in. -# - '%files': Path to 'files' directory. This will be looked up if not +# - 'files': Path to 'files' directory. This will be looked up if not # specified. -# - '%root': A reference to the Drupal root defined in the 'root' item in the -# site alias record. -# - 'php': path to custom php interpreter. -# - 'php-options': commandline options for php interpreter, you may -# want to set this to '-d error_reporting="E_ALL^E_DEPRECATED"' -# - 'command-specific': These options will only be set if the alias +# - 'command': These options will only be set if the alias # is used with the specified command. In the example below, the option # `--no-dump` will be selected whenever the @stage alias # is used in any of the following ways: -# - `drush @stage sql-sync @self @live` -# - `drush sql-sync @stage @live` -# - `drush sql-sync @live @stage` -# - '#peer': Settings that begin with a '#' are not used directly by Drush, and -# in fact are removed before making a backend invoke call (for example). -# These kinds of values are useful in conjunction with shell aliases. See -# `drush topic docs-shell-aliases` for more information on this. -# - '#env-vars': An associative array of keys and values that should be set on -# the remote side before invoking drush. +# - `drush @stage sql-sync @self @live` +# - `drush sql-sync @stage @live` +# - `drush sql-sync @live @stage` +# NOTE: Setting boolean options broke with Symfony 3. This will be fixed +# in a future release. See: # # Altering aliases: # # It is no longer possible to alter aliases dynamically. This is a planned feature. Help wanted. # -# Some examples appear below. Remove the leading hash signs to enable. -#stage: -# uri: http://stage.example.com -# root: /path/to/remote/drupal/root -# remote-host: mystagingserver.myisp.com -# remote-user: publisher -# os: Linux -# path-aliases: -# - %drush-script: path/to/drush/drush -# - %files: sites/mydrupalsite.com/files -# - %custom: /my/custom/path -# command-specific: +# Environment variables: +# +# It is no longer possible to set environment variables from within an alias. +# This is also a planned feature. +# +# An example appears below. Edit to suit and remove the @code / @endcode and +# leading hashes to enable. +# +# @code +# # File: mysite.alias.yml +# stage: +# uri: http://stage.example.com +# root: /path/to/remote/drupal/root +# host: mystagingserver.myisp.com +# user: publisher +# os: Linux +# paths: +# - files: sites/mydrupalsite.com/files +# - custom: /my/custom/path +# command-specific: # sql-sync: # options: # no-dump: true +# dev: +# root: /path/to/docroot +# uri: https://dev.example.com +# @endcode # -#dev: -# root: /path/to/docroot -# uri: https://dev.example.com -# -# \ No newline at end of file From 81a22d92992c79d2b7739b629c098ff352244671 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 15:50:52 -0700 Subject: [PATCH 02/13] Make sure that we continue to search in site-aliases for alias files. --- examples/example.aliases.yml | 4 +-- isolation/.gitignore | 1 + .../drush/site-aliases/elements.aliases.yml | 15 +++++++++++ isolation/tests/ConfigLocatorTest.php | 22 ++++++++++++--- src/Config/ConfigLocator.php | 27 +++++++++++-------- src/Preflight/Preflight.php | 4 +-- 6 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 isolation/fixtures/sites/d8/drush/site-aliases/elements.aliases.yml diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index 6248d89449..b52cf2f194 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -51,7 +51,7 @@ # a. The /drush and /sites/all/drush folders for the current Drupal site # b. The /drush folder in the directory above the current Drupal site # -# These locations are no longer searched recursively; alias files musst +# These locations are no longer searched recursively; alias files must # appear directly inside one of the search locations, or in a directory called # 'site-aliases' in a search location, or it will not be found. # @@ -181,7 +181,7 @@ # - `drush sql-sync @stage @live` # - `drush sql-sync @live @stage` # NOTE: Setting boolean options broke with Symfony 3. This will be fixed -# in a future release. See: +# in a future release. See: https://github.com/drush-ops/drush/issues/2956 # # Altering aliases: # diff --git a/isolation/.gitignore b/isolation/.gitignore index 22d0d82f80..181546de83 100644 --- a/isolation/.gitignore +++ b/isolation/.gitignore @@ -1 +1,2 @@ vendor +build diff --git a/isolation/fixtures/sites/d8/drush/site-aliases/elements.aliases.yml b/isolation/fixtures/sites/d8/drush/site-aliases/elements.aliases.yml new file mode 100644 index 0000000000..c3ae1b29cc --- /dev/null +++ b/isolation/fixtures/sites/d8/drush/site-aliases/elements.aliases.yml @@ -0,0 +1,15 @@ +sites: + earth: + dev: + root: /path/to/drupal-earth + uri: https://dev.earth.com + live: + root: /other/path/to/drupal-earth + uri: https://earth.com + wind: + dev: + root: /path/to/drupal-wind + uri: https://dev.wind.com + live: + root: /other/path/to/drupal-wind + uri: https://earth.com diff --git a/isolation/tests/ConfigLocatorTest.php b/isolation/tests/ConfigLocatorTest.php index 87d8a89706..8cd5a9df0f 100644 --- a/isolation/tests/ConfigLocatorTest.php +++ b/isolation/tests/ConfigLocatorTest.php @@ -26,7 +26,7 @@ function testOnlyEnvironmentData() */ function testLoadAll() { - $configLocator = $this->createConfigLoader(); + $configLocator = $this->createConfigLocator(); $sources = $configLocator->sources(); //$this->assertEquals('environment', $sources['env']['cwd']); @@ -52,7 +52,7 @@ function testLoadAll() */ function testLocalMode() { - $configLocator = $this->createConfigLoader(true); + $configLocator = $this->createConfigLocator(true); /* $sources = $configLocator->sources(); @@ -69,10 +69,26 @@ function testLocalMode() $this->assertEquals('A site-specific setting', $config->get('test.site')); } + function testAliasPaths() + { + $configLocator = $this->createConfigLocator(); + $aliasPaths = $configLocator->getSiteAliasPaths(['/home/user/aliases'], $this->environment()); + $aliasPaths = array_map( + function ($item) { + return str_replace(dirname(__DIR__) . '/', '', $item); + }, + $aliasPaths + ); + sort($aliasPaths); + + $expected = 'fixtures/sites/d8/drush,fixtures/sites/d8/drush/site-aliases'; + $this->assertEquals($expected, implode(',', $aliasPaths)); + } + /** * Create a config locator from All The Sources, for use in multiple tests. */ - protected function createConfigLoader($isLocal = false, $configPath = '', $aliasPath = '', $alias = '') + protected function createConfigLocator($isLocal = false, $configPath = '') { $configLocator = new ConfigLocator(); $configLocator->collectSources(); diff --git a/src/Config/ConfigLocator.php b/src/Config/ConfigLocator.php index 113b01d820..e57ed66374 100644 --- a/src/Config/ConfigLocator.php +++ b/src/Config/ConfigLocator.php @@ -312,7 +312,7 @@ public function addConfigPaths($contextName, $paths) */ protected function addConfigCandidates(ConfigProcessor $processor, ConfigLoaderInterface $loader, $paths, $candidates) { - $configFiles = $this->locateConfigs($paths, $candidates); + $configFiles = $this->identifyCandidates($paths, $candidates); foreach ($configFiles as $configFile) { $processor->extend($loader->load($configFile)); $this->configFilePaths[] = $configFile; @@ -320,29 +320,32 @@ protected function addConfigCandidates(ConfigProcessor $processor, ConfigLoaderI } /** - * Find available configuration files. + * Given a list of paths, and candidates that might exist at each path, + * return all of the candidates that can be found. Candidates may be + * either directories or files. * * @param string[] $paths * @param string[] $candidates * @return string[] paths */ - protected function locateConfigs($paths, $candidates) + protected function identifyCandidates($paths, $candidates) { $configFiles = []; foreach ($paths as $path) { - $configFiles = array_merge($configFiles, $this->locateConfig($path, $candidates)); + $configFiles = array_merge($configFiles, $this->identifyCandidatesAtPath($path, $candidates)); } return $configFiles; } /** - * Search for all config candidate locations at a single path. + * Search for all matching candidate locations at a single path. + * Candidate locations may be either directories or files. * * @param string $path * @param string[] $candidates * @return string[] */ - protected function locateConfig($path, $candidates) + protected function identifyCandidatesAtPath($path, $candidates) { if (!is_dir($path)) { return []; @@ -350,7 +353,7 @@ protected function locateConfig($path, $candidates) $result = []; foreach ($candidates as $candidate) { - $configFile = "$path/$candidate"; + $configFile = empty($candidate) ? $path : "$path/$candidate"; if (file_exists($configFile)) { $result[] = $configFile; } @@ -366,13 +369,16 @@ protected function locateConfig($path, $candidates) * * @return array */ - public function getSiteAliasPaths(PreflightArgsInterface $preflightArgs, Environment $environment) + public function getSiteAliasPaths($paths, Environment $environment) { - $paths = $preflightArgs->aliasPaths(); + // In addition to the paths passed in to us (from --alias-paths + // commandline options), add some site-local locations. foreach ($this->siteRoots as $siteRoot) { $paths[] = $siteRoot . '/drush'; } $paths[] = $this->composerRoot . '/drush'; + $candidates = [ '', 'site-aliases' ]; + $paths = $this->identifyCandidates($paths, $candidates); return $paths; } @@ -384,7 +390,7 @@ public function getSiteAliasPaths(PreflightArgsInterface $preflightArgs, Environ * * @return array */ - public function getCommandFilePaths(PreflightArgsInterface $preflightArgs) + public function getCommandFilePaths($commandPaths) { // Start with the built-in commands. $searchpath = [ @@ -392,7 +398,6 @@ public function getCommandFilePaths(PreflightArgsInterface $preflightArgs) ]; // Commands specified by 'include' option - $commandPaths = $preflightArgs->commandPaths(); foreach ($commandPaths as $commandPath) { if (is_dir($commandPath)) { $searchpath[] = $commandPath; diff --git a/src/Preflight/Preflight.php b/src/Preflight/Preflight.php index c0fb7158c6..c5b82ef88c 100644 --- a/src/Preflight/Preflight.php +++ b/src/Preflight/Preflight.php @@ -219,7 +219,7 @@ protected function doRun($argv) $configLocator->setComposerRoot($this->selectedComposerRoot()); // Look up the locations where alias files may be found. - $paths = $configLocator->getSiteAliasPaths($preflightArgs, $this->environment); + $paths = $configLocator->getSiteAliasPaths($preflightArgs->aliasPaths(), $this->environment); // Configure alias manager. $aliasManager = (new SiteAliasManager())->addSearchLocations($paths); @@ -254,7 +254,7 @@ protected function doRun($argv) // Find all of the available commandfiles, save for those that are // provided by modules in the selected site; those will be added // during bootstrap. - $commandfileSearchpath = $configLocator->getCommandFilePaths($preflightArgs); + $commandfileSearchpath = $configLocator->getCommandFilePaths($preflightArgs->commandPaths()); // Require the Composer autoloader for Drupal (if different) $loader = $this->environment->loadSiteAutoloader($root); From ad27d45a2bd6d3297e2f8ec0371d2d048223ff78 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 16:14:55 -0700 Subject: [PATCH 03/13] Remove out-of-date comment. --- src/Application.php | 70 --------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/src/Application.php b/src/Application.php index c2d004b4b1..f251813e90 100644 --- a/src/Application.php +++ b/src/Application.php @@ -49,76 +49,6 @@ public function __construct($name, $version) { parent::__construct($name, $version); - // TODO: Add all of Drush's global options that are NOT handled - // by PreflightArgs here. - - // - // All legacy global options from drush_get_global_options() in drush.inc: - // - // Options handled by PreflightArgs: - // - // --root / -r - // --include - // --config - // --alias-path - // --local - // --ssh-options : See \Drush\Preflight\Preflight::remapArguments - // - // Global options registered with Symfony: - // - // --remote-host - // --remote-user - // --root / -r - // --uri / -l - // --simulate - // --backend : the value is now ignored. see PreflightArgs. - // --strict - // --debug / -d : equivalent to -vv - // --yes / -y : equivalent to --no-interaction - // --no / -n : equivalent to --no-interaction - // - // Functionality provided by Symfony: - // - // --verbose / -v - // --help - // --quiet - // - // No longer supported - // - // --nocolor Equivalent to --no-ansi - // --search-depth We could just decide the level we will search for aliases - // --show-invoke - // --early Completion handled by standard symfony extension - // --complete-debug - // --interactive If command isn't -n, then it is interactive - // --command-specific Now handled by consolidation/config component - // --php If needed prefix command with PATH=/path/to/php:$PATH. Also see #env_vars in site aliases. - // --php-options - // --pipe - // - // Not handled yet (probably to be implemented, but maybe not all): - // - // --tty - // --exclude - // --choice - // --ignored-modules : see \Drush\Boot\DrupalBoot8::bootstrapDrupalFull - // --no-label - // --label-separator - // --cache-default-class - // --cache-class- - // --confirm-rollback - // --halt-on-error - // --deferred-sanitization - // --remote-os - // --site-list - // --reserve-margin - // --drush-coverage - // - // --site-aliases - // --shell-aliases - // --path-aliases - - $this->getDefinition() ->addOption( new InputOption('--debug', 'd', InputOption::VALUE_NONE, 'Equivalent to -vv') From 6f6f4d8e485f60670653ede22f10ca0818605ce5 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 16:38:05 -0700 Subject: [PATCH 04/13] Move config path options to the 'drush' configuration namespace rather than 'runtime'. --- examples/example.aliases.yml | 8 ++++---- src/Commands/core/SiteCommands.php | 4 ++-- src/Config/Environment.php | 14 +++----------- src/Preflight/PreflightArgs.php | 14 ++++++++------ 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index b52cf2f194..fea2c8d77c 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -32,7 +32,7 @@ # See the --uri option documentation below for hints on setting its value. # # Any option that can be placed on the Drush commandline -# can also appear in an alias definition inside an 'optons' section. +# can also appear in an alias definition inside an 'options' section. # # There are several ways to create alias files. # @@ -44,7 +44,7 @@ # the alias search path. The following locations are examined # for alias files: # -# 1. In any path set in runtime.context in drush.yml +# 1. In any path set in drush.alias-path in drush.yml # or (equivalently) any path passed in via --alias-path=... # on the command line. # 2. In one of the site-specific locations: @@ -65,7 +65,7 @@ # to local and remote Drupal installations. These aliases can be # used in place of a site specification on the command line, and # may also be used in arguments to certain commands such as -# "drush rsync" and "drush sql-sync". +# "drush rsync" and "drush sql:sync". # # Alias groups (aliases stored together in files called # GROUPNAME.aliases.yml can be used to collect related sites @@ -143,7 +143,7 @@ # $ drush @none status # # See `drush help site:alias` for more options for displaying site -# aliases. See `drush topic docs-bastion` for instructions on configuring +# aliases. See `drush topic docs:bastion` for instructions on configuring # remote access to a Drupal site behind a firewall via a bastion server. # # Although most aliases will contain only a few options, a number diff --git a/src/Commands/core/SiteCommands.php b/src/Commands/core/SiteCommands.php index 7de47eb02a..fd258a0358 100644 --- a/src/Commands/core/SiteCommands.php +++ b/src/Commands/core/SiteCommands.php @@ -40,9 +40,9 @@ class SiteCommands extends DrushCommands implements SiteAliasManagerAwareInterfa */ public function siteSet($site = '@none') { - $filename = $this->getConfig()->get('drush.site-file-current'); + $filename = $this->getConfig()->get('runtime.site-file-current'); if ($filename) { - $last_site_filename = $this->getConfig()->get('drush.site-file-previous'); + $last_site_filename = $this->getConfig()->get('runtime.site-file-previous'); if ($site == '-') { if (file_exists($last_site_filename)) { $site = file_get_contents($last_site_filename); diff --git a/src/Config/Environment.php b/src/Config/Environment.php index f98556fe31..bd351e29cf 100644 --- a/src/Config/Environment.php +++ b/src/Config/Environment.php @@ -98,22 +98,12 @@ protected function getUsername() * be added into the ConfigProcessor, where it will become accessible * via the configuration object. * - * So, this seems like a good idea becuase we already have ConfigAwareInterface - * et. al. that makes the config object easily available via dependency - * injection. Instead of this, we could also add the Environment object - * to the DI container and make an EnvironmentAwareInterface & etc. - * - * Not convinced that is better, but this mapping will grow. + * @see PreflightArgs::applyToConfig(), which also exports information to config. * * @return array Nested associative array that is overlayed on configuration. */ public function exportConfigData() { - // TODO: decide how to organize / name this hierarchy. - // i.e. which is better: - // $config->get('drush.base-dir') - // - or - - // $config->get('drush.base.dir') return [ // Information about the environment presented to Drush 'env' => [ @@ -135,6 +125,8 @@ public function exportConfigData() 'user-dir' => $this->userConfigPath(), 'system-dir' => $this->systemConfigPath(), 'system-command-dir' => $this->systemCommandFilePath(), + ], + 'runtime' => [ 'site-file-previous' => $this->getSiteSetAliasFilePath('drush-drupal-prev-site-'), 'site-file-current' => $this->getSiteSetAliasFilePath(), ], diff --git a/src/Preflight/PreflightArgs.php b/src/Preflight/PreflightArgs.php index d01c3d6fc7..1d3d59db66 100644 --- a/src/Preflight/PreflightArgs.php +++ b/src/Preflight/PreflightArgs.php @@ -23,7 +23,7 @@ class PreflightArgs extends Config implements PreflightArgsInterface */ protected $args; - const DRUSH_CONFIG_CONTEXT_NAMESPACE = 'runtime.context'; + const DRUSH_CONFIG_PATH_NAMESPACE = 'drush'; const ALIAS = 'alias'; const ALIAS_PATH = 'alias-path'; const COMMAND_PATH = 'include'; @@ -90,8 +90,8 @@ protected function optionConfigMap() { return [ self::SIMULATE => \Robo\Config\Config::SIMULATE, - self::BACKEND => self::BACKEND, - self::LOCAL => self::DRUSH_CONFIG_CONTEXT_NAMESPACE . '.' . self::LOCAL, + self::BACKEND => 'runtime.' . self::BACKEND, + self::LOCAL => 'runtime.' . self::LOCAL, ]; } @@ -102,14 +102,16 @@ protected function optionConfigMap() protected function optionConfigPathMap() { return [ - self::ALIAS_PATH => self::DRUSH_CONFIG_CONTEXT_NAMESPACE . '.' . self::ALIAS_PATH, - self::CONFIG_PATH => self::DRUSH_CONFIG_CONTEXT_NAMESPACE . '.' . self::CONFIG_PATH, - self::COMMAND_PATH => self::DRUSH_CONFIG_CONTEXT_NAMESPACE . '.' . self::COMMAND_PATH, + self::ALIAS_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::ALIAS_PATH, + self::CONFIG_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::CONFIG_PATH, + self::COMMAND_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::COMMAND_PATH, ]; } /** * @inheritdoc + * + * @see Environment::exportConfigData(), which also exports information to config. */ public function applyToConfig(ConfigInterface $config) { From 021e7be7c87bcba0835b93bfd07d5bec4e3f0fb7 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 18:27:03 -0700 Subject: [PATCH 05/13] Fix evaluation of ${env.home} and similar expressions in config processing, and update alias file documentation to match. --- examples/example.aliases.yml | 49 ++++++++++++++------------------- src/Config/ConfigLocator.php | 5 +++- src/Preflight/Preflight.php | 7 +++-- src/Preflight/PreflightArgs.php | 2 +- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index fea2c8d77c..97b3865889 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -61,11 +61,21 @@ # $ROOT/sites/all/drush/site-aliases # $ROOT/../drush/site-aliases # -# Files stored in these locations can be used to create aliases -# to local and remote Drupal installations. These aliases can be -# used in place of a site specification on the command line, and -# may also be used in arguments to certain commands such as -# "drush rsync" and "drush sql:sync". +# If you would like to add additional locations, you can do so by +# listing additional locations in your configuraton files. For example, +# to re-add the default user alias path from Drush 8, put the following +# in your ~/.drush/drush.yml configuration file: +# +# @code +# drush: +# alias-path: '${env.home}/.drush/site-aliases' +# @endcode +# +# Files stored in one of the search path locations can be used to create +# aliases to local and remote Drupal installations. These aliases can be +# used in place of a site specification on the command line, and may also +# be used in arguments to certain commands such as "drush rsync" and +# "drush sql:sync". # # Alias groups (aliases stored together in files called # GROUPNAME.aliases.yml can be used to collect related sites @@ -113,26 +123,6 @@ # live: # host: server.domain.com # user: www-admin -# @endcode -# -# The form above requires that Drush be installed on the remote machine, -# and that there also be an alias of the same name defined on that -# machine. The remote alias should define the 'root' and 'uri' elements, -# as shown in the initial example at the top of this file. -# -# TODO: I am not sure the feature above is working in Drush 9. Test and fix. -# -# If you do not wish to maintain site aliases on the remote machine, -# then you may define an alias that contains all of the elements -# 'host', 'user', 'root' and 'uri'. If you do this, then -# Drush will make the remote call using the --root and --uri options -# to identify the site, so no site alias is required on the remote server. -# -# @code -# # File: remote.alias.yml -# live: -# host: server.domain.com -# user: www-admin # root: /other/path/to/drupal # uri: http://example.com # @endcode @@ -206,10 +196,11 @@ # paths: # - files: sites/mydrupalsite.com/files # - custom: /my/custom/path -# command-specific: -# sql-sync: -# options: -# no-dump: true +# command: +# sql: +# sync: +# options: +# no-dump: true # dev: # root: /path/to/docroot # uri: https://dev.example.com diff --git a/src/Config/ConfigLocator.php b/src/Config/ConfigLocator.php index e57ed66374..7dc588284d 100644 --- a/src/Config/ConfigLocator.php +++ b/src/Config/ConfigLocator.php @@ -291,12 +291,15 @@ public function addConfigPaths($contextName, $paths) 'config/drush.yml', ]; + // Make all of the config values parsed so far available in evaluations + $reference = $this->config()->export(); + $processor = new ConfigProcessor(); $context = $this->config->getContext($contextName); $processor->add($context->export()); $this->addConfigCandidates($processor, $loader, $paths, $candidates); $this->addToSources($processor->sources()); - $context->import($processor->export()); + $context->import($processor->export($reference)); $this->config->addContext($contextName, $context); return $this; diff --git a/src/Preflight/Preflight.php b/src/Preflight/Preflight.php index c5b82ef88c..b42eaf4959 100644 --- a/src/Preflight/Preflight.php +++ b/src/Preflight/Preflight.php @@ -135,13 +135,14 @@ public function prepareConfig(PreflightArgs $preflightArgs, Environment $environ // Load configuration and aliases from defined global locations // where such things are found. $configLocator = new ConfigLocator(); - $configLocator->setLocal($preflightArgs->isLocal()); - $configLocator->addUserConfig($preflightArgs->configPaths(), $environment->systemConfigPath(), $environment->userConfigPath()); - $configLocator->addDrushConfig($environment->drushBasePath()); // Make our environment settings available as configuration items $configLocator->addEnvironment($environment); + $configLocator->setLocal($preflightArgs->isLocal()); + $configLocator->addUserConfig($preflightArgs->configPaths(), $environment->systemConfigPath(), $environment->userConfigPath()); + $configLocator->addDrushConfig($environment->drushBasePath()); + return $configLocator; } diff --git a/src/Preflight/PreflightArgs.php b/src/Preflight/PreflightArgs.php index 1d3d59db66..b517b42595 100644 --- a/src/Preflight/PreflightArgs.php +++ b/src/Preflight/PreflightArgs.php @@ -122,7 +122,7 @@ public function applyToConfig(ConfigInterface $config) // Merging as they are lists. foreach ($this->optionConfigPathMap() as $option_key => $config_key) { $cli_paths = $this->get($option_key, []); - $config_paths = $config->get($config_key, []); + $config_paths = (array) $config->get($config_key, []); $merged_paths = array_merge($cli_paths, $config_paths); $config->set($config_key, $merged_paths); $this->set($option_key, $merged_paths); From 79d3692dd88465e0d81a67299b58cb1e8c0e8e5c Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 21:42:09 -0700 Subject: [PATCH 06/13] Also read environment variables to provide arbitrary configuration values. --- composer.json | 2 +- isolation/tests/ConfigLocatorTest.php | 4 ++-- src/Config/ConfigLocator.php | 11 +++++++++-- src/Preflight/Preflight.php | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 722952e7ae..d2e221b88a 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "psr/log": "~1.0", "psy/psysh": "~0.6", "league/container": "~2", - "consolidation/config": "dev-master", + "consolidation/config": "dev-env-config as 1.0.1", "consolidation/robo": "^1.1.2", "symfony/config": "~2.2|^3", "chi-teck/drupal-code-generator": "^1.17.3", diff --git a/isolation/tests/ConfigLocatorTest.php b/isolation/tests/ConfigLocatorTest.php index 8cd5a9df0f..1bd964e0c6 100644 --- a/isolation/tests/ConfigLocatorTest.php +++ b/isolation/tests/ConfigLocatorTest.php @@ -15,7 +15,7 @@ class ConfigLocatorTest extends TestCase */ function testOnlyEnvironmentData() { - $configLocator = new ConfigLocator(); + $configLocator = new ConfigLocator('TEST_'); $configLocator->addEnvironment($this->environment()); $config = $configLocator->config(); $this->assertEquals($this->homeDir(), $config->get('env.cwd')); @@ -90,7 +90,7 @@ function ($item) { */ protected function createConfigLocator($isLocal = false, $configPath = '') { - $configLocator = new ConfigLocator(); + $configLocator = new ConfigLocator('TEST_'); $configLocator->collectSources(); $configLocator->setLocal($isLocal); $configLocator->addUserConfig([$configPath], $this->environment()->systemConfigPath(), $this->environment()->userConfigPath()); diff --git a/src/Config/ConfigLocator.php b/src/Config/ConfigLocator.php index 7dc588284d..3d6267f470 100644 --- a/src/Config/ConfigLocator.php +++ b/src/Config/ConfigLocator.php @@ -5,6 +5,8 @@ use Consolidation\Config\Loader\YamlConfigLoader; use Consolidation\Config\Loader\ConfigProcessor; use Consolidation\Config\Util\ConfigOverlay; +use Consolidation\Config\Util\EnvConfig; + use Drush\Preflight\PreflightArgsInterface; /** @@ -69,7 +71,7 @@ class ConfigLocator */ // 'process' context is provided by ConfigOverlay - const ENVIRONMENT_CONTEXT = 'environment'; // new context + const ENVIRONMENT_CONTEXT = 'environment'; // This is more of a 'runtime' context const PREFLIGHT_CONTEXT = 'cli'; // 'stdin' context not implemented // 'specific' context obsolete; command-specific options handled differently by annotated command library @@ -80,6 +82,7 @@ class ConfigLocator const USER_CONTEXT = 'user'; // home.drush is obsolete (loaded in USER_CONTEXT) // system context is obsolect (loaded in USER_CONTEXT - note priority change) + const ENV_CONTEXT = 'env'; const DRUSH_CONTEXT = 'drush'; // 'default' context is provided by ConfigOverlay @@ -87,13 +90,17 @@ class ConfigLocator /** * ConfigLocator constructor */ - public function __construct() + public function __construct($envPrefix = '') { $this->config = new ConfigOverlay(); // Add placeholders to establish priority. We add // contexts from lowest to highest priority. $this->config->addPlaceholder(self::DRUSH_CONTEXT); + if (!empty($envPrefix)) { + $envConfig = new EnvConfig($envPrefix); + $this->config->addContext(self::ENV_CONTEXT, $envConfig); + } $this->config->addPlaceholder(self::USER_CONTEXT); $this->config->addPlaceholder(self::DRUPAL_CONTEXT); $this->config->addPlaceholder(self::SITE_CONTEXT); // not implemented yet (multisite) diff --git a/src/Preflight/Preflight.php b/src/Preflight/Preflight.php index b42eaf4959..0ae7b22c4b 100644 --- a/src/Preflight/Preflight.php +++ b/src/Preflight/Preflight.php @@ -134,7 +134,7 @@ public function prepareConfig(PreflightArgs $preflightArgs, Environment $environ { // Load configuration and aliases from defined global locations // where such things are found. - $configLocator = new ConfigLocator(); + $configLocator = new ConfigLocator('DRUSH_'); // Make our environment settings available as configuration items $configLocator->addEnvironment($environment); From 19000a827f91d1ed8014ab7d68e2c9b19280032c Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Wed, 4 Oct 2017 21:59:44 -0700 Subject: [PATCH 07/13] Back to stable releases in composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d2e221b88a..1019e6bd30 100644 --- a/composer.json +++ b/composer.json @@ -28,11 +28,11 @@ "psr/log": "~1.0", "psy/psysh": "~0.6", "league/container": "~2", - "consolidation/config": "dev-env-config as 1.0.1", + "consolidation/config": "^1.0.3", "consolidation/robo": "^1.1.2", "symfony/config": "~2.2|^3", "chi-teck/drupal-code-generator": "^1.17.3", - "consolidation/annotated-command": "dev-master as 2.7.0", + "consolidation/annotated-command": "^2.7.1", "consolidation/output-formatters": "^3.1.11", "grasmash/yaml-expander": "^1.1.1", "symfony/yaml": "~2.3|^3", From 3bb3a924f42c6529abbf3a0ce9456b3d0f4c5c8f Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 05:52:58 -0700 Subject: [PATCH 08/13] Fix up drush.paths config namespace. --- includes/backend.inc | 3 ++- src/Preflight/PreflightArgs.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/backend.inc b/includes/backend.inc index 300d508ec5..47ad15492d 100644 --- a/includes/backend.inc +++ b/includes/backend.inc @@ -744,7 +744,8 @@ function drush_backend_invoke_concurrent($invocations, $common_options = array() // $command_options += drush_command_get_command_specific_options($site_record, $command); // Add in preflight option contexts (--include et. al) - $preflightContextOptions = \Drush\Drush::config()->get(PreflightArgs::DRUSH_CONFIG_CONTEXT_NAMESPACE, []); + $preflightContextOptions = \Drush\Drush::config()->get(PreflightArgs::DRUSH_CONFIG_PATH_NAMESPACE, []); + $preflightContextOptions['local'] = \Drush\Drush::config()->get('runtime.local', false); foreach ($preflightContextOptions as $key => $value) { if ($value) { $command_options[$key] = $value; diff --git a/src/Preflight/PreflightArgs.php b/src/Preflight/PreflightArgs.php index b517b42595..d8d3904e9c 100644 --- a/src/Preflight/PreflightArgs.php +++ b/src/Preflight/PreflightArgs.php @@ -23,7 +23,7 @@ class PreflightArgs extends Config implements PreflightArgsInterface */ protected $args; - const DRUSH_CONFIG_PATH_NAMESPACE = 'drush'; + const DRUSH_CONFIG_PATH_NAMESPACE = 'drush.paths'; const ALIAS = 'alias'; const ALIAS_PATH = 'alias-path'; const COMMAND_PATH = 'include'; From 3d8800c3a4ee26156fe01798b7a688b64285d043 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 07:09:12 -0700 Subject: [PATCH 09/13] Add /etc/drush to example legacy alias paths in alias documentation. --- examples/example.aliases.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index 97b3865889..bcb1137855 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -68,7 +68,9 @@ # # @code # drush: -# alias-path: '${env.home}/.drush/site-aliases' +# alias-path: +# - '${env.home}/.drush/site-aliases' +# - /etc/drush # @endcode # # Files stored in one of the search path locations can be used to create From 2c7df53d103f89713a718fbc1176b8b6de37c51d Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 08:58:28 -0700 Subject: [PATCH 10/13] Reorganize configuration contexts a bit; fix backend mode. --- examples/example.aliases.yml | 7 ++++--- includes/backend.inc | 4 +++- src/Drush.php | 4 ++-- src/Preflight/PreflightArgs.php | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/example.aliases.yml b/examples/example.aliases.yml index bcb1137855..25fc9dc7ea 100644 --- a/examples/example.aliases.yml +++ b/examples/example.aliases.yml @@ -68,9 +68,10 @@ # # @code # drush: -# alias-path: -# - '${env.home}/.drush/site-aliases' -# - /etc/drush +# paths: +# alias-path: +# - '${env.home}/.drush/site-aliases' +# - /etc/drush # @endcode # # Files stored in one of the search path locations can be used to create diff --git a/includes/backend.inc b/includes/backend.inc index 47ad15492d..413d9d6a04 100644 --- a/includes/backend.inc +++ b/includes/backend.inc @@ -744,7 +744,9 @@ function drush_backend_invoke_concurrent($invocations, $common_options = array() // $command_options += drush_command_get_command_specific_options($site_record, $command); // Add in preflight option contexts (--include et. al) - $preflightContextOptions = \Drush\Drush::config()->get(PreflightArgs::DRUSH_CONFIG_PATH_NAMESPACE, []); + $preflightContextOptions = + \Drush\Drush::config()->get(PreflightArgs::DRUSH_RUNTIME_CONTEXT_NAMESPACE, []) + + \Drush\Drush::config()->get(PreflightArgs::DRUSH_CONFIG_PATH_NAMESPACE, []); $preflightContextOptions['local'] = \Drush\Drush::config()->get('runtime.local', false); foreach ($preflightContextOptions as $key => $value) { if ($value) { diff --git a/src/Drush.php b/src/Drush.php index 9d2957ba3c..1b8e6dcd7f 100644 --- a/src/Drush.php +++ b/src/Drush.php @@ -276,7 +276,7 @@ public static function simulate() */ public static function backend() { - return \Drush\Drush::config()->get('backend'); + return \Drush\Drush::config()->get(\Drush\Preflight\PreflightArgs::BACKEND); } /** @@ -335,7 +335,7 @@ public static function redispatchOptions($input = null) $options = array_intersect_key($options, array_flip($optionNamesFromCommandline)); // Add in the 'runtime.context' items, which includes --include, --alias-path et. al. - return $options + array_filter(static::config()->get('runtime.context')); + return $options + array_filter(static::config()->get(\Drush\Preflight\PreflightArgs::DRUSH_RUNTIME_CONTEXT_NAMESPACE)); } /** diff --git a/src/Preflight/PreflightArgs.php b/src/Preflight/PreflightArgs.php index d8d3904e9c..1a2f339a91 100644 --- a/src/Preflight/PreflightArgs.php +++ b/src/Preflight/PreflightArgs.php @@ -24,6 +24,7 @@ class PreflightArgs extends Config implements PreflightArgsInterface protected $args; const DRUSH_CONFIG_PATH_NAMESPACE = 'drush.paths'; + const DRUSH_RUNTIME_CONTEXT_NAMESPACE = 'runtime.contxt'; const ALIAS = 'alias'; const ALIAS_PATH = 'alias-path'; const COMMAND_PATH = 'include'; @@ -90,8 +91,8 @@ protected function optionConfigMap() { return [ self::SIMULATE => \Robo\Config\Config::SIMULATE, - self::BACKEND => 'runtime.' . self::BACKEND, - self::LOCAL => 'runtime.' . self::LOCAL, + self::BACKEND => self::BACKEND, + self::LOCAL => self::DRUSH_RUNTIME_CONTEXT_NAMESPACE . '.' . self::LOCAL, ]; } From a086d6f9a29e7a8cd55d93c776d0ce32544dde2d Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 09:21:04 -0700 Subject: [PATCH 11/13] Code style. --- src/Cache/JSONCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cache/JSONCache.php b/src/Cache/JSONCache.php index 8e6db7b86c..0e44948839 100644 --- a/src/Cache/JSONCache.php +++ b/src/Cache/JSONCache.php @@ -17,7 +17,7 @@ class JSONCache extends FileCache public function readFile($filename) { $item = file_get_contents($filename); - return $item ? (object)json_decode($item, TRUE) : false; + return $item ? (object)json_decode($item, true) : false; } public function writeFile($filename, $cache) From 463674660e07291f2fe2190bbc458a4236a041b4 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 10:36:56 -0700 Subject: [PATCH 12/13] Fine-tune alias path handling. --- tests/UnishTestCase.php | 4 ++-- tests/rsyncTest.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/UnishTestCase.php b/tests/UnishTestCase.php index d08fdbd16b..379ce81a1c 100644 --- a/tests/UnishTestCase.php +++ b/tests/UnishTestCase.php @@ -13,7 +13,7 @@ abstract class UnishTestCase extends \PHPUnit_Framework_TestCase { * @var array */ private static $sites = array(); - + private static $sandbox; private static $drush; @@ -534,7 +534,7 @@ function writeSiteAliases($sites) { } $etc = self::getSandbox() . '/etc/drush'; file_put_contents(Path::join($etc, 'unish.alias.yml'), Yaml::dump($groups)); - $config['runtime']['context']['alias-path'][] = $etc; + $config['drush']['paths']['alias-path'][] = $etc; file_put_contents(Path::join($etc, 'drush.yml'), Yaml::dump($config, 3)); } diff --git a/tests/rsyncTest.php b/tests/rsyncTest.php index 5b0c4cd61f..6da7087d9b 100644 --- a/tests/rsyncTest.php +++ b/tests/rsyncTest.php @@ -25,7 +25,7 @@ public function testSimulated() { // Test simulated backend invoke $this->drush('rsync', ['@example.dev', '@example.stage'], $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); - $expected = "Simulating backend invoke: ssh -o PasswordAuthentication=no user@server 'drush --alias-path=__DIR__/resources/alias-fixtures --root=/path/to/drupal --uri=sitename --no-ansi rsync '\''@example.dev'\'' '\''@example.stage'\'' 2>&1' 2>&1"; + $expected = "Simulating backend invoke: ssh -o PasswordAuthentication=no user@server 'drush --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --root=/path/to/drupal --uri=sitename --no-ansi rsync '\''@example.dev'\'' '\''@example.stage'\'' 2>&1' 2>&1"; $this->assertOutputEquals($expected); // Test simulated simple rsync with two local sites @@ -94,6 +94,7 @@ protected function assertOutputEquals($expected) $output = preg_replace('# *#', ' ', $output); // Get rid of any full paths in the output $output = str_replace(__DIR__, '__DIR__', $output); + $output = str_replace(self::getSandbox(), '__SANDBOX__', $output); $this->assertEquals($expected, $output); } From 7bfc463012df4992ed39b74e63d613d3ef9b43d0 Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Thu, 5 Oct 2017 10:58:27 -0700 Subject: [PATCH 13/13] Fix up rsync test --- tests/rsyncTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rsyncTest.php b/tests/rsyncTest.php index 6da7087d9b..0784236792 100644 --- a/tests/rsyncTest.php +++ b/tests/rsyncTest.php @@ -25,7 +25,7 @@ public function testSimulated() { // Test simulated backend invoke $this->drush('rsync', ['@example.dev', '@example.stage'], $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); - $expected = "Simulating backend invoke: ssh -o PasswordAuthentication=no user@server 'drush --alias-path=__DIR__/resources/alias-fixtures:__SANDBOX__/etc/drush --root=/path/to/drupal --uri=sitename --no-ansi rsync '\''@example.dev'\'' '\''@example.stage'\'' 2>&1' 2>&1"; + $expected = "Simulating backend invoke: ssh -o PasswordAuthentication=no user@server 'drush --alias-path=__DIR__/resources/alias-fixtures --root=/path/to/drupal --uri=sitename --no-ansi rsync '\''@example.dev'\'' '\''@example.stage'\'' 2>&1' 2>&1"; $this->assertOutputEquals($expected); // Test simulated simple rsync with two local sites