diff --git a/setup/src/Magento/Setup/Console/Command/ModuleConfigStatusCommand.php b/setup/src/Magento/Setup/Console/Command/ModuleConfigStatusCommand.php
new file mode 100644
index 0000000000000..70dbf14728bd7
--- /dev/null
+++ b/setup/src/Magento/Setup/Console/Command/ModuleConfigStatusCommand.php
@@ -0,0 +1,109 @@
+configReader = $configReader;
+ $this->installerFactory = $installerFactory;
+
+ parent::__construct();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('module:config:status')
+ ->setDescription(
+ 'Checks the modules configuration in the \'app/etc/config.php\' file '
+ . 'and reports if they are up to date or not'
+ );
+
+ parent::configure();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ try {
+ // the config as currently in app/etc/config.php
+ $currentConfig = $this->configReader->load(ConfigFilePool::APP_CONFIG);
+ if (!array_key_exists(ConfigOptionsListConstants::KEY_MODULES, $currentConfig)) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \Exception('Can\'t find the modules configuration in the \'app/etc/config.php\' file.');
+ }
+
+ $currentModuleConfig = $currentConfig[ConfigOptionsListConstants::KEY_MODULES];
+
+ $installer = $this->installerFactory->create(new ConsoleLogger($output));
+
+ // the module config as Magento calculated it
+ $correctModuleConfig = $installer->getModulesConfig();
+
+ if ($currentModuleConfig !== $correctModuleConfig) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \Exception(
+ 'The modules configuration in the \'app/etc/config.php\' file is outdated. '
+ . 'Run \'setup:upgrade\' to fix it.'
+ );
+ }
+
+ $output->writeln(
+ 'The modules configuration is up to date.'
+ );
+ // phpcs:disable Magento2.Exceptions.ThrowCatch
+ } catch (\Exception $e) {
+ $output->writeln('' . $e->getMessage() . '');
+
+ return Cli::RETURN_FAILURE;
+ }
+
+ return Cli::RETURN_SUCCESS;
+ }
+}
diff --git a/setup/src/Magento/Setup/Console/CommandList.php b/setup/src/Magento/Setup/Console/CommandList.php
index 08befdca2221d..f0dad6f4a7452 100644
--- a/setup/src/Magento/Setup/Console/CommandList.php
+++ b/setup/src/Magento/Setup/Console/CommandList.php
@@ -66,6 +66,7 @@ protected function getCommandsClasses()
\Magento\Setup\Console\Command\ModuleDisableCommand::class,
\Magento\Setup\Console\Command\ModuleStatusCommand::class,
\Magento\Setup\Console\Command\ModuleUninstallCommand::class,
+ \Magento\Setup\Console\Command\ModuleConfigStatusCommand::class,
\Magento\Setup\Console\Command\MaintenanceAllowIpsCommand::class,
\Magento\Setup\Console\Command\MaintenanceDisableCommand::class,
\Magento\Setup\Console\Command\MaintenanceEnableCommand::class,
@@ -91,6 +92,7 @@ public function getCommands()
if (class_exists($class)) {
$commands[] = $this->serviceManager->get($class);
} else {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
throw new \Exception('Class ' . $class . ' does not exist');
}
}
diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php
index d4ed261a9a945..f80a35937d5dc 100644
--- a/setup/src/Magento/Setup/Model/Installer.php
+++ b/setup/src/Magento/Setup/Model/Installer.php
@@ -1200,6 +1200,17 @@ public function updateModulesSequence($keepGeneratedFiles = false)
$this->createModulesConfig([]);
}
+ /**
+ * Get the modules config as Magento sees it
+ *
+ * @return array
+ * @throws \LogicException
+ */
+ public function getModulesConfig()
+ {
+ return $this->createModulesConfig([], true);
+ }
+
/**
* Uninstall Magento application
*
diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleConfigStatusCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleConfigStatusCommandTest.php
new file mode 100644
index 0000000000000..16577ed2db738
--- /dev/null
+++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/ModuleConfigStatusCommandTest.php
@@ -0,0 +1,91 @@
+createMock(\Magento\Framework\App\DeploymentConfig\Reader::class);
+ $configReader->expects($this->once())
+ ->method('load')
+ ->willReturn([ConfigOptionsListConstants::KEY_MODULES => $currentConfig]);
+
+ $installer = $this->createMock(\Magento\Setup\Model\Installer::class);
+ $installer->expects($this->once())
+ ->method('getModulesConfig')
+ ->willReturn($correctConfig);
+
+ $installerFactory = $this->createMock(\Magento\Setup\Model\InstallerFactory::class);
+ $installerFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($installer);
+
+ $command = new ModuleConfigStatusCommand($configReader, $installerFactory);
+
+ $tester = new CommandTester($command);
+ $tester->execute([]);
+
+ $this->assertEquals($expectedOutput, $tester->getDisplay());
+ }
+
+ public function executeDataProvider()
+ {
+ $successMessage = 'The modules configuration is up to date.' . PHP_EOL;
+ $failureMessage = 'The modules configuration in the \'app/etc/config.php\' '
+ . 'file is outdated. Run \'setup:upgrade\' to fix it.' . PHP_EOL;
+
+ return [
+ [
+ ['Magento_ModuleA' => 1, 'Magento_ModuleB' => 1],
+ ['Magento_ModuleA' => 1, 'Magento_ModuleB' => 1],
+ $successMessage,
+ ],
+ [
+ ['Magento_ModuleA' => 0, 'Magento_ModuleB' => 1],
+ ['Magento_ModuleA' => 0, 'Magento_ModuleB' => 1],
+ $successMessage,
+ ],
+ [
+ ['Magento_ModuleA' => 1, 'Magento_ModuleB' => 1],
+ ['Magento_ModuleB' => 1, 'Magento_ModuleA' => 1],
+ $failureMessage,
+ ],
+ [
+ ['Magento_ModuleA' => 0, 'Magento_ModuleB' => 1],
+ ['Magento_ModuleB' => 1, 'Magento_ModuleA' => 0],
+ $failureMessage,
+ ],
+ [
+ ['Magento_ModuleA' => 1],
+ ['Magento_ModuleB' => 1, 'Magento_ModuleA' => 1],
+ $failureMessage,
+ ],
+ [
+ ['Magento_ModuleA' => 1, 'Magento_ModuleB' => 1],
+ ['Magento_ModuleB' => 1],
+ $failureMessage,
+ ],
+ ];
+ }
+}