aboutsummaryrefslogtreecommitdiffstats
path: root/core/Command/App
diff options
context:
space:
mode:
Diffstat (limited to 'core/Command/App')
-rw-r--r--core/Command/App/Disable.php83
-rw-r--r--core/Command/App/Enable.php151
-rw-r--r--core/Command/App/GetPath.php70
-rw-r--r--core/Command/App/Install.php84
-rw-r--r--core/Command/App/ListApps.php145
-rw-r--r--core/Command/App/Remove.php124
-rw-r--r--core/Command/App/Update.php119
7 files changed, 776 insertions, 0 deletions
diff --git a/core/Command/App/Disable.php b/core/Command/App/Disable.php
new file mode 100644
index 00000000000..121ad3f010c
--- /dev/null
+++ b/core/Command/App/Disable.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Core\Command\App;
+
+use OCP\App\IAppManager;
+use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
+use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Disable extends Command implements CompletionAwareInterface {
+ protected int $exitCode = 0;
+
+ public function __construct(
+ protected IAppManager $appManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('app:disable')
+ ->setDescription('disable an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED | InputArgument::IS_ARRAY,
+ 'disable the specified app'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $appIds = $input->getArgument('app-id');
+
+ foreach ($appIds as $appId) {
+ $this->disableApp($appId, $output);
+ }
+
+ return $this->exitCode;
+ }
+
+ private function disableApp(string $appId, OutputInterface $output): void {
+ if ($this->appManager->isEnabledForAnyone($appId) === false) {
+ $output->writeln('No such app enabled: ' . $appId);
+ return;
+ }
+
+ try {
+ $this->appManager->disableApp($appId);
+ $appVersion = $this->appManager->getAppVersion($appId);
+ $output->writeln($appId . ' ' . $appVersion . ' disabled');
+ } catch (\Exception $e) {
+ $output->writeln($e->getMessage());
+ $this->exitCode = 2;
+ }
+ }
+
+ /**
+ * @param string $optionName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeOptionValues($optionName, CompletionContext $context): array {
+ return [];
+ }
+
+ /**
+ * @param string $argumentName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeArgumentValues($argumentName, CompletionContext $context): array {
+ if ($argumentName === 'app-id') {
+ return array_diff(\OC_App::getEnabledApps(true, true), $this->appManager->getAlwaysEnabledApps());
+ }
+ return [];
+ }
+}
diff --git a/core/Command/App/Enable.php b/core/Command/App/Enable.php
new file mode 100644
index 00000000000..3936acfbf6e
--- /dev/null
+++ b/core/Command/App/Enable.php
@@ -0,0 +1,151 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Core\Command\App;
+
+use OC\Installer;
+use OCP\App\AppPathNotFoundException;
+use OCP\App\IAppManager;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
+use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Enable extends Command implements CompletionAwareInterface {
+ protected int $exitCode = 0;
+
+ public function __construct(
+ protected IAppManager $appManager,
+ protected IGroupManager $groupManager,
+ private Installer $installer,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('app:enable')
+ ->setDescription('enable an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED | InputArgument::IS_ARRAY,
+ 'enable the specified app'
+ )
+ ->addOption(
+ 'groups',
+ 'g',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'enable the app only for a list of groups'
+ )
+ ->addOption(
+ 'force',
+ 'f',
+ InputOption::VALUE_NONE,
+ 'enable the app regardless of the Nextcloud version requirement'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $appIds = $input->getArgument('app-id');
+ $groups = $this->resolveGroupIds($input->getOption('groups'));
+ $forceEnable = (bool)$input->getOption('force');
+
+ foreach ($appIds as $appId) {
+ $this->enableApp($appId, $groups, $forceEnable, $output);
+ }
+
+ return $this->exitCode;
+ }
+
+ /**
+ * @param string $appId
+ * @param array $groupIds
+ * @param bool $forceEnable
+ * @param OutputInterface $output
+ */
+ private function enableApp(string $appId, array $groupIds, bool $forceEnable, OutputInterface $output): void {
+ $groupNames = array_map(function (IGroup $group) {
+ return $group->getDisplayName();
+ }, $groupIds);
+
+ if ($this->appManager->isEnabledForUser($appId) && $groupIds === []) {
+ $output->writeln($appId . ' already enabled');
+ return;
+ }
+
+ try {
+ if ($this->installer->isDownloaded($appId) === false) {
+ $this->installer->downloadApp($appId);
+ }
+
+ $this->installer->installApp($appId, $forceEnable);
+ $appVersion = $this->appManager->getAppVersion($appId);
+
+ if ($groupIds === []) {
+ $this->appManager->enableApp($appId, $forceEnable);
+ $output->writeln($appId . ' ' . $appVersion . ' enabled');
+ } else {
+ $this->appManager->enableAppForGroups($appId, $groupIds, $forceEnable);
+ $output->writeln($appId . ' ' . $appVersion . ' enabled for groups: ' . implode(', ', $groupNames));
+ }
+ } catch (AppPathNotFoundException $e) {
+ $output->writeln($appId . ' not found');
+ $this->exitCode = 1;
+ } catch (\Exception $e) {
+ $output->writeln($e->getMessage());
+ $this->exitCode = 1;
+ }
+ }
+
+ /**
+ * @param array $groupIds
+ * @return array
+ */
+ private function resolveGroupIds(array $groupIds): array {
+ $groups = [];
+ foreach ($groupIds as $groupId) {
+ $group = $this->groupManager->get($groupId);
+ if ($group instanceof IGroup) {
+ $groups[] = $group;
+ }
+ }
+ return $groups;
+ }
+
+ /**
+ * @param string $optionName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeOptionValues($optionName, CompletionContext $context): array {
+ if ($optionName === 'groups') {
+ return array_map(function (IGroup $group) {
+ return $group->getGID();
+ }, $this->groupManager->search($context->getCurrentWord()));
+ }
+ return [];
+ }
+
+ /**
+ * @param string $argumentName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeArgumentValues($argumentName, CompletionContext $context): array {
+ if ($argumentName === 'app-id') {
+ $allApps = $this->appManager->getAllAppsInAppsFolders();
+ return array_diff($allApps, \OC_App::getEnabledApps(true, true));
+ }
+ return [];
+ }
+}
diff --git a/core/Command/App/GetPath.php b/core/Command/App/GetPath.php
new file mode 100644
index 00000000000..3ba4ed7781b
--- /dev/null
+++ b/core/Command/App/GetPath.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Core\Command\App;
+
+use OC\Core\Command\Base;
+use OCP\App\AppPathNotFoundException;
+use OCP\App\IAppManager;
+use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class GetPath extends Base {
+ public function __construct(
+ protected IAppManager $appManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+
+ $this
+ ->setName('app:getpath')
+ ->setDescription('Get an absolute path to the app directory')
+ ->addArgument(
+ 'app',
+ InputArgument::REQUIRED,
+ 'Name of the app'
+ )
+ ;
+ }
+
+ /**
+ * Executes the current command.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ * @return int 0 if everything went fine, or an error code
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $appName = $input->getArgument('app');
+ try {
+ $path = $this->appManager->getAppPath($appName);
+ } catch (AppPathNotFoundException) {
+ // App not found, exit with non-zero
+ return self::FAILURE;
+ }
+ $output->writeln($path);
+ return self::SUCCESS;
+ }
+
+ /**
+ * @param string $argumentName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeArgumentValues($argumentName, CompletionContext $context): array {
+ if ($argumentName === 'app') {
+ return $this->appManager->getAllAppsInAppsFolders();
+ }
+ return [];
+ }
+}
diff --git a/core/Command/App/Install.php b/core/Command/App/Install.php
new file mode 100644
index 00000000000..c8a396c8e36
--- /dev/null
+++ b/core/Command/App/Install.php
@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Core\Command\App;
+
+use OC\Installer;
+use OCP\App\IAppManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Install extends Command {
+ public function __construct(
+ protected IAppManager $appManager,
+ private Installer $installer,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('app:install')
+ ->setDescription('install an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED,
+ 'install the specified app'
+ )
+ ->addOption(
+ 'keep-disabled',
+ null,
+ InputOption::VALUE_NONE,
+ 'don\'t enable the app afterwards'
+ )
+ ->addOption(
+ 'force',
+ 'f',
+ InputOption::VALUE_NONE,
+ 'install the app regardless of the Nextcloud version requirement'
+ )
+ ->addOption(
+ 'allow-unstable',
+ null,
+ InputOption::VALUE_NONE,
+ 'allow installing an unstable releases'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $appId = $input->getArgument('app-id');
+ $forceEnable = (bool)$input->getOption('force');
+
+ if ($this->appManager->isEnabledForAnyone($appId)) {
+ $output->writeln($appId . ' already installed');
+ return 1;
+ }
+
+ try {
+ $this->installer->downloadApp($appId, $input->getOption('allow-unstable'));
+ $result = $this->installer->installApp($appId, $forceEnable);
+ } catch (\Exception $e) {
+ $output->writeln('Error: ' . $e->getMessage());
+ return 1;
+ }
+
+ $appVersion = $this->appManager->getAppVersion($appId);
+ $output->writeln($appId . ' ' . $appVersion . ' installed');
+
+ if (!$input->getOption('keep-disabled')) {
+ $this->appManager->enableApp($appId);
+ $output->writeln($appId . ' enabled');
+ }
+
+ return 0;
+ }
+}
diff --git a/core/Command/App/ListApps.php b/core/Command/App/ListApps.php
new file mode 100644
index 00000000000..dc947bea55f
--- /dev/null
+++ b/core/Command/App/ListApps.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC\Core\Command\App;
+
+use OC\Core\Command\Base;
+use OCP\App\IAppManager;
+use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListApps extends Base {
+ public function __construct(
+ protected IAppManager $appManager,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+
+ $this
+ ->setName('app:list')
+ ->setDescription('List all available apps')
+ ->addOption(
+ 'shipped',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'true - limit to shipped apps only, false - limit to non-shipped apps only'
+ )
+ ->addOption(
+ 'enabled',
+ null,
+ InputOption::VALUE_NONE,
+ 'shows only enabled apps'
+ )
+ ->addOption(
+ 'disabled',
+ null,
+ InputOption::VALUE_NONE,
+ 'shows only disabled apps'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ if ($input->getOption('shipped') === 'true' || $input->getOption('shipped') === 'false') {
+ $shippedFilter = $input->getOption('shipped') === 'true';
+ } else {
+ $shippedFilter = null;
+ }
+
+ $showEnabledApps = $input->getOption('enabled') || !$input->getOption('disabled');
+ $showDisabledApps = $input->getOption('disabled') || !$input->getOption('enabled');
+
+ $apps = $this->appManager->getAllAppsInAppsFolders();
+ $enabledApps = $disabledApps = [];
+ $versions = $this->appManager->getAppInstalledVersions();
+
+ //sort enabled apps above disabled apps
+ foreach ($apps as $app) {
+ if ($shippedFilter !== null && $this->appManager->isShipped($app) !== $shippedFilter) {
+ continue;
+ }
+ if ($this->appManager->isEnabledForAnyone($app)) {
+ $enabledApps[] = $app;
+ } else {
+ $disabledApps[] = $app;
+ }
+ }
+
+ $apps = [];
+
+ if ($showEnabledApps) {
+ $apps['enabled'] = [];
+
+ sort($enabledApps);
+ foreach ($enabledApps as $app) {
+ $apps['enabled'][$app] = $versions[$app] ?? true;
+ }
+ }
+
+ if ($showDisabledApps) {
+ $apps['disabled'] = [];
+
+ sort($disabledApps);
+ foreach ($disabledApps as $app) {
+ $apps['disabled'][$app] = $this->appManager->getAppVersion($app) . (isset($versions[$app]) ? ' (installed ' . $versions[$app] . ')' : '');
+ }
+ }
+
+ $this->writeAppList($input, $output, $apps);
+ return 0;
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ */
+ protected function writeAppList(InputInterface $input, OutputInterface $output, $items): void {
+ switch ($input->getOption('output')) {
+ case self::OUTPUT_FORMAT_PLAIN:
+ if (isset($items['enabled'])) {
+ $output->writeln('Enabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['enabled']);
+ }
+
+ if (isset($items['disabled'])) {
+ $output->writeln('Disabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['disabled']);
+ }
+ break;
+
+ default:
+ parent::writeArrayInOutputFormat($input, $output, $items);
+ break;
+ }
+ }
+
+ /**
+ * @param string $optionName
+ * @param CompletionContext $context
+ * @return array
+ */
+ public function completeOptionValues($optionName, CompletionContext $context): array {
+ if ($optionName === 'shipped') {
+ return ['true', 'false'];
+ }
+ return [];
+ }
+
+ /**
+ * @param string $argumentName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeArgumentValues($argumentName, CompletionContext $context): array {
+ return [];
+ }
+}
diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php
new file mode 100644
index 00000000000..d43bfa96ccc
--- /dev/null
+++ b/core/Command/App/Remove.php
@@ -0,0 +1,124 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Core\Command\App;
+
+use OC\Installer;
+use OCP\App\IAppManager;
+use Psr\Log\LoggerInterface;
+use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
+use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Throwable;
+
+class Remove extends Command implements CompletionAwareInterface {
+ public function __construct(
+ protected IAppManager $manager,
+ private Installer $installer,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('app:remove')
+ ->setDescription('remove an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED,
+ 'remove the specified app'
+ )
+ ->addOption(
+ 'keep-data',
+ null,
+ InputOption::VALUE_NONE,
+ 'keep app data and do not remove them'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $appId = $input->getArgument('app-id');
+
+ // Check if the app is enabled
+ if (!$this->manager->isEnabledForAnyone($appId)) {
+ $output->writeln($appId . ' is not enabled');
+ return 1;
+ }
+
+ // Removing shipped apps is not possible, therefore we pre-check that
+ // before trying to remove it
+ if ($this->manager->isShipped($appId)) {
+ $output->writeln($appId . ' could not be removed as it is a shipped app');
+ return 1;
+ }
+
+ // If we want to keep the data of the app, we simply don't disable it here.
+ // App uninstall tasks are being executed when disabled. More info: PR #11627.
+ if (!$input->getOption('keep-data')) {
+ try {
+ $this->manager->disableApp($appId);
+ $output->writeln($appId . ' disabled');
+ } catch (Throwable $e) {
+ $output->writeln('<error>Error: ' . $e->getMessage() . '</error>');
+ $this->logger->error($e->getMessage(), [
+ 'app' => 'CLI',
+ 'exception' => $e,
+ ]);
+ return 1;
+ }
+ }
+
+ // Let's try to remove the app...
+ try {
+ $result = $this->installer->removeApp($appId);
+ } catch (Throwable $e) {
+ $output->writeln('<error>Error: ' . $e->getMessage() . '</error>');
+ $this->logger->error($e->getMessage(), [
+ 'app' => 'CLI',
+ 'exception' => $e,
+ ]);
+ return 1;
+ }
+
+ if ($result === false) {
+ $output->writeln($appId . ' could not be removed');
+ return 1;
+ }
+
+ $appVersion = $this->manager->getAppVersion($appId);
+ $output->writeln($appId . ' ' . $appVersion . ' removed');
+
+ return 0;
+ }
+
+ /**
+ * @param string $optionName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeOptionValues($optionName, CompletionContext $context): array {
+ return [];
+ }
+
+ /**
+ * @param string $argumentName
+ * @param CompletionContext $context
+ * @return string[]
+ */
+ public function completeArgumentValues($argumentName, CompletionContext $context): array {
+ if ($argumentName === 'app-id') {
+ return $this->manager->getEnabledApps();
+ }
+ return [];
+ }
+}
diff --git a/core/Command/App/Update.php b/core/Command/App/Update.php
new file mode 100644
index 00000000000..71c7f84e5b0
--- /dev/null
+++ b/core/Command/App/Update.php
@@ -0,0 +1,119 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Core\Command\App;
+
+use OC\Installer;
+use OCP\App\AppPathNotFoundException;
+use OCP\App\IAppManager;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Update extends Command {
+ public function __construct(
+ protected IAppManager $manager,
+ private Installer $installer,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('app:update')
+ ->setDescription('update an app or all apps')
+ ->addArgument(
+ 'app-id',
+ InputArgument::OPTIONAL,
+ 'update the specified app'
+ )
+ ->addOption(
+ 'all',
+ null,
+ InputOption::VALUE_NONE,
+ 'update all updatable apps'
+ )
+ ->addOption(
+ 'showonly',
+ null,
+ InputOption::VALUE_NONE,
+ 'show update(s) without updating'
+ )
+ ->addOption(
+ 'allow-unstable',
+ null,
+ InputOption::VALUE_NONE,
+ 'allow updating to unstable releases'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $singleAppId = $input->getArgument('app-id');
+ $updateFound = false;
+
+ if ($singleAppId) {
+ $apps = [$singleAppId];
+ try {
+ $this->manager->getAppPath($singleAppId);
+ } catch (AppPathNotFoundException $e) {
+ $output->writeln($singleAppId . ' not installed');
+ return 1;
+ }
+ } elseif ($input->getOption('all') || $input->getOption('showonly')) {
+ $apps = $this->manager->getAllAppsInAppsFolders();
+ } else {
+ $output->writeln('<error>Please specify an app to update or "--all" to update all updatable apps"</error>');
+ return 1;
+ }
+
+ $return = 0;
+ foreach ($apps as $appId) {
+ $newVersion = $this->installer->isUpdateAvailable($appId, $input->getOption('allow-unstable'));
+ if ($newVersion) {
+ $updateFound = true;
+ $output->writeln($appId . ' new version available: ' . $newVersion);
+
+ if (!$input->getOption('showonly')) {
+ try {
+ $result = $this->installer->updateAppstoreApp($appId, $input->getOption('allow-unstable'));
+ } catch (\Exception $e) {
+ $this->logger->error('Failure during update of app "' . $appId . '"', [
+ 'app' => 'app:update',
+ 'exception' => $e,
+ ]);
+ $output->writeln('Error: ' . $e->getMessage());
+ $result = false;
+ $return = 1;
+ }
+
+ if ($result === false) {
+ $output->writeln($appId . ' couldn\'t be updated');
+ $return = 1;
+ } else {
+ $output->writeln($appId . ' updated');
+ }
+ }
+ }
+ }
+
+ if (!$updateFound) {
+ if ($singleAppId) {
+ $output->writeln($singleAppId . ' is up-to-date or no updates could be found');
+ } else {
+ $output->writeln('All apps are up-to-date or no updates could be found');
+ }
+ }
+
+ return $return;
+ }
+}