diff --git a/core/register_command.php b/core/register_command.php
index 58aed05ba68a6..eb0992ba5bc7e 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -3,7 +3,7 @@
declare(strict_types=1);
/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016-2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -110,157 +110,294 @@
use OCP\Server;
use Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand;
-$application->add(new CompletionCommand());
-$application->add(Server::get(Status::class));
-$application->add(Server::get(Check::class));
-$application->add(Server::get(CreateJs::class));
-$application->add(Server::get(SignApp::class));
-$application->add(Server::get(SignCore::class));
-$application->add(Server::get(CheckApp::class));
-$application->add(Server::get(CheckCore::class));
-$application->add(Server::get(ListRoutes::class));
-$application->add(Server::get(MatchRoute::class));
-
-$config = Server::get(IConfig::class);
-
-if ($config->getSystemValueBool('installed', false)) {
- $application->add(Server::get(Disable::class));
- $application->add(Server::get(Enable::class));
- $application->add(Server::get(Install::class));
- $application->add(Server::get(GetPath::class));
- $application->add(Server::get(ListApps::class));
- $application->add(Server::get(Remove::class));
- $application->add(Server::get(Update::class));
-
- $application->add(Server::get(Cleanup::class));
- $application->add(Server::get(Enforce::class));
- $application->add(Server::get(Command\TwoFactorAuth\Enable::class));
- $application->add(Server::get(Command\TwoFactorAuth\Disable::class));
- $application->add(Server::get(State::class));
-
- $application->add(Server::get(Mode::class));
- $application->add(Server::get(Job::class));
- $application->add(Server::get(ListCommand::class));
- $application->add(Server::get(Delete::class));
- $application->add(Server::get(JobWorker::class));
-
- $application->add(Server::get(Test::class));
-
- $application->add(Server::get(DeleteConfig::class));
- $application->add(Server::get(GetConfig::class));
- $application->add(Server::get(SetConfig::class));
- $application->add(Server::get(Import::class));
- $application->add(Server::get(ListConfigs::class));
- $application->add(Server::get(Preset::class));
- $application->add(Server::get(Command\Config\System\DeleteConfig::class));
- $application->add(Server::get(Command\Config\System\GetConfig::class));
- $application->add(Server::get(Command\Config\System\SetConfig::class));
-
- $application->add(Server::get(File::class));
- $application->add(Server::get(Space::class));
- $application->add(Server::get(Storage::class));
- $application->add(Server::get(Storages::class));
-
- $application->add(Server::get(ConvertType::class));
- $application->add(Server::get(ConvertMysqlToMB4::class));
- $application->add(Server::get(ConvertFilecacheBigInt::class));
- $application->add(Server::get(AddMissingColumns::class));
- $application->add(Server::get(AddMissingIndices::class));
- $application->add(Server::get(AddMissingPrimaryKeys::class));
- $application->add(Server::get(ExpectedSchema::class));
- $application->add(Server::get(ExportSchema::class));
-
- $application->add(Server::get(GenerateMetadataCommand::class));
- $application->add(Server::get(PreviewCommand::class));
- if ($config->getSystemValueBool('debug', false)) {
- $application->add(Server::get(StatusCommand::class));
- $application->add(Server::get(MigrateCommand::class));
- $application->add(Server::get(GenerateCommand::class));
- $application->add(Server::get(ExecuteCommand::class));
+/**
+ * This file registers Nextcloud core console commands for Nextcloud's CLI application (OCC).
+ *
+ * Core commands are explicitly added in this file, resolving them from the DI container via
+ * Server::get(...).
+ *
+ * Some commands are always registered; many are only registered when the instance is already
+ * installed. Other commands are only registered when `occ` is executed in debug mode. A
+ * single "install" command is registered when the instance is not yet installed.
+ *
+ * The Symfony Console `$application` instance ris provided by the including scope (see
+ * OC\Console\Application::loadCommands).
+ *
+ * TODO (maybe):
+ * - Refactor this into a real class/service/callable w/ clear dependency handling/etc.
+ * - Make each core command a tagged service and have the container or a service aggregator
+ * auto-register them.
+ */
+
+// These variables are expected to be provided by the including scope (Application::loadCommands)
+/** @var \Symfony\Component\Console\Application $application */
+/** @var bool $installed */
+/** @var bool $maintenance */
+/** @var bool $needUpgrade */
+/** @var bool $debug */
+
+/*
+ * Commands that should always be registered (i.e. normal, pre-install, maintenance, upgrade needed)
+*/
+$alwaysCommands = [
+ CompletionCommand::class,
+ Status::class,
+ Check::class,
+ CreateJs::class,
+ SignApp::class,
+ SignCore::class,
+ CheckApp::class,
+ CheckCore::class,
+ ListRoutes::class,
+ MatchRoute::class,
+];
+
+/*
+ * Commands required when an upgrade is needed (besides above)
+ */
+$upgradeCommands = [
+ Command\Maintenance\Mode::class,
+ Upgrade::class,
+];
+
+/*
+ * Commands available only when not installed
+ */
+$installerCommands = [
+ Command\Maintenance\Install::class,
+];
+
+/*
+ * Commands allowed in maintenance mode (no apps loaded)
+ */
+$maintenanceCommands = [
+ Command\Maintenance\Mode::class,
+];
+
+/*
+ * Commands for normal (installed/up-to-date/non-maintenance) operating mode
+ */
+$installedCommands = [
+ // "app"
+ Disable::class,
+ Enable::class,
+ GetPath::class,
+ Install::class,
+ ListApps::class,
+ Remove::class,
+ Update::class,
+
+ // "background"
+ Mode::class,
+
+ // "background-job"
+ Delete::class,
+ Job::class,
+ JobWorker::class,
+ ListCommand::class,
+
+ // "broadcast"
+ Test::class,
+
+ // "config"
+ Import::class,
+ ListConfigs::class,
+ Preset::class,
+
+ // "config:app"
+ DeleteConfig::class,
+ GetConfig::class,
+ SetConfig::class,
+
+ // "config:system"
+ Command\Config\System\DeleteConfig::class,
+ Command\Config\System\GetConfig::class,
+ Command\Config\System\SetConfig::class,
+
+ // "db"
+ AddMissingColumns::class,
+ AddMissingIndices::class,
+ AddMissingPrimaryKeys::class,
+ ConvertFilecacheBigInt::class,
+ ConvertMysqlToMB4::class,
+ ConvertType::class,
+ ExpectedSchema::class,
+ ExportSchema::class,
+
+ // "encryption"
+ ChangeKeyStorageRoot::class,
+ DecryptAll::class,
+ Command\Encryption\Disable::class,
+ Command\Encryption\Enable::class,
+ EncryptAll::class,
+ ListModules::class,
+ MigrateKeyStorage::class,
+ SetDefaultModule::class,
+ ShowKeyStorageRoot::class,
+ Command\Encryption\Status::class,
+
+ // "group"
+ Command\Group\Add::class,
+ AddUser::class,
+ Command\Group\Delete::class,
+ Command\Group\Info::class,
+ Command\Group\ListCommand::class,
+ RemoveUser::class,
+
+ // "info"
+ File::class,
+ Space::class,
+ Storage::class,
+ Storages::class,
+
+ // "log"
+ Command\Log\File::class,
+ Manage::class,
+
+ // "maintenance"
+ DataFingerprint::class,
+ Command\Maintenance\Mode::class,
+ Repair::class,
+ RepairShareOwnership::class,
+ UpdateTheme::class,
+ UpdateHtaccess::class,
+
+ // "maintenance:mimetype"
+ UpdateDB::class,
+ UpdateJS::class,
+
+ // "memcache""
+ RedisCommand::class, // TODO: Should probably be moved under debug; it's not currently gated
+ DistributedClear::class, // ditto
+ DistributedDelete::class, // ditto
+ DistributedGet::class, // ditto probably
+ DistributedSet::class, // ditto
+
+ // "metadata"
+ Get::class,
+
+ // "migrations"
+ GenerateMetadataCommand::class,
+ PreviewCommand::class,
+
+ // "preview"
+ Command\Preview\Cleanup::class,
+ Generate::class,
+ ResetRenderedTexts::class,
+
+ // "tag"
+ Command\SystemTag\Add::class,
+ Command\SystemTag\Delete::class,
+ Edit::class,
+ Command\SystemTag\ListCommand::class,
+
+ // "twofactorauth"
+ Cleanup::class,
+ Command\TwoFactorAuth\Disable::class,
+ Command\TwoFactorAuth\Enable::class,
+ Enforce::class,
+ State::class,
+
+ // "security:bruteforce"
+ BruteforceAttempts::class,
+ BruteforceResetAttempts::class,
+
+ // "security:certificates"
+ ListCertificates::class,
+ ExportCertificates::class,
+ ImportCertificate::class,
+ RemoveCertificate::class,
+
+ // "setupchecks"
+ SetupChecks::class,
+
+ // "snowflake"
+ SnowflakeDecodeId::class,
+
+ // "taskprocessing"
+ EnabledCommand::class,
+ Command\TaskProcessing\Cleanup::class,
+ GetCommand::class,
+ Command\TaskProcessing\ListCommand::class,
+ Statistics::class,
+
+ // "user"
+ Add::class,
+ Command\User\AuthTokens\Add::class,
+ Command\User\AuthTokens\Delete::class,
+ Command\User\AuthTokens\ListCommand::class,
+ ClearGeneratedAvatarCacheCommand::class,
+ Command\User\Delete::class,
+ Command\User\Disable::class,
+ Command\User\Enable::class,
+ Info::class,
+ Verify::class,
+ LastSeen::class,
+ Command\User\ListCommand::class,
+ Profile::class,
+ Report::class,
+ ResetPassword::class,
+ Setting::class,
+ SyncAccountDataCommand::class,
+ Welcome::class,
+];
+
+/*
+ * Debug-mode only commands
+ */
+$debugCommands = [
+ // "migrations"
+ ExecuteCommand::class,
+ GenerateCommand::class,
+ MigrateCommand::class,
+ StatusCommand::class,
+];
+
+/**
+ * Helper to resolve & add a list of command classes.
+ *
+ * Will abort registering if any app/service fails to load.
+ *
+ */
+/** @var \Symfony\Component\Console\Application $application */
+$addCommands = function (array $classes) use ($application) {
+ foreach ($classes as $class) {
+ // CompletionCommand is instantiated directly (not resolved from container).
+ if ($class === CompletionCommand::class) {
+ $application->add(new CompletionCommand());
+ } else {
+ $application->add(Server::get($class));
+ }
}
+};
+
+/*
+ * Register commands according to state
+ */
+
+// Register always available commands
+$addCommands($alwaysCommands);
+
+if ($needUpgrade) {
+ // Register minimal extra commands needed to perform or diagnose the upgrade.
+ $addCommands($upgradeCommands);
+ return;
+}
+
+if (!$installed) {
+ // Register pre-install only commands
+ $addCommands($installerCommands);
+ return;
+}
+
+if ($maintenance) {
+ $addCommands($maintenanceCommands);
+ return;
+}
+
+// Normal installed & not maintenance path
+$addCommands($installedCommands);
- $application->add(Server::get(Command\Encryption\Disable::class));
- $application->add(Server::get(Command\Encryption\Enable::class));
- $application->add(Server::get(ListModules::class));
- $application->add(Server::get(SetDefaultModule::class));
- $application->add(Server::get(Command\Encryption\Status::class));
- $application->add(Server::get(EncryptAll::class));
- $application->add(Server::get(DecryptAll::class));
-
- $application->add(Server::get(Manage::class));
- $application->add(Server::get(Command\Log\File::class));
-
- $application->add(Server::get(ChangeKeyStorageRoot::class));
- $application->add(Server::get(ShowKeyStorageRoot::class));
- $application->add(Server::get(MigrateKeyStorage::class));
-
- $application->add(Server::get(DataFingerprint::class));
- $application->add(Server::get(UpdateDB::class));
- $application->add(Server::get(UpdateJS::class));
- $application->add(Server::get(Command\Maintenance\Mode::class));
- $application->add(Server::get(UpdateHtaccess::class));
- $application->add(Server::get(UpdateTheme::class));
-
- $application->add(Server::get(Upgrade::class));
- $application->add(Server::get(Repair::class));
- $application->add(Server::get(RepairShareOwnership::class));
-
- $application->add(Server::get(Command\Preview\Cleanup::class));
- $application->add(Server::get(Generate::class));
- $application->add(Server::get(ResetRenderedTexts::class));
-
- $application->add(Server::get(Add::class));
- $application->add(Server::get(Command\User\Delete::class));
- $application->add(Server::get(Command\User\Disable::class));
- $application->add(Server::get(Command\User\Enable::class));
- $application->add(Server::get(LastSeen::class));
- $application->add(Server::get(Report::class));
- $application->add(Server::get(ResetPassword::class));
- $application->add(Server::get(Setting::class));
- $application->add(Server::get(Profile::class));
- $application->add(Server::get(Command\User\ListCommand::class));
- $application->add(Server::get(ClearGeneratedAvatarCacheCommand::class));
- $application->add(Server::get(Info::class));
- $application->add(Server::get(SyncAccountDataCommand::class));
- $application->add(Server::get(Command\User\AuthTokens\Add::class));
- $application->add(Server::get(Command\User\AuthTokens\ListCommand::class));
- $application->add(Server::get(Command\User\AuthTokens\Delete::class));
- $application->add(Server::get(Verify::class));
- $application->add(Server::get(Welcome::class));
-
- $application->add(Server::get(Command\Group\Add::class));
- $application->add(Server::get(Command\Group\Delete::class));
- $application->add(Server::get(Command\Group\ListCommand::class));
- $application->add(Server::get(AddUser::class));
- $application->add(Server::get(RemoveUser::class));
- $application->add(Server::get(Command\Group\Info::class));
-
- $application->add(Server::get(Command\SystemTag\ListCommand::class));
- $application->add(Server::get(Command\SystemTag\Delete::class));
- $application->add(Server::get(Command\SystemTag\Add::class));
- $application->add(Server::get(Edit::class));
-
- $application->add(Server::get(ListCertificates::class));
- $application->add(Server::get(ExportCertificates::class));
- $application->add(Server::get(ImportCertificate::class));
- $application->add(Server::get(RemoveCertificate::class));
- $application->add(Server::get(BruteforceAttempts::class));
- $application->add(Server::get(BruteforceResetAttempts::class));
- $application->add(Server::get(SetupChecks::class));
- $application->add(Server::get(SnowflakeDecodeId::class));
- $application->add(Server::get(Get::class));
-
- $application->add(Server::get(GetCommand::class));
- $application->add(Server::get(EnabledCommand::class));
- $application->add(Server::get(Command\TaskProcessing\ListCommand::class));
- $application->add(Server::get(Statistics::class));
- $application->add(Server::get(Command\TaskProcessing\Cleanup::class));
-
- $application->add(Server::get(RedisCommand::class));
- $application->add(Server::get(DistributedClear::class));
- $application->add(Server::get(DistributedDelete::class));
- $application->add(Server::get(DistributedGet::class));
- $application->add(Server::get(DistributedSet::class));
-} else {
- $application->add(Server::get(Command\Maintenance\Install::class));
+if ($debug) {
+ $addCommands($debugCommands);
}
diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php
index 4cf1e0da8ca13..5a2e3a1e3fb82 100644
--- a/lib/private/Console/Application.php
+++ b/lib/private/Console/Application.php
@@ -1,7 +1,7 @@
application = new SymfonyApplication($defaults->getName(), $serverVersion->getVersionString());
+ $this->application = new SymfonyApplication(
+ $defaults->getName(),
+ $serverVersion->getVersionString()
+ );
}
/**
+ * Loads relevant core and, if applicable, app commands.
+ *
* @throws \Exception
*/
public function loadCommands(
InputInterface $input,
ConsoleOutputInterface $output,
): void {
- // $application is required to be defined in the register_command scripts
+ $this->checkEnvironmentEssentials($input, $output);
+
+ // Variables utilized by downstream `require_once */register_command.php` files (i.e. core)
$application = $this->application;
+ // State flags used to determine which commands to load, checks to do, and warnings/errors to show.
+ $installed = (bool) $this->config->getSystemValueBool('installed', false);
+ $maintenance = (bool) ($installed && $this->config->getSystemValueBool('maintenance', false));
+ $needUpgrade = (bool) ($installed && \OCP\Util::needUpgrade());
+ /**
+ * @var bool $debug Used by core/register_command.php (required file reads this local)
+ * @psalm-suppress UnusedVariable
+ * @noinspection PhpUnusedLocalVariableInspection
+ */
+ $debug = (bool) $this->config->getSystemValueBool('debug', false);
+
+ // Add and handle `--no-warnings` by default regardless of command
$inputDefinition = $application->getDefinition();
$inputDefinition->addOption(
new InputOption(
@@ -63,104 +81,138 @@ public function loadCommands(
null
)
);
- try {
- $input->bind($inputDefinition);
- } catch (\RuntimeException $e) {
- //expected if there are extra options
- }
- if ($input->getOption('no-warnings')) {
+ // Note: environment errors (above) are still shown
+ if ($input->hasParameterOption('--no-warnings', true)) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
}
- if ($this->memoryInfo->isMemoryLimitSufficient() === false) {
- $output->getErrorOutput()->writeln(
- 'The current PHP memory limit '
- . 'is below the recommended value of 512MB.'
- );
+ $this->writeMemoryCheckInfoIfLow($input, $output);
+
+ // Load core commands
+ /** @var \Symfony\Component\Console\Application $application */
+ require_once __DIR__ . '/../../../core/register_command.php';
+
+ if ($needUpgrade) {
+ $this->writeNeedsUpdateInfo($input, $output);
+ } elseif (!$installed) {
+ $this->writeNotInstalledInfo($input, $output);
+ } elseif ($maintenance) {
+ $this->writeMaintenanceModeInfo($input, $output);
+ } else {
+ // Normal installed path
+ $this->loadAppCommands($input, $output);
}
+ }
- try {
- require_once __DIR__ . '/../../../core/register_command.php';
- if ($this->config->getSystemValueBool('installed', false)) {
- if (\OCP\Util::needUpgrade()) {
- throw new NeedsUpdateException();
- } elseif ($this->config->getSystemValueBool('maintenance')) {
- $this->writeMaintenanceModeInfo($input, $output);
- } else {
- $this->appManager->loadApps();
- foreach ($this->appManager->getEnabledApps() as $app) {
- try {
- $appPath = $this->appManager->getAppPath($app);
- } catch (AppPathNotFoundException) {
- continue;
- }
- // load commands using info.xml
- $info = $this->appManager->getAppInfo($app);
- if (isset($info['commands'])) {
- try {
- $this->loadCommandsFromInfoXml($info['commands']);
- } catch (\Throwable $e) {
- $output->writeln('' . $e->getMessage() . '');
- $this->logger->error($e->getMessage(), [
- 'exception' => $e,
- ]);
- }
- }
- // load from register_command.php
- \OC_App::registerAutoloading($app, $appPath);
- $file = $appPath . '/appinfo/register_command.php';
- if (file_exists($file)) {
- try {
- require $file;
- } catch (\Exception $e) {
- $this->logger->error($e->getMessage(), [
- 'exception' => $e,
- ]);
- }
- }
- }
- }
- } elseif ($input->getArgument('command') !== '_completion' && $input->getArgument('command') !== 'maintenance:install') {
+ /**
+ * Essential environment checks that must pass except in rare cases.
+ *
+ * @throws \Exception if checks fail and command isn't whitelisted.
+ */
+ private function checkEnvironmentEssentials(InputInterface $input, ConsoleOutputInterface $output): void {
+ $cmd = (string)$input->getFirstArgument();
+ $errors = \OC_Util::checkServer(Server::get(SystemConfig::class));
+ if (!empty($errors)) {
+ foreach ($errors as $error) {
$errorOutput = $output->getErrorOutput();
- $errorOutput->writeln('Nextcloud is not installed - only a limited number of commands are available');
+ $errorOutput->writeln('' . (string)$error['error'] . '');
+ $errorOutput->writeln('' . (string)$error['hint'] . '');
+ $errorOutput->writeln('');
}
- } catch (NeedsUpdateException) {
- if ($input->getArgument('command') !== '_completion' && $input->getArgument('command') !== 'upgrade') {
- $errorOutput = $output->getErrorOutput();
- $errorOutput->writeln('Nextcloud or one of the apps require upgrade - only a limited number of commands are available');
- $errorOutput->writeln('You may use your browser or the occ upgrade command to do the upgrade');
+
+ // Command exceptions we let proceed even if environment fails essential checks
+ // Note: For (conservative) backwards compatibility; other than 'check' most of these are likely unnecessary...
+ // ...they can't be used to fix these types of errors anyhow!
+ //
+ // TODO: Remove all but 'check'.
+ $whitelist = [ 'check', 'upgrade', 'maintenance:mode', 'status', '_completion' ];
+ if (!in_array($cmd, $whitelist, true)) {
+ throw new \Exception(
+ 'Environment not properly prepared for Nextcloud. Errors should be fixed before proceeding. ' .
+ 'Please refer to the Admin Manual to correct the above error(s) then retry your "occ" command.');
}
}
+ }
+
+ private function writeMemoryCheckInfoIfLow(InputInterface $input, ConsoleOutputInterface $output): void {
+ if ($this->memoryInfo->isMemoryLimitSufficient() === false) {
+ $currentLimit = trim((string)ini_get('memory_limit')); // we want the nearly raw ini value to show bogus values too
+ $recommendedLimit = '512M';
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln(
+ 'The PHP-CLI memory limit (' . $currentLimit . ') is below the recommended ' . $recommendedLimit .
+ '. Some functions may fail.');
+ $errorOutput->writeln(
+ 'Please adjust your "memory_limit" setting in the relevant php.ini file to at least ' .
+ $recommendedLimit . ' to prevent potential errors.');
+ }
+ }
+
+ /**
+ * @throws \Throwable if unable to load commands specified in an app's info.xml
+ */
+ private function loadAppCommands(InputInterface $input, ConsoleOutputInterface $output): void {
+ $this->appManager->loadApps();
- if ($input->getFirstArgument() !== 'check') {
- $errors = \OC_Util::checkServer(Server::get(SystemConfig::class));
- if (!empty($errors)) {
- foreach ($errors as $error) {
- $output->writeln((string)$error['error']);
- $output->writeln((string)$error['hint']);
- $output->writeln('');
+ foreach ($this->appManager->getEnabledApps() as $app) {
+ try {
+ $appPath = $this->appManager->getAppPath($app);
+ } catch (AppPathNotFoundException) {
+ continue;
+ }
+
+ // load commands using info.xml
+ $info = $this->appManager->getAppInfo($app);
+ if (isset($info['commands'])) {
+ try {
+ $this->loadCommandsFromInfoXml($info['commands']);
+ } catch (\Throwable $e) {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('' . $e->getMessage() . '');
+ $this->logger->error($e->getMessage(), [ 'exception' => $e, ]);
}
- throw new \Exception('Environment not properly prepared.');
}
+
+ // load from app's register_command.php if present
+ \OC_App::registerAutoloading($app, $appPath);
+ $file = $appPath . '/appinfo/register_command.php';
+ if (file_exists($file)) {
+ try {
+ require $file;
+ } catch (\Throwable $e) {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('' . $e->getMessage() . '');
+ $this->logger->error($e->getMessage(), [ 'exception' => $e, ]);
+ }
+ }
+ }
+ }
+
+ private function writeNeedsUpdateInfo(InputInterface $input, ConsoleOutputInterface $output): void {
+ $cmd = (string)$input->getFirstArgument();
+ if ($cmd !== '_completion' && $cmd !== 'upgrade') {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('Nextcloud or one of its apps requires an upgrade. Only a limited number of commands are available.');
+ $errorOutput->writeln('Please use your browser or the "occ upgrade" command to finish the upgrade.');
+ }
+ }
+
+ private function writeNotInstalledInfo(InputInterface $input, ConsoleOutputInterface $output): void {
+ $cmd = (string)$input->getFirstArgument();
+ if ($cmd !== '_completion' && $cmd !== 'maintenance:install') {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('Nextcloud is not installed. Only a limited number of commands are available.');
+ $errorOutput->writeln('Please use your browser or the "occ maintenance:install" command to proceed with installation.');
}
}
- /**
- * Write a maintenance mode info.
- * The commands "_completion" and "maintenance:mode" are excluded.
- *
- * @param InputInterface $input The input implementation for reading inputs.
- * @param ConsoleOutputInterface $output The output implementation
- * for writing outputs.
- * @return void
- */
private function writeMaintenanceModeInfo(InputInterface $input, ConsoleOutputInterface $output): void {
- if ($input->getArgument('command') !== '_completion'
- && $input->getArgument('command') !== 'maintenance:mode'
- && $input->getArgument('command') !== 'status') {
- $errOutput = $output->getErrorOutput();
- $errOutput->writeln('Nextcloud is in maintenance mode, no apps are loaded.');
- $errOutput->writeln('Commands provided by apps are unavailable.');
+ $cmd = (string)$input->getFirstArgument();
+ if ($cmd !== '_completion' && $cmd !== 'maintenance:mode' && $cmd !== 'status') {
+ $errorOutput = $output->getErrorOutput();
+ $errorOutput->writeln('Nextcloud is in maintenance mode. Only a limited number of commands are available.');
+ $errorOutput->writeln('In maintenance mode logins are blocked and events may not be triggered since apps are not loaded.');
+ $errorOutput->writeln('Proceed with maintenance activities then use the "occ maintenance:mode --off" to disable maintenance mode.');
}
}
@@ -174,10 +226,9 @@ public function setAutoExit(bool $boolean): void {
}
/**
- * @return int
* @throws \Exception
*/
- public function run(?InputInterface $input = null, ?OutputInterface $output = null) {
+ public function run(?InputInterface $input = null, ?OutputInterface $output = null): int {
$event = new ConsoleEvent(
ConsoleEvent::EVENT_RUN,
$this->request->server['argv']
@@ -198,8 +249,8 @@ private function loadCommandsFromInfoXml(iterable $commands): void {
if (class_exists($command)) {
try {
$c = new $command();
- } catch (ArgumentCountError) {
- throw new \Exception("Failed to construct console command '$command': " . $e->getMessage(), 0, $e);
+ } catch (ArgumentCountError $ace) {
+ throw new \Exception("Failed to construct console command '$command': " . $ace->getMessage(), 0, $ace);
}
} else {
throw new \Exception("Console command '$command' is unknown and could not be loaded");