From 58f13f7693a781f9971de3b2a01e2e8b69c9fa1b Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Sat, 4 May 2019 15:32:59 +0300 Subject: [PATCH 1/6] Added enumerated update statuses --- .../update_helper_checklist.module | 14 ++- src/Updater.php | 110 ++++++++++++++---- 2 files changed, 100 insertions(+), 24 deletions(-) diff --git a/modules/update_helper_checklist/update_helper_checklist.module b/modules/update_helper_checklist/update_helper_checklist.module index 3b9bb0c..be47c97 100644 --- a/modules/update_helper_checklist/update_helper_checklist.module +++ b/modules/update_helper_checklist/update_helper_checklist.module @@ -14,6 +14,11 @@ use Drupal\update_helper_checklist\UpdateChecklist; use Symfony\Component\Yaml\Yaml; use Drupal\update_helper_checklist\Entity\Update; +const CONFIG_NOT_FOUND = 0; +const CONFIG_ALREADY_APPLIED = 1; +const CONFIG_NOT_EXPECTED = 2; +const CONFIG_APPLIED_SUCCESSFULLY = 3; + /** * Implements hook_checklistapi_checklist_info(). */ @@ -62,10 +67,10 @@ function _update_helper_checklist_checklistapi_checklist_items() { $entry = Update::load($update_key); $status = ($entry && $entry->wasSuccessfulByHook()) ? TRUE : FALSE; - if ($status && !empty($update['#description_successful'])) { + if ($entry && $status && !empty($update['#description_successful'])) { $update['#description'] .= $update['#description_successful']; } - elseif (!$status && !empty($update['#description_failed'])) { + elseif ($entry && !$status && !empty($update['#description_failed'])) { $update['#description'] .= $update['#description_failed']; } } @@ -107,6 +112,7 @@ function _update_helper_checklist_checklistapi_checklist_items() { function update_helper_checklist_modules_installed(array $modules) { /** @var \Drupal\Core\Extension\ModuleHandler $module_handler */ $module_handler = \Drupal::service('module_handler'); + $updateHelper = \Drupal::service('update_helper.updater'); $modules_checklist = []; $module_directories = $module_handler->getModuleDirectories(); @@ -120,6 +126,10 @@ function update_helper_checklist_modules_installed(array $modules) { foreach ($updates_checklist as $version_items) { foreach ($version_items as $update_hook_name => $checklist_definition) { if (is_array($checklist_definition)) { + + $status = $updateHelper->checkUpdate($module_name, $update_hook_name); + if ($status != CONFIG_ALREADY_APPLIED && $status != CONFIG_APPLIED_SUCCESSFULLY) continue; + if (!isset($modules_checklist[$module_name])) { $modules_checklist[$module_name] = []; } diff --git a/src/Updater.php b/src/Updater.php index 6f91b36..39051b6 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -20,6 +20,11 @@ class Updater implements UpdaterInterface { use StringTranslationTrait; + const CONFIG_NOT_FOUND = 0; + const CONFIG_ALREADY_APPLIED = 1; + const CONFIG_NOT_EXPECTED = 2; + const CONFIG_APPLIED_SUCCESSFULLY = 3; + /** * Site configFactory object. * @@ -130,7 +135,7 @@ protected function logInfo($message) { /** * {@inheritdoc} */ - public function executeUpdate($module, $update_definition_name) { + public function executeUpdate($module, $update_definition_name, $force = FALSE) { $this->warningCount = 0; $update_definitions = $this->configHandler->loadUpdate($module, $update_definition_name); @@ -141,7 +146,7 @@ public function executeUpdate($module, $update_definition_name) { } if (!empty($update_definitions)) { - $this->executeConfigurationActions($update_definitions); + $this->executeConfigurationActions($update_definitions, $force); } // Dispatch event after update has finished. @@ -151,6 +156,29 @@ public function executeUpdate($module, $update_definition_name) { return $this->warningCount === 0; } + /** + * Check update status of configuration from update definitions. + * + * @param string $module + * Module name where update definition is saved. + * @param string $update_definition_name + * Update definition name. Usually same name as update hook. + * + * @return bool + * Returns update status. + */ + public function checkUpdate($module, $update_definition_name) { + $this->warningCount = 0; + + $update_definitions = $this->configHandler->loadUpdate($module, $update_definition_name); + + if (!empty($update_definitions)) { + return $this->executeConfigurationActions($update_definitions, FALSE, TRUE); + } + + return Updater::CONFIG_NOT_FOUND; + } + /** * Get array with defined global actions. * @@ -176,8 +204,15 @@ protected function executeGlobalActions(array $global_actions) { * * @param array $update_definitions * List of configurations with update definitions for them. + * @param bool $force + * Force the update. + * @param bool $checkOnly + * Check the update status and don't apply the update. + * + * @return bool + * Returns update status if checkOnly flag is set. */ - protected function executeConfigurationActions(array $update_definitions) { + protected function executeConfigurationActions(array $update_definitions, $force = FALSE, $checkOnly = FALSE) { foreach ($update_definitions as $configName => $configChange) { $update_actions = $configChange['update_actions']; @@ -198,15 +233,33 @@ protected function executeConfigurationActions(array $update_definitions) { $new_config = NestedArray::mergeDeep($new_config, $update_actions['add']); } - if ($this->updateConfig($configName, $new_config, $configChange['expected_config'], $delete_keys)) { - $this->logInfo($this->t('Configuration @configName has been successfully updated.', ['@configName' => $configName])); + $result = $this->updateConfig($configName, $new_config, $configChange['expected_config'], $delete_keys, $force, $checkOnly); + + if ($checkOnly) { + return $result; } - else { - $this->logWarning($this->t('Unable to update configuration for @configName.', ['@configName' => $configName])); + + switch ($result) { + case Updater::CONFIG_APPLIED_SUCCESSFULLY: + $this->logInfo($this->t('Configuration @configName has been successfully updated.', ['@configName' => $configName])); + break; + + case Updater::CONFIG_ALREADY_APPLIED: + $this->logWarning($this->t('Configuration @configName is already updated.', ['@configName' => $configName])); + break; + + case Updater::CONFIG_NOT_EXPECTED: + $this->logWarning($this->t('Expected current configuration is modefied, Unable to apply new config @configName.', ['@configName' => $configName])); + break; + + case Updater::CONFIG_NOT_FOUND: + $this->logWarning($this->t('Unable to find config @configName. Skipping update.', ['@configName' => $configName])); + break; } } } + /** * Installs modules. * @@ -321,41 +374,54 @@ protected function getFlatKeys(array $nested_array) { * Only if current config is same like old config we are updating. * @param array $delete_keys * List of parent keys to remove. @see NestedArray::unsetValue() + * @param bool $force + * Force the update. + * @param bool $checkOnly + * Check the update status and don't apply the update. * * @return bool * Returns TRUE if update of configuration was successful. */ - protected function updateConfig($config_name, array $configuration, array $expected_configuration = [], array $delete_keys = []) { + protected function updateConfig($config_name, array $configuration, array $expected_configuration = [], array $delete_keys = [], $force = FALSE, $checkOnly = FALSE) { $config = $this->configFactory->getEditable($config_name); $config_data = $config->get(); + // Reset expected_config in case of force flag. + if ($force) { + $expected_configuration = []; + } + // Check that configuration exists before executing update. if (empty($config_data)) { - return FALSE; + return Updater::CONFIG_NOT_FOUND; } // Check if configuration is already in new state. $merged_data = NestedArray::mergeDeep($expected_configuration, $configuration); - if (empty(DiffArray::diffAssocRecursive($merged_data, $config_data))) { - return TRUE; + if (!$force && empty(DiffArray::diffAssocRecursive($merged_data, $config_data))) { + return Updater::CONFIG_ALREADY_APPLIED; } - if (!empty($expected_configuration) && DiffArray::diffAssocRecursive($expected_configuration, $config_data)) { - return FALSE; - } + if (empty($expected_configuration) || !DiffArray::diffAssocRecursive($expected_configuration, $config_data)) { + // Delete configuration keys from config. + if($checkOnly){ + return Updater::CONFIG_APPLIED_SUCCESSFULLY; + } - // Delete configuration keys from config. - if (!empty($delete_keys)) { - foreach ($delete_keys as $key_path) { - NestedArray::unsetValue($config_data, $key_path); + if (!empty($delete_keys)) { + foreach ($delete_keys as $key_path) { + NestedArray::unsetValue($config_data, $key_path); + } } - } - $config->setData(NestedArray::mergeDeep($config_data, $configuration)); - $config->save(); + $config->setData(NestedArray::mergeDeep($config_data, $configuration)); + $config->save(); - return TRUE; + return Updater::CONFIG_APPLIED_SUCCESSFULLY; + }else{ + return Updater::CONFIG_NOT_EXPECTED; + } } } From d387506f752b42a102d6ae54286b4af8f9639e62 Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Sat, 4 May 2019 16:47:14 +0300 Subject: [PATCH 2/6] Create a drush command to run updates --- .../ApplyConfigurationUpdateCommands.php | 56 +++++++++++++++++++ src/Utility/CommandHelper.php | 34 +++++++++++ update_helper.drush.inc | 55 ++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/Command/ApplyConfigurationUpdateCommands.php create mode 100644 src/Utility/CommandHelper.php create mode 100644 update_helper.drush.inc diff --git a/src/Command/ApplyConfigurationUpdateCommands.php b/src/Command/ApplyConfigurationUpdateCommands.php new file mode 100644 index 0000000..f047a55 --- /dev/null +++ b/src/Command/ApplyConfigurationUpdateCommands.php @@ -0,0 +1,56 @@ +commandHelper = new CommandHelper(); + } + + /** + * {@inheritdoc} + */ + public function setLogger(LoggerInterface $logger) { + parent::setLogger($logger); + $this->commandHelper->setLogger($logger); + } + + /** + * applying an update hook (function) from module install file + * Apply updates by invoking the related update hooks. + * + * @param string $module + * @param string $update_hook + * @param array $options + * @option force + * + * @command update_helper:apply-update + * @aliases uhau + */ + public function apply_update ($module = '', $update_hook = '', $options = ['force' => FALSE]) { + $force = $options['force']; + $this->commandHelper->apply_update($module, $update_hook, $force); + } +} \ No newline at end of file diff --git a/src/Utility/CommandHelper.php b/src/Utility/CommandHelper.php new file mode 100644 index 0000000..4be109d --- /dev/null +++ b/src/Utility/CommandHelper.php @@ -0,0 +1,34 @@ +logger->info(dt('Please provide a module name and an update hook. Example: drush varbase-up ')); + return; + } + + $updateHelper = \Drupal::service('update_helper.updater'); + $updateHelper->executeUpdate($module, $updateName, $force); + return $updateHelper->logger()->output(); + } + +} \ No newline at end of file diff --git a/update_helper.drush.inc b/update_helper.drush.inc new file mode 100644 index 0000000..d94e6e5 --- /dev/null +++ b/update_helper.drush.inc @@ -0,0 +1,55 @@ + 'Apply config updates.', + 'aliases' => ['uhau'], + 'arguments' => [ + 'module' => 'Module name.', + 'updateName' => 'Update name.', + ], + 'options' => [ + 'force' => FALSE, + ], + 'examples' => [ + 'drush uhau ' => 'Apply the update from ', + 'drush uhau --force ' => 'Force apply the update from ', + ], + ]; + + return $commands; +} + +/** + * Drush command logic. + * + * The drush_[MODULE_NAME]_[COMMAND_NAME](). + */ +function drush_update_helper_apply_update($module = "", $update_hook = "") { + $commandhelper = _update_helper_drush_command_helper(); + $force = drush_get_option('force', FALSE); + $commandhelper->apply_update($module, $update_hook, $force); +} + +/** + * Returns an instance of the command helper. + * + * @return \Drupal\update_helper\Utility\CommandHelper + * An instance of the command helper class. + */ +function _update_helper_drush_command_helper() { + $command_helper = new CommandHelper(); + $command_helper->setLogger(\Drupal::logger('update_helper')); + return $command_helper; +} From ebe109f429e793d08ae9c6c320833b431efb3d24 Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Sun, 5 May 2019 15:26:55 +0300 Subject: [PATCH 3/6] drush command fixes, and added missing services.yml --- drush.services.yml | 5 +++++ src/Command/ApplyConfigurationUpdateCommands.php | 6 +++--- src/Utility/CommandHelper.php | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 drush.services.yml diff --git a/drush.services.yml b/drush.services.yml new file mode 100644 index 0000000..ca74563 --- /dev/null +++ b/drush.services.yml @@ -0,0 +1,5 @@ +services: + update_helper.commands: + class: Drupal\update_helper\Command\ApplyConfigurationUpdateCommands + tags: + - { name: drush.command } \ No newline at end of file diff --git a/src/Command/ApplyConfigurationUpdateCommands.php b/src/Command/ApplyConfigurationUpdateCommands.php index f047a55..895bf44 100644 --- a/src/Command/ApplyConfigurationUpdateCommands.php +++ b/src/Command/ApplyConfigurationUpdateCommands.php @@ -1,6 +1,6 @@ commandHelper = new CommandHelper(); diff --git a/src/Utility/CommandHelper.php b/src/Utility/CommandHelper.php index 4be109d..4efaf1b 100644 --- a/src/Utility/CommandHelper.php +++ b/src/Utility/CommandHelper.php @@ -22,12 +22,12 @@ public function __construct() { */ public function apply_update($module = '', $update_hook = '', $force = FALSE) { if (!$update_hook || !$module) { - $this->logger->info(dt('Please provide a module name and an update hook. Example: drush varbase-up ')); + $this->logger->error(dt('Please provide a module name and an update hook. Example: drush uhau ')); return; } $updateHelper = \Drupal::service('update_helper.updater'); - $updateHelper->executeUpdate($module, $updateName, $force); + $updateHelper->executeUpdate($module, $update_hook, $force); return $updateHelper->logger()->output(); } From a943e976676acb3831397a9ab323d0bac9a957a3 Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Sun, 5 May 2019 15:28:52 +0300 Subject: [PATCH 4/6] fixed typo in message --- src/Updater.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Updater.php b/src/Updater.php index 39051b6..6ea37d3 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -249,7 +249,7 @@ protected function executeConfigurationActions(array $update_definitions, $force break; case Updater::CONFIG_NOT_EXPECTED: - $this->logWarning($this->t('Expected current configuration is modefied, Unable to apply new config @configName.', ['@configName' => $configName])); + $this->logWarning($this->t('Expected current configuration for @configName is not matching. Unable to apply new config.', ['@configName' => $configName])); break; case Updater::CONFIG_NOT_FOUND: From ab4dd3112a70fed4ae502e09c8899fea55ce1e1a Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Mon, 6 May 2019 16:05:54 +0300 Subject: [PATCH 5/6] added fix for global actions error on check update --- src/Updater.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Updater.php b/src/Updater.php index 6ea37d3..49c1411 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -171,7 +171,9 @@ public function checkUpdate($module, $update_definition_name) { $this->warningCount = 0; $update_definitions = $this->configHandler->loadUpdate($module, $update_definition_name); - + if (isset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS])) { + unset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS]); + } if (!empty($update_definitions)) { return $this->executeConfigurationActions($update_definitions, FALSE, TRUE); } From 1cb7132223e8bbc8a7301174748cfd62cc87157f Mon Sep 17 00:00:00 2001 From: Waleed Qadi Date: Thu, 16 May 2019 13:42:14 +0300 Subject: [PATCH 6/6] fixing already updated check, and removed CONSTANTS from checklist helper, added check for installed modules --- .../update_helper_checklist.module | 8 ++------ src/Updater.php | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/modules/update_helper_checklist/update_helper_checklist.module b/modules/update_helper_checklist/update_helper_checklist.module index be47c97..46a9685 100644 --- a/modules/update_helper_checklist/update_helper_checklist.module +++ b/modules/update_helper_checklist/update_helper_checklist.module @@ -13,11 +13,7 @@ use Drupal\Core\Url; use Drupal\update_helper_checklist\UpdateChecklist; use Symfony\Component\Yaml\Yaml; use Drupal\update_helper_checklist\Entity\Update; - -const CONFIG_NOT_FOUND = 0; -const CONFIG_ALREADY_APPLIED = 1; -const CONFIG_NOT_EXPECTED = 2; -const CONFIG_APPLIED_SUCCESSFULLY = 3; +use Drupal\update_helper\Updater; /** * Implements hook_checklistapi_checklist_info(). @@ -128,7 +124,7 @@ function update_helper_checklist_modules_installed(array $modules) { if (is_array($checklist_definition)) { $status = $updateHelper->checkUpdate($module_name, $update_hook_name); - if ($status != CONFIG_ALREADY_APPLIED && $status != CONFIG_APPLIED_SUCCESSFULLY) continue; + if ($status != Updater::CONFIG_ALREADY_APPLIED && $status != Updater::CONFIG_APPLIED_SUCCESSFULLY) continue; if (!isset($modules_checklist[$module_name])) { $modules_checklist[$module_name] = []; diff --git a/src/Updater.php b/src/Updater.php index 49c1411..6b17d7d 100644 --- a/src/Updater.php +++ b/src/Updater.php @@ -24,7 +24,8 @@ class Updater implements UpdaterInterface { const CONFIG_ALREADY_APPLIED = 1; const CONFIG_NOT_EXPECTED = 2; const CONFIG_APPLIED_SUCCESSFULLY = 3; - + const MODULES_FOUND = 4; + const MODULES_NOT_FOUND = 5; /** * Site configFactory object. * @@ -169,15 +170,28 @@ public function executeUpdate($module, $update_definition_name, $force = FALSE) */ public function checkUpdate($module, $update_definition_name) { $this->warningCount = 0; - + $moduleHandler = \Drupal::service('module_handler'); + $modulesInstalled = []; $update_definitions = $this->configHandler->loadUpdate($module, $update_definition_name); if (isset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS])) { + if (isset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS][UpdateDefinitionInterface::GLOBAL_ACTION_INSTALL_MODULES])) { + $modules = $update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS][UpdateDefinitionInterface::GLOBAL_ACTION_INSTALL_MODULES]; + foreach ($modules as $module) { + if (!$moduleHandler->moduleExists($module)) return Updater::MODULES_NOT_FOUND; + $modulesInstalled[] = $module; + } + } unset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS]); } + if (!empty($update_definitions)) { return $this->executeConfigurationActions($update_definitions, FALSE, TRUE); } + if (empty($update_definitions) && !empty($modulesInstalled)) { + return Updater::MODULES_FOUND; + } + return Updater::CONFIG_NOT_FOUND; } @@ -401,7 +415,7 @@ protected function updateConfig($config_name, array $configuration, array $expec // Check if configuration is already in new state. $merged_data = NestedArray::mergeDeep($expected_configuration, $configuration); - if (!$force && empty(DiffArray::diffAssocRecursive($merged_data, $config_data))) { + if (!$force && empty(DiffArray::diffAssocRecursive($configuration, $config_data))) { return Updater::CONFIG_ALREADY_APPLIED; }