diff --git a/core/lib/Drupal/Core/Test/TestUpdateRegistry.php b/core/lib/Drupal/Core/Test/TestUpdateRegistry.php
new file mode 100644
index 0000000..34f0076
--- /dev/null
+++ b/core/lib/Drupal/Core/Test/TestUpdateRegistry.php
@@ -0,0 +1,52 @@
+routerBuilder = $router_builder;
+ $this->moduleHandler = $module_handler;
+ $this->postUpdateRegistry = $post_update_registry;
}
/**
@@ -78,6 +100,53 @@ public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) {
}
/**
+ * Checks for any pending database updates.
+ *
+ * As pending database update can cause issues as they potentially make
+ * changes code base and database.
+ *
+ * @see https://www.drupal.org/node/2628144
+ *
+ * @param ConfigImporterEvent $event
+ * The config import event.
+ */
+ public function onConfigImporterValidateDatabaseUpdate(ConfigImporterEvent $event) {
+ if ($this->hasModuleUpdates()) {
+ $event->getConfigImporter()->logError(
+ $this->t('Some modules have database schema updates to install. You should run the database update script immediately.', [':update' => Url::fromRoute('system.db_update')->toString()])
+ );
+ }
+ }
+
+ /**
+ * Checks if there any pending database updates.
+ *
+ * @return bool
+ * TRUE, if there any pending update on modules, FALSE otherwise.
+ */
+ protected function hasModuleUpdates() {
+ // Check installed modules.
+ $has_pending_updates = FALSE;
+ foreach ($this->moduleHandler->getModuleList() as $module => $filename) {
+ $updates = drupal_get_schema_versions($module);
+ if ($updates !== FALSE) {
+ $default = drupal_get_installed_schema_version($module);
+ if (max($updates) > $default) {
+ return TRUE;
+ }
+ }
+ }
+ if (!$has_pending_updates) {
+ $missing_post_update_functions = $this->postUpdateRegistry->getPendingUpdateFunctions();
+ if (!empty($missing_post_update_functions)) {
+ return TRUE;
+ }
+ }
+
+ return $has_pending_updates;
+ }
+
+ /**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
@@ -86,6 +155,7 @@ public static function getSubscribedEvents() {
// there is no configuration to import.
$events[ConfigEvents::IMPORT_VALIDATE][] = ['onConfigImporterValidateNotEmpty', 512];
$events[ConfigEvents::IMPORT_VALIDATE][] = ['onConfigImporterValidateSiteUUID', 256];
+ $events[ConfigEvents::IMPORT_VALIDATE][] = ['onConfigImporterValidateDatabaseUpdate', 128];
return $events;
}
diff --git a/core/modules/system/src/Tests/Update/UpdatePendingConfigImportTest.php b/core/modules/system/src/Tests/Update/UpdatePendingConfigImportTest.php
new file mode 100644
index 0000000..3c73a07
--- /dev/null
+++ b/core/modules/system/src/Tests/Update/UpdatePendingConfigImportTest.php
@@ -0,0 +1,78 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
+ __DIR__ . '/../../../tests/fixtures/update/drupal-8.update-test-config-import.php',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSelectionTest() {
+ parent::doSelectionTest();
+ $this->assertRaw('8001 - Pending update.');
+ }
+
+ /**
+ * Tests database update with configuration import.
+ */
+ public function testConfigImport() {
+
+ // Add 'profile' key so we don't get notices.
+ \Drupal::configFactory()->getEditable('core.extension')->set('profile', \Drupal::installProfile())->save();
+ // Export active config to sync.
+ $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
+ // Get site name.
+ $site_name = \Drupal::config('system.site')->get('name');
+ $new_site_name = 'Overridden site name.';
+ // Change site name.
+ \Drupal::configFactory()->getEditable('system.site')->set('name', $new_site_name)->save();
+
+ try {
+ // Try running config import with pending.
+ $this->configImporter()->import();
+ $error_log = [];
+ }
+ catch (ConfigImporterException $e) {
+ $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
+ $error_log = $this->configImporter->getErrors();
+ }
+ $this->assertEqual(t('Some modules have database schema updates to install. You should run the database update script immediately.', [':update' => Url::fromRoute('system.db_update')->toString()]), $error_log[0]);
+
+ // Run database updates.
+ $this->runUpdates();
+ $this->drupalGet('update.php/selection');
+ $this->assertText('No pending updates.');
+
+ // Check site name.
+ $this->assertEqual($new_site_name, \Drupal::config('system.site')->get('name'));
+
+ // Use the UI to import (for some reason I can't get the config importer to
+ // get rid of it's error log of pending database updates, and that error log
+ // is probably stuck somewhere deeper).
+ $this->drupalLogin($this->rootUser);
+ $this->drupalGet('admin/config/development/configuration');
+ $this->drupalPostForm(NULL, [], 'Import all');
+
+ // Site name should be reverted again.
+ $this->assertEqual($site_name, \Drupal::config('system.site')->get('name'));
+ }
+
+}
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index c70889d..0c4e23a 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -35,7 +35,7 @@ services:
- { name: theme_negotiator, priority: 100 }
system.config_subscriber:
class: Drupal\system\SystemConfigSubscriber
- arguments: ['@router.builder']
+ arguments: ['@router.builder', '@module_handler', '@update.post_update_registry']
tags:
- { name: event_subscriber }
system.config_cache_tag:
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.update-test-config-import.php b/core/modules/system/tests/fixtures/update/drupal-8.update-test-config-import.php
new file mode 100644
index 0000000..7993a77
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.update-test-config-import.php
@@ -0,0 +1,40 @@
+merge('key_value')
+ ->condition('collection', 'system.schema')
+ ->condition('name', 'update_test_config_import')
+ ->fields([
+ 'collection' => 'system.schema',
+ 'name' => 'update_test_config_import',
+ 'value' => 'i:8000;',
+ ])
+ ->execute();
+
+// Update core.extension.
+$extensions = $connection->select('config')
+ ->fields('config', ['data'])
+ ->condition('collection', '')
+ ->condition('name', 'core.extension')
+ ->execute()
+ ->fetchField();
+$extensions = unserialize($extensions);
+$extensions['module']['update_test_config_import'] = 8000;
+$extensions['theme']['stable'] = 0;
+$connection->update('config')
+ ->fields([
+ 'data' => serialize($extensions),
+ ])
+ ->condition('collection', '')
+ ->condition('name', 'core.extension')
+ ->execute();
diff --git a/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.info.yml b/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.info.yml
new file mode 100644
index 0000000..faf112f
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.info.yml
@@ -0,0 +1,5 @@
+core: 8.x
+name: Update test config import
+type: module
+package: Testing
+version: VERSION
diff --git a/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.install b/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.install
new file mode 100644
index 0000000..11ec5e3
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_config_import/update_test_config_import.install
@@ -0,0 +1,12 @@
+addTag('event_subscriber');
}
+ // We don't care about post updates in the kernel test base.
+ $container->removeDefinition('update.post_update_registry');
+ $container->register('update.post_update_registry', TestUpdateRegistry::class);
+
if ($container->hasDefinition('path_processor_alias')) {
// Prevent the alias-based path processor, which requires a url_alias db
// table, from being registered to the path processor manager. We do this
@@ -811,6 +816,10 @@ protected function installSchema($module, $tables) {
throw new \LogicException("$module module does not define a schema for table '$table'.");
}
$this->container->get('database')->schema()->createTable($table, $schema);
+
+ // Set a very high schema version so no updates are found because we
+ // don't want to run them anyway.
+ drupal_set_installed_schema_version($module, PHP_INT_MAX);
}
}