From 729cd02e0c8bd0d7e7729864f80eb694540e483f Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Thu, 21 Sep 2017 09:53:13 +0800 Subject: [PATCH 1/6] Allow using it with drush 9.x. --- drush_cmi_tools.drush.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drush_cmi_tools.drush.inc b/drush_cmi_tools.drush.inc index baf5d7b..990ab26 100644 --- a/drush_cmi_tools.drush.inc +++ b/drush_cmi_tools.drush.inc @@ -9,7 +9,7 @@ use Drupal\Component\Serialization\Yaml; use Drupal\config\StorageReplaceDataWrapper; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\StorageComparer; -use Drush\Config\StorageWrapper; +use Drush\Drupal\Commands\config\ConfigCommands; use Drush\Log\LogLevel; /** @@ -103,7 +103,7 @@ function drush_drush_cmi_tools_config_export_plus($destination = NULL) { } } - $result = _drush_config_export($destination, $destination_dir, FALSE); + $result = \Drupal::service('config.export.commands')->doExport($destination, $destination_dir, FALSE); $file_service = \Drupal::service('file_system'); foreach ($patterns as $pattern) { foreach (file_scan_directory($destination_dir, $pattern) as $file_url => $file) { @@ -194,7 +194,7 @@ function drush_drush_cmi_tools_config_import_plus($destination = NULL) { foreach ($storage_comparer->getAllCollectionNames() as $collection) { $change_list[$collection] = $storage_comparer->getChangelist(NULL, $collection); } - _drush_print_config_changes_table($change_list); + ConfigCommands::configChangesTablePrint($change_list); } else { // Copy active storage to the temporary directory. @@ -212,6 +212,6 @@ function drush_drush_cmi_tools_config_import_plus($destination = NULL) { } if (drush_confirm(dt('Import the listed configuration changes?'))) { - return drush_op('_drush_config_import', $storage_comparer); + \Drupal::service('config.import.commands')->doImport($storage_comparer); } } From eacbb4cfa04f48cf6eeb37f74c38137ebbcd7e30 Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Wed, 14 Mar 2018 15:00:51 +0800 Subject: [PATCH 2/6] Updated to Drush 9 and converted to a module. --- composer.json | 13 +- drush.services.yml | 5 + drush_cmi_tools.info.yml | 5 + src/Commands/DrushCmiToolsCommands.php | 206 +++++++++++++++++++++++++ 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 drush.services.yml create mode 100644 drush_cmi_tools.info.yml create mode 100644 src/Commands/DrushCmiToolsCommands.php diff --git a/composer.json b/composer.json index 228a275..ad1aa36 100644 --- a/composer.json +++ b/composer.json @@ -11,5 +11,14 @@ "issues": "https://github.com/previousnext/drush_cmi_tools/issues", "source": "https://github.com/previousnext/drush_cmi_tools" }, - "require": {} -} \ No newline at end of file + "require": { + "php": ">=5.6.0" + }, + "extra": { + "drush": { + "services": { + "drush.services.yml": "^9" + } + } + } +} diff --git a/drush.services.yml b/drush.services.yml new file mode 100644 index 0000000..be5edd5 --- /dev/null +++ b/drush.services.yml @@ -0,0 +1,5 @@ +services: + drush_cmi_tools.commands: + class: \Drupal\drush_cmi_tools\Commands\DrushCmiToolsCommands + tags: + - { name: drush.command } diff --git a/drush_cmi_tools.info.yml b/drush_cmi_tools.info.yml new file mode 100644 index 0000000..5b82c43 --- /dev/null +++ b/drush_cmi_tools.info.yml @@ -0,0 +1,5 @@ +type: module +name: 'Drush CMI Tools' +description: 'Provides custom export/import commands.' +core: 8.x +package: 'Drush' diff --git a/src/Commands/DrushCmiToolsCommands.php b/src/Commands/DrushCmiToolsCommands.php new file mode 100644 index 0000000..c824541 --- /dev/null +++ b/src/Commands/DrushCmiToolsCommands.php @@ -0,0 +1,206 @@ + null, 'ignore-list' => null]) { + $this->logger()->debug(dt('Starting export')); + + // Do the actual config export operation + // Determine which target directory to use. + if (($target = $options['destination']) && $target !== TRUE) { + $destination_dir = $target; + // It is important to be able to specify a destination directory that + // does not exist yet, for exporting on remote systems + drush_mkdir($destination_dir); + } else { + $this->logger()->error((dt('You must provide a --destination option'))); + return; + } + $patterns = []; + if ($ignore_list = drush_get_option('ignore-list')) { + if (!is_file($ignore_list)) { + $this->logger()->error(dt('The file specified in --ignore-list option does not exist.')); + return; + } + if ($string = file_get_contents($ignore_list)) { + $ignore_list_error = FALSE; + $parsed = FALSE; + try { + $parsed = Yaml::decode($string); + } + catch (InvalidDataTypeException $e) { + $ignore_list_error = TRUE; + } + if (!isset($parsed['ignore']) || !is_array($parsed['ignore'])) { + $ignore_list_error = TRUE; + } + if ($ignore_list_error) { + $this->logger()->error(dt('The file specified in --ignore-list option is in the wrong format. It must be valid YAML with a top-level ignore key.')); + return; + } + foreach ($parsed['ignore'] as $ignore) { + // Allow for accidental .yml extension. + if (substr($ignore, -4) === '.yml') { + $ignore = substr($ignore, 0, -4); + } + $patterns[] = '/^' . str_replace('\*', '(.*)', preg_quote($ignore)) . '\.yml/'; + } + } + } + + $drushExportOptions = [ + 'diff' => FALSE, + + ]; + $result = \Drupal::service('config.export.commands')->doExport($drushExportOptions, $destination_dir, FALSE); + $file_service = \Drupal::service('file_system'); + foreach ($patterns as $pattern) { + foreach (file_scan_directory($destination_dir, $pattern) as $file_url => $file) { + $file_service->unlink($file_url); + $this->logger()->info("Removed $file_url according to ignore list."); + } + } + + return $result; + } + + /** + * Import config from a config directory resepecting live content and a delete list. + * + * @param array $options An associative array of options whose values come from cli, aliases, config, etc. + * @option preview + * Format for displaying proposed changes. Recognized values: list, diff. Defaults to list. + * @option source + * An arbitrary directory that holds the configuration files. + * @option delete-list + * Path to YAML file containing config to delete before importing. Useful when you need to remove items from active config store before importing. + * @option install + * Directory that holds the files to import once only. + * @usage drush config-import-plus --delete-list=./config-delete.yml --install=/some/install/folder --source=/some/export/folder + * Import configuration; do not enable or disable the devel module, regardless of whether or not it appears in the imported list of enabled modules. + * @validate-module-enabled config + * + * @command config:import-plus + * @aliases cimy,config-import-plus + */ + public function importPlus(array $options = ['preview' => null, 'source' => null, 'delete-list' => null, 'install' => null]) { + $this->logger()->debug(dt('Starting import')); + // Determine source directory. + if ($target = $options['source']) { + $source_dir = $target; + } + else { + $this->logger()->error(dt('You must provide a --source option')); + return; + } + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $source_storage = new StorageReplaceDataWrapper($active_storage); + $file_storage = new FileStorage($source_dir); + foreach ($file_storage->listAll() as $name) { + $data = $file_storage->read($name); + $source_storage->replaceData($name, $data); + } + if ($delete_list = drush_get_option('delete-list')) { + if (!is_file($delete_list)) { + $this->logger()->error(dt('The file specified in --delete-list option does not exist.')); + return; + } + if ($string = file_get_contents($delete_list)) { + $delete_list_error = FALSE; + $parsed = FALSE; + try { + $parsed = Yaml::decode($string); + } + catch (InvalidDataTypeException $e) { + $delete_list_error = TRUE; + } + if (!isset($parsed['delete']) || !is_array($parsed['delete'])) { + $delete_list_error = TRUE; + } + if ($delete_list_error) { + $this->logger()->error(dt('The file specified in --delete-list option is in the wrong format. It must be valid YAML with a top-level delete key.')); + return; + } + foreach ($parsed['delete'] as $delete) { + // Allow for accidental .yml extension. + if (substr($delete, -4) === '.yml') { + $delete = substr($delete, 0, -4); + } + if ($source_storage->exists($delete)) { + $source_storage->delete($delete); + $this->logger()->info("Deleted $delete as per delete list."); + } + else { + $this->logger()->info("Ignored deleting $delete, does not exist."); + } + } + } + } + if ($install = drush_get_option('install')) { + $file_storage = new FileStorage($install); + foreach ($file_storage->listAll() as $name) { + if (!$source_storage->exists($name)) { + $data = $file_storage->read($name); + $source_storage->replaceData($name, $data); + $this->logger()->info("Installed $name for first time."); + } + } + } + + /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */ + $config_manager = \Drupal::service('config.manager'); + $storage_comparer = new StorageComparer($source_storage, $active_storage, $config_manager); + + + if (!$storage_comparer->createChangelist()->hasChanges()) { + $this->logger()->info(dt('There are no changes to import.')); + return; + } + + // Copy active storage to the temporary directory. + $temp_dir = drush_tempdir(); + $temp_storage = new FileStorage($temp_dir); + $source_dir_storage = new FileStorage($source_dir); + foreach ($source_dir_storage->listAll() as $name) { + if ($data = $active_storage->read($name)) { + $temp_storage->write($name, $data); + } + } + drush_shell_exec('diff -x %s -u %s %s', '*.git', $temp_dir, $source_dir); + $output = drush_shell_exec_output(); + $this->logger()->info(implode("\n", $output)); + + if ($this->io()->confirm(dt('Import the listed configuration changes?'))) { + \Drupal::service('config.import.commands')->doImport($storage_comparer); + } + } + +} From 7974f10b2b014e476b48adac55a65d0778990404 Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Mon, 23 Apr 2018 09:44:49 +0800 Subject: [PATCH 3/6] Updated options. --- src/Commands/DrushCmiToolsCommands.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commands/DrushCmiToolsCommands.php b/src/Commands/DrushCmiToolsCommands.php index c824541..5264f76 100644 --- a/src/Commands/DrushCmiToolsCommands.php +++ b/src/Commands/DrushCmiToolsCommands.php @@ -44,7 +44,7 @@ public function exportPlus(array $options = ['destination' => null, 'ignore-list return; } $patterns = []; - if ($ignore_list = drush_get_option('ignore-list')) { + if ($ignore_list = $options['ignore-list']) { if (!is_file($ignore_list)) { $this->logger()->error(dt('The file specified in --ignore-list option does not exist.')); return; @@ -128,7 +128,7 @@ public function importPlus(array $options = ['preview' => null, 'source' => null $data = $file_storage->read($name); $source_storage->replaceData($name, $data); } - if ($delete_list = drush_get_option('delete-list')) { + if ($delete_list = $options['delete-list']) { if (!is_file($delete_list)) { $this->logger()->error(dt('The file specified in --delete-list option does not exist.')); return; @@ -164,7 +164,7 @@ public function importPlus(array $options = ['preview' => null, 'source' => null } } } - if ($install = drush_get_option('install')) { + if ($install = $options['install']) { $file_storage = new FileStorage($install); foreach ($file_storage->listAll() as $name) { if (!$source_storage->exists($name)) { From db1413968dd17e40a52bb6982268449d21fe4a0f Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Thu, 26 Apr 2018 11:40:00 +0800 Subject: [PATCH 4/6] Moved to notices. --- src/Commands/DrushCmiToolsCommands.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Commands/DrushCmiToolsCommands.php b/src/Commands/DrushCmiToolsCommands.php index 5264f76..5da6f55 100644 --- a/src/Commands/DrushCmiToolsCommands.php +++ b/src/Commands/DrushCmiToolsCommands.php @@ -84,7 +84,7 @@ public function exportPlus(array $options = ['destination' => null, 'ignore-list foreach ($patterns as $pattern) { foreach (file_scan_directory($destination_dir, $pattern) as $file_url => $file) { $file_service->unlink($file_url); - $this->logger()->info("Removed $file_url according to ignore list."); + $this->logger()->notice("Removed $file_url according to ignore list."); } } @@ -156,10 +156,10 @@ public function importPlus(array $options = ['preview' => null, 'source' => null } if ($source_storage->exists($delete)) { $source_storage->delete($delete); - $this->logger()->info("Deleted $delete as per delete list."); + $this->logger()->notice("Deleted $delete as per delete list."); } else { - $this->logger()->info("Ignored deleting $delete, does not exist."); + $this->logger()->notice("Ignored deleting $delete, does not exist."); } } } @@ -170,7 +170,7 @@ public function importPlus(array $options = ['preview' => null, 'source' => null if (!$source_storage->exists($name)) { $data = $file_storage->read($name); $source_storage->replaceData($name, $data); - $this->logger()->info("Installed $name for first time."); + $this->logger()->notice("Installed $name for first time."); } } } @@ -181,7 +181,7 @@ public function importPlus(array $options = ['preview' => null, 'source' => null if (!$storage_comparer->createChangelist()->hasChanges()) { - $this->logger()->info(dt('There are no changes to import.')); + $this->logger()->notice(dt('There are no changes to import.')); return; } @@ -196,7 +196,7 @@ public function importPlus(array $options = ['preview' => null, 'source' => null } drush_shell_exec('diff -x %s -u %s %s', '*.git', $temp_dir, $source_dir); $output = drush_shell_exec_output(); - $this->logger()->info(implode("\n", $output)); + $this->logger()->notice(implode("\n", $output)); if ($this->io()->confirm(dt('Import the listed configuration changes?'))) { \Drupal::service('config.import.commands')->doImport($storage_comparer); From 5cbc9230a79e3f48cc0a6db7664064e71a5ff54f Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Fri, 4 May 2018 09:18:17 +0800 Subject: [PATCH 5/6] New option for diff on import. --- src/Commands/DrushCmiToolsCommands.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Commands/DrushCmiToolsCommands.php b/src/Commands/DrushCmiToolsCommands.php index 5da6f55..ca376e7 100644 --- a/src/Commands/DrushCmiToolsCommands.php +++ b/src/Commands/DrushCmiToolsCommands.php @@ -110,7 +110,7 @@ public function exportPlus(array $options = ['destination' => null, 'ignore-list * @command config:import-plus * @aliases cimy,config-import-plus */ - public function importPlus(array $options = ['preview' => null, 'source' => null, 'delete-list' => null, 'install' => null]) { + public function importPlus(array $options = ['preview' => null, 'source' => null, 'delete-list' => null, 'install' => null, 'diff' => null]) { $this->logger()->debug(dt('Starting import')); // Determine source directory. if ($target = $options['source']) { @@ -186,17 +186,19 @@ public function importPlus(array $options = ['preview' => null, 'source' => null } // Copy active storage to the temporary directory. - $temp_dir = drush_tempdir(); - $temp_storage = new FileStorage($temp_dir); - $source_dir_storage = new FileStorage($source_dir); - foreach ($source_dir_storage->listAll() as $name) { - if ($data = $active_storage->read($name)) { - $temp_storage->write($name, $data); + if ($options['diff']) { + $temp_dir = drush_tempdir(); + $temp_storage = new FileStorage($temp_dir); + $source_dir_storage = new FileStorage($source_dir); + foreach ($source_dir_storage->listAll() as $name) { + if ($data = $active_storage->read($name)) { + $temp_storage->write($name, $data); + } } + drush_shell_exec('diff -x %s -u %s %s', '*.git', $temp_dir, $source_dir); + $output = drush_shell_exec_output(); + $this->logger()->notice(implode("\n", $output)); } - drush_shell_exec('diff -x %s -u %s %s', '*.git', $temp_dir, $source_dir); - $output = drush_shell_exec_output(); - $this->logger()->notice(implode("\n", $output)); if ($this->io()->confirm(dt('Import the listed configuration changes?'))) { \Drupal::service('config.import.commands')->doImport($storage_comparer); From 52672ffcb0245b9cadde539467dd219137d91054 Mon Sep 17 00:00:00 2001 From: Ben Dougherty Date: Wed, 8 Aug 2018 17:51:49 +0800 Subject: [PATCH 6/6] Support reading/writing from a config directory specific in settings.php --- src/Commands/DrushCmiToolsCommands.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Commands/DrushCmiToolsCommands.php b/src/Commands/DrushCmiToolsCommands.php index ca376e7..e243794 100644 --- a/src/Commands/DrushCmiToolsCommands.php +++ b/src/Commands/DrushCmiToolsCommands.php @@ -8,7 +8,6 @@ use Drupal\Core\Config\StorageComparer; use Drupal\Core\Serialization\Yaml; use Drush\Commands\DrushCommands; -use Drush\Drupal\Commands\config\ConfigCommands; /** * A Drush commandfile. @@ -36,6 +35,14 @@ public function exportPlus(array $options = ['destination' => null, 'ignore-list // Determine which target directory to use. if (($target = $options['destination']) && $target !== TRUE) { $destination_dir = $target; + + // Support writing to a destination from the $config_directories specified + // in settings.php + global $config_directories; + if (!empty($config_directories[$destination_dir])) { + $destination_dir = $config_directories[$destination_dir]; + } + // It is important to be able to specify a destination directory that // does not exist yet, for exporting on remote systems drush_mkdir($destination_dir); @@ -115,6 +122,13 @@ public function importPlus(array $options = ['preview' => null, 'source' => null // Determine source directory. if ($target = $options['source']) { $source_dir = $target; + + // Support reading the source from the $config_directories specified in + // settings.php + global $config_directories; + if (!empty($config_directories[$source_dir])) { + $source_dir = $config_directories[$source_dir]; + } } else { $this->logger()->error(dt('You must provide a --source option'));