diff --git a/src/Commands/updatedb/UpdateDbStatusCommand.php b/src/Commands/updatedb/UpdateDbStatusCommand.php index a0de3c938b..6b4a8ce906 100644 --- a/src/Commands/updatedb/UpdateDbStatusCommand.php +++ b/src/Commands/updatedb/UpdateDbStatusCommand.php @@ -30,7 +30,8 @@ 'module' => 'Module', 'update_id' => 'Update ID', 'description' => 'Description', - 'type' => 'Type' + 'type' => 'Type', + 'allowed' => 'Allowed', ])] #[CLI\DefaultTableFields(fields: ['module', 'update_id', 'type', 'description'])] #[CLI\FilterDefaultField(field: 'type')] @@ -74,6 +75,17 @@ public function doExecute(InputInterface $input, OutputInterface $output): ?Rows if (empty($pending)) { (new DrushStyle($input, $output))->success('No database updates required.'); } else { + if (empty($input->getOption('filter'))) { + // Show only allowed updates by default. + // Would ideally set a default filter in the parameter, however + // there's an upstream issue in + // https://github.com/consolidation/filter-via-dot-access-data/pull/51 + // that needs to be addressed before that's possible, so + // manually handling the filter for now. + $pending = array_filter($pending, static function ($update_hook) { + return empty($update_hook['allowed']) || $update_hook['allowed'] === 'yes'; + }); + } $return = new RowsOfFields($pending); } return $return; @@ -94,28 +106,53 @@ public function getUpdatedbStatus(): array require_once DRUPAL_ROOT . '/core/includes/update.inc'; $pending = \update_get_update_list(); + $start = $this->getUpdateList(); + // Resolve any update dependencies to determine the actual updates that will + // be run and the order they will be run in. + $upcoming_updates = update_resolve_dependencies($start); + $return = []; $warnings = []; // Ensure system module's updates run first. $start['system'] = []; - foreach ($pending as $module => $updates) { - if (isset($updates['start'])) { - $start[$module] = $updates['start']; - foreach ($updates['pending'] as $update_id => $description) { - // Strip cruft from front. - $description = str_replace($update_id . ' - ', '', $description); - $return[$module . "_update_$update_id"] = [ - 'module' => $module, - 'update_id' => $update_id, - 'description' => $description, - 'type' => 'hook_update_n' - ]; + foreach ($upcoming_updates as $upcoming_update) { + $module = $upcoming_update['module']; + $update_id = $upcoming_update['number']; + $description = $pending[$module]['pending'][$update_id]; + // Strip cruft from front. + $description = str_replace($update_id . ' - ', '', $description); + $module_update_function = $module . "_update_$update_id"; + $return[$module_update_function] = [ + 'module' => $module, + 'update_id' => $update_id, + 'description' => $description, + 'type' => 'hook_update_n', + 'allowed' => !empty($upcoming_update['allowed']) ? 'yes' : 'no', + ]; + if (empty($upcoming_update['allowed'])) { + if ($upcoming_update['missing_dependencies']) { + // This should rarely happen, but the user should be notified + // since skipping them can potentially put the database in an + // inconsistent state. + $missing_warning = dt('Skipping @update_function due to missing dependencies: @missing_dependencies.', [ + '@update_function' => "{$module_update_function}()", + '@missing_dependencies' => implode('(), ', $upcoming_update['missing_dependencies']) . '()', + ]); + } else { + $missing_warning = dt("Skipping @update_function due to an error in the module's code.", [ + '@update_function' => "{$module_update_function}()", + ]); + } + if (isset($pending[$module]['warning'])) { + $pending[$module]['warning'] .= "\n$missing_warning"; + } else { + $pending[$module]['warning'] = $missing_warning; } } - if (isset($updates['warning'])) { - $warnings[$module] = $updates['warning']; + if (isset($pending[$module]['warning'])) { + $warnings[$module] = $pending[$module]['warning']; } } @@ -131,7 +168,8 @@ public function getUpdatedbStatus(): array 'module' => $module, 'update_id' => $id, 'description' => trim($item), - 'type' => 'post-update' + 'type' => 'post-update', + 'allowed' => 'yes', ]; } } @@ -140,4 +178,16 @@ public function getUpdatedbStatus(): array return [$return, $start, $warnings]; } + + // Copy of protected \Drupal\system\Controller\DbUpdateController::getModuleUpdates. + protected function getUpdateList(): array + { + $return = []; + $updates = update_get_update_list(); + foreach ($updates as $module => $update) { + $return[$module] = $update['start']; + } + + return $return; + } } diff --git a/sut/modules/unish/drush_empty_module/drush_empty_module.install b/sut/modules/unish/drush_empty_module/drush_empty_module.install index 7b61d8f19d..e431d7276c 100644 --- a/sut/modules/unish/drush_empty_module/drush_empty_module.install +++ b/sut/modules/unish/drush_empty_module/drush_empty_module.install @@ -35,3 +35,17 @@ function drush_empty_module_requirements($phase) { function drush_empty_module_update_8001() { return 'The update ran'; } + +/** + * Fake update hook 2. + */ +function drush_empty_module_update_8002() { + return 'The second update ran'; +} + +/** + * Fake update hook 3. + */ +function drush_empty_module_update_8003() { + return 'The third update ran'; +} diff --git a/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.info.yml b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.info.yml new file mode 100644 index 0000000000..aaf2d97aa9 --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.info.yml @@ -0,0 +1,6 @@ +name: Missing Drush update +type: module +description: Module to always trigger a missing update dependency. +core_version_requirement: '>=8' +package: Other +dependencies: [] diff --git a/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.install b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.install new file mode 100644 index 0000000000..c7ae56e7e8 --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency/drush_missing_update_dependency.install @@ -0,0 +1,37 @@ + 8001, + ]; + $dependencies['drush_missing_update_dependency'][8002] = [ + 'drush_non_existent_update_dependency' => 8002, + ]; + return $dependencies; +} + + +/** + * This should never have run (missing dependency). + */ +function drush_missing_update_dependency_update_8001() { + throw new \LogicException('This update function is not allowed to run, since the schema version should already be at least 8001.'); +} + +/** + * This should never run (missing dependency). + */ +function drush_missing_update_dependency_update_8002() { + throw new \LogicException('This update function is not allowed to run, since it has a direct dependency on a missing update.'); +} + diff --git a/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.info.yml b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.info.yml new file mode 100644 index 0000000000..8ed9cb23d3 --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.info.yml @@ -0,0 +1,6 @@ +name: Missing Drush update dependency 1 +type: module +description: Module to trigger missing update dependency. +core_version_requirement: '>=8' +package: Other +dependencies: [] diff --git a/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.install b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.install new file mode 100644 index 0000000000..ddff253600 --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency1/drush_missing_update_dependency1.install @@ -0,0 +1,20 @@ +=8' +package: Other +dependencies: [] diff --git a/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency2/drush_missing_update_dependency2.install b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency2/drush_missing_update_dependency2.install new file mode 100644 index 0000000000..2446adc8bd --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_missing_update_dependency2/drush_missing_update_dependency2.install @@ -0,0 +1,14 @@ +=8' +package: Other +dependencies: [] diff --git a/sut/modules/unish/drush_update_dependency/drush_update_dependency.install b/sut/modules/unish/drush_update_dependency/drush_update_dependency.install new file mode 100644 index 0000000000..cd1d43c752 --- /dev/null +++ b/sut/modules/unish/drush_update_dependency/drush_update_dependency.install @@ -0,0 +1,73 @@ + 8001, + ]; + + // These are coordinated with the corresponding dependencies declared in + // update_test_1_update_dependencies(). + $dependencies['drush_missing_update_dependency1'][8002] = [ + 'drush_update_dependency' => 8002, + ]; + // Ensure the two update hooks won't run until the module exists. + $dependencies['drush_update_dependency'][8003] = [ + 'drush_missing_update_dependency1' => 8001, + ]; + $dependencies['drush_update_dependency'][8004] = [ + 'drush_missing_update_dependency2' => 8001, + ]; + + return $dependencies; +} + +/** + * Dependency update hook 1. + */ +function drush_update_dependency_update_8001() { + return 'The first update ran'; +} + +/** + * Dependency update hook 2. + */ +function drush_update_dependency_update_8002() { + return 'The second update ran'; +} + +/** + * Dependency update hook 3. + */ +function drush_update_dependency_update_8003() { + return 'The third update ran'; +} + +/** + * Dependency update hook 4. + */ +function drush_update_dependency_update_8004() { + return 'The fourth update ran'; +} + +/** + * Dependency update hook 5 (blocked). + */ +function drush_update_dependency_update_8005() { + // This will not be ran until the previous hooks have been successfully ran. + return 'The fifth update ran'; +} diff --git a/tests/functional/UpdateDBTest.php b/tests/functional/UpdateDBTest.php index bbe4be370e..46d6ee2a87 100644 --- a/tests/functional/UpdateDBTest.php +++ b/tests/functional/UpdateDBTest.php @@ -4,11 +4,12 @@ namespace Unish; +use Drush\Commands\core\PhpEvalCommand; +use Drush\Commands\core\PhpScriptCommand; +use Drush\Commands\pm\PmInstallCommand; +use Drush\Commands\sql\SqlQueryCommand; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\DataProvider; -use Drush\Commands\core\PhpCommands; -use Drush\Commands\pm\PmCommands; -use Drush\Commands\sql\SqlCommands; use Drush\Commands\updatedb\UpdateDBCommand; use Drush\Commands\updatedb\UpdateDbStatusCommand; use Symfony\Component\Filesystem\Path; @@ -24,33 +25,129 @@ class UpdateDBTest extends CommandUnishTestCase public function testUpdateDBStatus(): void { $this->setUpDrupal(1, true); - $this->drush(PmCommands::INSTALL, ['drush_empty_module']); + $this->drush(PmInstallCommand::NAME, ['drush_empty_module', 'drush_update_dependency']); $this->drush(UpdateDBStatusCommand::NAME); $err = $this->getErrorOutput(); $this->assertStringContainsString('[OK] No database updates required.', $err); - // Force a pending update. - $this->drush(PhpCommands::SCRIPT, ['updatedb_script'], ['script-path' => __DIR__ . '/resources']); + // Force pending updates. + $this->drush(PhpScriptCommand::NAME, [ + 'updatedb_script', + 'drush_empty_module', + 'drush_update_dependency', + ], ['script-path' => __DIR__ . '/resources']); - // Assert that pending hook_update_n appears + // Assert that pending hook_update_n appears and in the right order $this->drush(UpdateDBStatusCommand::NAME, [], ['format' => 'json']); $out = $this->getOutputFromJSON('drush_empty_module_update_8001'); $this->assertStringContainsString('Fake update hook', trim($out['description'])); + // Show only allowed updates by default + $expected_update_order = [ + 'drush_empty_module_update_8001', + 'drush_update_dependency_update_8001', + 'drush_update_dependency_update_8002', + 'drush_empty_module_update_8002', + 'drush_empty_module_update_8003', + ]; + $this->assertEquals($expected_update_order, array_keys($this->getOutputFromJSON())); + + // Ensure skipped dependencies are shown as warnings. + $err = $this->getErrorOutput(); + $this->assertStringContainsString('Skipping drush_update_dependency_update_8003() due to missing dependencies: drush_missing_update_dependency1_update_8001().', $err); + $this->assertStringContainsString('Skipping drush_update_dependency_update_8004() due to missing dependencies: drush_missing_update_dependency2_update_8001(), drush_missing_update_dependency1_update_8001().', $err); + $this->assertStringContainsString('Skipping drush_update_dependency_update_8005() due to missing dependencies: drush_missing_update_dependency2_update_8001(), drush_missing_update_dependency1_update_8001().', $err); + + // Show just the ones which have been skipped + $this->drush(UpdateDBStatusCommand::NAME, [], ['format' => 'json', 'filter' => 'allowed=no']); + $expected_update_order = [ + 'drush_update_dependency_update_8003', + 'drush_update_dependency_update_8004', + 'drush_update_dependency_update_8005', + ]; + $this->assertEquals($expected_update_order, array_keys($this->getOutputFromJSON())); + + // Show both allowed and not allowed updates + $this->drush(UpdateDBStatusCommand::NAME, [], ['format' => 'json', 'filter' => 'allowed=yes||allowed=no']); + $expected_update_order = [ + 'drush_empty_module_update_8001', + 'drush_update_dependency_update_8001', + 'drush_update_dependency_update_8002', + 'drush_update_dependency_update_8003', + 'drush_update_dependency_update_8004', + 'drush_update_dependency_update_8005', + 'drush_empty_module_update_8002', + 'drush_empty_module_update_8003', + ]; + $this->assertEquals($expected_update_order, array_keys($this->getOutputFromJSON())); + + // Install the missing dependencies and force pending updates for them + $this->drush(PmInstallCommand::NAME, ['drush_missing_update_dependency1', 'drush_missing_update_dependency2']); + $this->drush(PhpScriptCommand::NAME, [ + 'updatedb_script', + 'drush_missing_update_dependency1', + 'drush_missing_update_dependency2', + ], ['script-path' => __DIR__ . '/resources']); + $this->drush(UpdateDbStatusCommand::NAME, [], ['format' => 'json']); + $err = $this->getErrorOutput(); + // There should be no warnings since all the main dependencies are + // available to run + $this->assertEmpty($err); + $expected_update_order = [ + // The initial dependency1 and dependency2 hooks must be run before + // drush_update_dependency_update_8003() update + 'drush_missing_update_dependency2_update_8001', + 'drush_missing_update_dependency1_update_8001', + 'drush_empty_module_update_8001', + 'drush_update_dependency_update_8001', + 'drush_update_dependency_update_8002', + 'drush_missing_update_dependency1_update_8002', + 'drush_update_dependency_update_8003', + 'drush_update_dependency_update_8004', + 'drush_update_dependency_update_8005', + 'drush_empty_module_update_8002', + 'drush_empty_module_update_8003', + ]; + $this->assertEquals($expected_update_order, array_keys($this->getOutputFromJSON())); + // Run hook_update_n $this->drush(UpdateDBCommand::NAME, []); + // Ensure the update order matches the summary. + $err = $this->getErrorOutput(); + $output_offset = 0; + foreach ($expected_update_order as $expected_update) { + $update_completed_string = "Update completed: $expected_update"; + $output_offset = strpos($err, $update_completed_string, $output_offset); + $this->assertIsInt($output_offset, "Could not find update hook: $expected_update"); + $this->assertStringContainsString($update_completed_string, substr($err, $output_offset)); + // Move the offset for the next update to search. + $output_offset += strlen($update_completed_string); + } + // Assert that we ran hook_update_n properly $this->drush(UpdateDbStatusCommand::NAME); $err = $this->getErrorOutput(); $this->assertStringContainsString('[OK] No database updates required.', $err); + // Force a pending update for the module that can't run update hooks + // due to a missing dependency. + $this->drush(PmInstallCommand::NAME, ['drush_missing_update_dependency']); + $this->drush(PhpScriptCommand::NAME, ['updatedb_script', 'drush_missing_update_dependency'], [ + 'script-path' => __DIR__ . '/resources', + '--' => null, + 'schema-version' => 8001, + ]); + // Assure that a pending post-update is reported. $this->pathPostUpdate = Path::join($this->webroot(), 'modules/unish/drush_empty_module/drush_empty_module.post_update.php'); copy(__DIR__ . '/resources/drush_empty_module.post_update.php', $this->pathPostUpdate); $this->drush(UpdateDBStatusCommand::NAME, [], ['format' => 'json']); $out = $this->getOutputFromJSON('drush_empty_module-post-null_op'); $this->assertStringContainsString('This is a test of the emergency broadcast system.', trim($out['description'])); + // The missing dependency warning is still shown. + $err = $this->getErrorOutput(); + $this->assertStringContainsString('[warning] drush_missing_update_dependency: Skipping drush_missing_update_dependency_update_8002() due to missing dependencies: drush_non_existent_update_dependency_update_8002().', $err); } /** @@ -63,13 +160,13 @@ public function testFailedUpdate(int $last_successful_update, array $expected_st $options = [ 'yes' => null, ]; - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force a pending update. - $this->drush(PhpCommands::SCRIPT, ['updatedb_script'], ['script-path' => __DIR__ . '/resources']); + $this->drush(PhpScriptCommand::NAME, ['updatedb_script'], ['script-path' => __DIR__ . '/resources']); // Force re-run of woot_update_8101(). - $this->drush(PhpCommands::EVAL, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", ' . $last_successful_update . ')'], $options); + $this->drush(PhpEvalCommand::NAME, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", ' . $last_successful_update . ')'], $options); // Force re-run of the post-update woot_post_update_failing(). $this->forcePostUpdate('woot_post_update_failing', $options); @@ -146,10 +243,10 @@ public function testFailedPostUpdate(): void $options = [ 'yes' => null, ]; - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of woot_update_8104(). - $this->drush(PhpCommands::EVAL, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8103)'], $options); + $this->drush(PhpEvalCommand::NAME, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8103)'], $options); // Force re-run of post-update hooks. $this->forcePostUpdate('woot_post_update_a', $options); @@ -195,7 +292,7 @@ public function testUpdateModuleWithServiceDependency(): void $options = [ 'include' => __DIR__, ]; - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of the post-update woot_post_update_install_drush_empty_module(). $this->forcePostUpdate('woot_post_update_install_drush_empty_module', $options); @@ -242,10 +339,10 @@ public function testSuccessfulUpdate(): void $options = [ 'yes' => null, ]; - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of woot_update_8104() which is expected to be completed successfully. - $this->drush(PhpCommands::EVAL, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8103)'], $options); + $this->drush(PhpEvalCommand::NAME, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8103)'], $options); // Force re-run of post-update hooks which are expected to be completed successfully. $this->forcePostUpdate('woot_post_update_a', $options); @@ -272,10 +369,10 @@ public function testBatchUpdateLogMessages(): void 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of woot_update_8105(). - $this->drush(PhpCommands::EVAL, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8104)'], $options); + $this->drush(PhpEvalCommand::NAME, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8104)'], $options); // Force re-run of woot_post_update_batch(). $this->forcePostUpdate('woot_post_update_batch', $options); @@ -314,10 +411,10 @@ public function testEnableModuleViaUpdate(): void 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of woot_update_8106(). - $this->drush(PhpCommands::EVAL, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8105)'], $options); + $this->drush(PhpEvalCommand::NAME, ['Drupal::service("update.update_hook_registry")->setInstalledVersion("woot", 8105)'], $options); // Run updates. $this->drush(UpdateDBCommand::NAME, [], $options); @@ -326,7 +423,7 @@ public function testEnableModuleViaUpdate(): void $this->assertStringContainsString('[notice] taxonomy_term', $this->getErrorOutputRaw()); // Check that the new entity type is installed. - $this->drush(PhpCommands::EVAL, ['woot_get_taxonomy_term_entity_type_id();']); + $this->drush(PhpEvalCommand::NAME, ['woot_get_taxonomy_term_entity_type_id();']); $this->assertStringContainsString('taxonomy_term', $this->getOutputRaw()); } @@ -339,7 +436,7 @@ public function testEnableModuleViaPostUpdate(): void 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->drush(PmCommands::INSTALL, ['woot'], $options); + $this->drush(PmInstallCommand::NAME, ['woot'], $options); // Force re-run of woot_post_update_install_taxonomy(). $this->forcePostUpdate('woot_post_update_install_taxonomy', $options); @@ -351,7 +448,7 @@ public function testEnableModuleViaPostUpdate(): void $this->assertStringContainsString('[notice] taxonomy_term', $this->getErrorOutputRaw()); // Check that the new entity type is installed. - $this->drush(PhpCommands::EVAL, ['woot_get_taxonomy_term_entity_type_id();']); + $this->drush(PhpEvalCommand::NAME, ['woot_get_taxonomy_term_entity_type_id();']); $this->assertStringContainsString('taxonomy_term', $this->getOutputRaw()); } @@ -383,10 +480,10 @@ public function tearDown(): void */ protected function forcePostUpdate($hook, array $options) { - $this->drush(SqlCommands::QUERY, ["SELECT value FROM key_value WHERE collection = 'post_update' AND name = 'existing_updates'"], $options); + $this->drush(SqlQueryCommand::NAME, ["SELECT value FROM key_value WHERE collection = 'post_update' AND name = 'existing_updates'"], $options); $functions = unserialize($this->getOutput()); unset($functions[array_search($hook, $functions)]); $functions = serialize($functions); - $this->drush(SqlCommands::QUERY, ["UPDATE key_value SET value = '$functions' WHERE collection = 'post_update' AND name = 'existing_updates'"], $options); + $this->drush(SqlQueryCommand::NAME, ["UPDATE key_value SET value = '$functions' WHERE collection = 'post_update' AND name = 'existing_updates'"], $options); } } diff --git a/tests/functional/resources/updatedb_script.php b/tests/functional/resources/updatedb_script.php index da13777e13..b5b63d6179 100644 --- a/tests/functional/resources/updatedb_script.php +++ b/tests/functional/resources/updatedb_script.php @@ -2,7 +2,46 @@ declare(strict_types=1); -// Set the schema version of drush_empty_module to a lower version, so we have a -// pending update. -$current = \Drupal::service('update.update_hook_registry')->getInstalledVersion('drush_empty_module'); -\Drupal::service('update.update_hook_registry')->setInstalledVersion('drush_empty_module', $current - 1); +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** @var \Drush\Commands\core\PhpScriptCommand $this */ +assert(isset($input) && $input instanceof InputInterface); +assert(isset($output) && $output instanceof OutputInterface); +$input_definition = new InputDefinition(); +$input_definition->addArgument(new InputArgument('modules', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'The modules to update schema versions for.', ['drush_empty_module'])); +$input_definition->addOptions([ + new InputOption('--schema-version', null, InputOption::VALUE_REQUIRED, 'The schema version to use.', 8000), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), +]); + +$arguments = new ArgvInput($input->getArgument('extra'), $input_definition); + +if ($arguments->getOption('help')) { + $output->writeln('Usage:'); + $output->writeln(sprintf( + " drush php-script %s -- [options] [...modules]\n", + $input->getArgument('extra')[0] + )); + $helper = new DescriptorHelper(); + $helper->describe($output, $input_definition); + $output->writeln(''); + return; +} + +/** @var \Drupal\Core\Update\UpdateHookRegistry $update_hook_registry */ +$update_hook_registry = \Drupal::service('update.update_hook_registry'); +$module_handler = \Drupal::moduleHandler(); + +// The schema version to set it to. +$schema_version = (int) $arguments->getOption('schema-version'); +$modules = $arguments->getArgument('modules'); +foreach ($modules as $module) { + // Set the installed version for the specific modules. + $update_hook_registry->setInstalledVersion($module, $schema_version); +} diff --git a/tests/unish/CommandUnishTestCase.php b/tests/unish/CommandUnishTestCase.php index 86a6245bec..41077de39a 100644 --- a/tests/unish/CommandUnishTestCase.php +++ b/tests/unish/CommandUnishTestCase.php @@ -171,7 +171,9 @@ protected function prepareDrushCommand(string $command, array $args = [], array foreach ($values as $value) { $dashes = strlen($key) === 1 ? '-' : '--'; $equals = strlen($key) === 1 ? '' : '='; - if (!isset($value)) { + if ($key === '--') { + $cmd[] = '--'; + } elseif (!isset($value)) { $cmd[] = "$dashes$key"; } else { // Cast because the CLI sends only strings.