2 namespace Drush\Commands\config;
4 use Consolidation\AnnotatedCommand\CommandData;
5 use Drush\Commands\DrushCommands;
7 use Consolidation\SiteAlias\HostPath;
8 use Consolidation\SiteAlias\SiteAliasManagerAwareInterface;
9 use Consolidation\SiteAlias\SiteAliasManagerAwareTrait;
11 class ConfigPullCommands extends DrushCommands implements SiteAliasManagerAwareInterface
13 use SiteAliasManagerAwareTrait;
16 * Export and transfer config from one environment to another.
18 * @command config:pull
19 * @param string $source A site-alias or the name of a subdirectory within /sites whose config you want to copy from.
20 * @param string $destination A site-alias or the name of a subdirectory within /sites whose config you want to replace.
21 * @option safe Validate that there are no git uncommitted changes before proceeding
22 * @option label A config directory label (i.e. a key in \$config_directories array in settings.php). Defaults to 'sync'
23 * @option runner Where to run the rsync command; defaults to the local site. Can also be 'source' or 'destination'
24 * @usage drush config:pull @prod @stage
25 * Export config from @prod and transfer to @stage.
26 * @usage drush config:pull @prod @self --label=vcs
27 * Export config from @prod and transfer to the 'vcs' config directory of current site.
28 * @usage drush config:pull @prod @self:../config/sync
29 * Export config to a custom directory. Relative paths are calculated from Drupal root.
30 * @aliases cpull,config-pull
31 * @topics docs:aliases,docs:config-exporting
34 public function pull($source, $destination, $options = ['safe' => false, 'label' => 'sync', 'runner' => null])
36 $global_options = Drush::redispatchOptions() + ['strict' => 0];
38 // @todo If either call is made interactive, we don't get an $return['object'] back.
39 $backend_options = ['interactive' => false];
40 if (Drush::simulate()) {
41 $backend_options['backend-simulate'] = true;
45 // Use the standard backup directory on Destination.
46 'destination' => true,
49 $this->logger()->notice(dt('Starting to export configuration on Target.'));
50 $return = drush_invoke_process($source, 'config-export', [], $global_options + $export_options, $backend_options);
51 if ($return['error_status']) {
52 throw new \Exception(dt('Config-export failed.'));
54 // Trailing slash assures that we transfer files and not the containing dir.
55 $export_path = $return['object'] . '/';
59 '--remove-source-files',
61 '--exclude=.htaccess',
63 if (strpos($destination, ':') === false) {
64 $destination .= ':%config-' . $options['label'];
66 $destinationHostPath = HostPath::create($this->siteAliasManager(), $destination);
68 if (!$runner = $options['runner']) {
69 $sourceRecord = $this->siteAliasManager()->get($source);
70 $destinationRecord = $destinationHostPath->getAliasRecord();
71 $runner = $sourceRecord->isRemote() && $destinationRecord->isRemote() ? $destinationRecord : '@self';
74 ->notice(dt('Starting to rsync configuration files from !source to !dest.', [
76 '!dest' => $destinationHostPath->getOriginal(),
78 // This comment applies similarly to sql-sync's use of core-rsync.
79 // Since core-rsync is a strict-handling command and drush_invoke_process() puts options at end, we can't send along cli options to rsync.
80 // Alternatively, add options like ssh.options to a site alias (usually on the machine that initiates the sql-sync).
81 $return = drush_invoke_process($runner, 'core-rsync', array_merge([
82 "$source:$export_path",
83 $destinationHostPath->getOriginal(),
85 ], $rsync_options), ['yes' => true], $backend_options);
86 if ($return['error_status']) {
87 throw new \Exception(dt('Config-pull rsync failed.'));
90 drush_backend_set_result($return['object']);
94 * @hook validate config-pull
96 public function validateConfigPull(CommandData $commandData)
98 if ($commandData->input()->getOption('safe')) {
99 $return = drush_invoke_process($commandData->input()
100 ->getArgument('destination'), 'core-execute', ['git diff --quiet'], ['escape' => 0]);
101 if ($return['error_status']) {
102 throw new \Exception('There are uncommitted changes in your git working copy.');