From d9469a6b72187560601cc209528be9bc5793df35 Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Fri, 5 Oct 2018 03:08:03 +0200 Subject: [PATCH 1/7] Add occ app:remove CLI command Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 93 +++++++++++++++++++++ core/register_command.php | 1 + lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + 4 files changed, 96 insertions(+) create mode 100644 core/Command/App/Remove.php diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php new file mode 100644 index 0000000000..5b4e29911c --- /dev/null +++ b/core/Command/App/Remove.php @@ -0,0 +1,93 @@ + + * + * @author Patrik Kernstock + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\App; + +use OC\Installer; +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 Remove extends Command implements CompletionAwareInterface { + + protected function configure() { + $this + ->setName('app:remove') + ->setDescription('remove an app') + ->addArgument( + 'app-id', + InputArgument::REQUIRED, + 'remove the specified app' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $appId = $input->getArgument('app-id'); + + if (!\OC_App::getAppPath($appId)) { + $output->writeln($appId . ' is not installed'); + return 1; + } + + try { + /** @var Installer $installer */ + $installer = \OC::$server->query(Installer::class); + $result = $installer->removeApp($appId); + } catch(\Exception $e) { + $output->writeln('Error: ' . $e->getMessage()); + return 1; + } + + if($result === false) { + $output->writeln($appId . ' could not be removed'); + return 1; + } + + $output->writeln($appId . ' removed'); + + return 0; + } + + /** + * @param string $optionName + * @param CompletionContext $context + * @return string[] + */ + public function completeOptionValues($optionName, CompletionContext $context) { + return []; + } + + /** + * @param string $argumentName + * @param CompletionContext $context + * @return string[] + */ + public function completeArgumentValues($argumentName, CompletionContext $context) { + if ($argumentName === 'app-id') { + return \OC_App::getAllApps(); + } + return []; + } +} diff --git a/core/register_command.php b/core/register_command.php index af8d9977c7..0e53cd20df 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -65,6 +65,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\App\Install()); $application->add(new OC\Core\Command\App\GetPath()); $application->add(new OC\Core\Command\App\ListApps(\OC::$server->getAppManager())); + $application->add(new OC\Core\Command\App\Remove()); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Cleanup::class)); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Enforce::class)); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 4e8a523fad..3bfea178c5 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -519,6 +519,7 @@ return array( 'OC\\Core\\Command\\App\\GetPath' => $baseDir . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\Install' => $baseDir . '/core/Command/App/Install.php', 'OC\\Core\\Command\\App\\ListApps' => $baseDir . '/core/Command/App/ListApps.php', + 'OC\\Core\\Command\\App\\Remove' => $baseDir . '/core/Command/App/Remove.php', 'OC\\Core\\Command\\Background\\Ajax' => $baseDir . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 34c530aa1e..24bb3243d4 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -549,6 +549,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\App\\GetPath' => __DIR__ . '/../../..' . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\Install' => __DIR__ . '/../../..' . '/core/Command/App/Install.php', 'OC\\Core\\Command\\App\\ListApps' => __DIR__ . '/../../..' . '/core/Command/App/ListApps.php', + 'OC\\Core\\Command\\App\\Remove' => __DIR__ . '/../../..' . '/core/Command/App/Remove.php', 'OC\\Core\\Command\\Background\\Ajax' => __DIR__ . '/../../..' . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php', From c73363c3abbb24da4b0c0c4025b27880dad0017a Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Fri, 5 Oct 2018 03:20:45 +0200 Subject: [PATCH 2/7] Fixed indents Signed-off-by: Patrik Kernstock --- lib/composer/composer/autoload_classmap.php | 2 +- lib/composer/composer/autoload_static.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 3bfea178c5..1e2940bfb3 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -519,7 +519,7 @@ return array( 'OC\\Core\\Command\\App\\GetPath' => $baseDir . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\Install' => $baseDir . '/core/Command/App/Install.php', 'OC\\Core\\Command\\App\\ListApps' => $baseDir . '/core/Command/App/ListApps.php', - 'OC\\Core\\Command\\App\\Remove' => $baseDir . '/core/Command/App/Remove.php', + 'OC\\Core\\Command\\App\\Remove' => $baseDir . '/core/Command/App/Remove.php', 'OC\\Core\\Command\\Background\\Ajax' => $baseDir . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 24bb3243d4..6065cf622e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -549,7 +549,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\App\\GetPath' => __DIR__ . '/../../..' . '/core/Command/App/GetPath.php', 'OC\\Core\\Command\\App\\Install' => __DIR__ . '/../../..' . '/core/Command/App/Install.php', 'OC\\Core\\Command\\App\\ListApps' => __DIR__ . '/../../..' . '/core/Command/App/ListApps.php', - 'OC\\Core\\Command\\App\\Remove' => __DIR__ . '/../../..' . '/core/Command/App/Remove.php', + 'OC\\Core\\Command\\App\\Remove' => __DIR__ . '/../../..' . '/core/Command/App/Remove.php', 'OC\\Core\\Command\\Background\\Ajax' => __DIR__ . '/../../..' . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php', From f8771d3d810b521f83f4c955d62b4c2f4b10cdfc Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Fri, 5 Oct 2018 12:49:34 +0200 Subject: [PATCH 3/7] Run uninstall tasks by default, added '--keep-data' parameter Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php index 5b4e29911c..75d3cac6b0 100644 --- a/core/Command/App/Remove.php +++ b/core/Command/App/Remove.php @@ -27,6 +27,7 @@ use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareI use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -40,6 +41,12 @@ class Remove extends Command implements CompletionAwareInterface { 'app-id', InputArgument::REQUIRED, 'remove the specified app' + ) + ->addOption( + 'keep-data', + null, + InputOption::VALUE_NONE, + 'keep app data and do not remove them' ); } @@ -51,6 +58,18 @@ class Remove extends Command implements CompletionAwareInterface { return 1; } + if (!$input->getOption('keep-data')) { + try { + /** @var IAppManager $appManager*/ + $appManager = \OC::$server->getAppManager(); + $appManager->disableApp($appId); + $output->writeln($appId . ' disabled'); + } catch(\Exception $e) { + $output->writeln('Error: ' . $e->getMessage()); + return 1; + } + } + try { /** @var Installer $installer */ $installer = \OC::$server->query(Installer::class); From f27ce6b5a0efaf2f7a5f4942cccd6186f439ed42 Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Fri, 5 Oct 2018 19:51:50 +0200 Subject: [PATCH 4/7] Inject AppManager and Installer, check for shipped app prior removing Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 35 +++++++++++++++++++++++++++++------ core/register_command.php | 2 +- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php index 75d3cac6b0..d14a3c6e09 100644 --- a/core/Command/App/Remove.php +++ b/core/Command/App/Remove.php @@ -23,6 +23,7 @@ namespace OC\Core\Command\App; use OC\Installer; +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; @@ -33,6 +34,21 @@ use Symfony\Component\Console\Output\OutputInterface; class Remove extends Command implements CompletionAwareInterface { + /** @var IAppManager */ + protected $manager; + /** @var Installer */ + private $installer; + + /** + * @param IAppManager $manager + * @param Installer $installer + */ + public function __construct(IAppManager $manager, Installer $installer) { + parent::__construct(); + $this->manager = $manager; + $this->installer = $installer; + } + protected function configure() { $this ->setName('app:remove') @@ -53,16 +69,24 @@ class Remove extends Command implements CompletionAwareInterface { protected function execute(InputInterface $input, OutputInterface $output) { $appId = $input->getArgument('app-id'); + // Check if the app is installed if (!\OC_App::getAppPath($appId)) { $output->writeln($appId . ' is not installed'); 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 { - /** @var IAppManager $appManager*/ - $appManager = \OC::$server->getAppManager(); - $appManager->disableApp($appId); + $this->manager->disableApp($appId); $output->writeln($appId . ' disabled'); } catch(\Exception $e) { $output->writeln('Error: ' . $e->getMessage()); @@ -70,10 +94,9 @@ class Remove extends Command implements CompletionAwareInterface { } } + // Let's try to remove the app... try { - /** @var Installer $installer */ - $installer = \OC::$server->query(Installer::class); - $result = $installer->removeApp($appId); + $result = $this->installer->removeApp($appId); } catch(\Exception $e) { $output->writeln('Error: ' . $e->getMessage()); return 1; diff --git a/core/register_command.php b/core/register_command.php index 0e53cd20df..02cc450c24 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -65,7 +65,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\App\Install()); $application->add(new OC\Core\Command\App\GetPath()); $application->add(new OC\Core\Command\App\ListApps(\OC::$server->getAppManager())); - $application->add(new OC\Core\Command\App\Remove()); + $application->add(new OC\Core\Command\App\Remove(\OC::$server->getAppManager(), \OC::$server->query(\OC\Installer::class))); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Cleanup::class)); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Enforce::class)); From 1973556346c4c0550aec1b7b38727ea89416bbeb Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Fri, 5 Oct 2018 21:09:13 +0200 Subject: [PATCH 5/7] Log exception using ILogger Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 15 ++++++++++++++- core/register_command.php | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php index d14a3c6e09..dc8ea7c220 100644 --- a/core/Command/App/Remove.php +++ b/core/Command/App/Remove.php @@ -24,6 +24,7 @@ namespace OC\Core\Command\App; use OC\Installer; use OCP\App\IAppManager; +use OCP\ILogger; use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface; use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Command\Command; @@ -38,15 +39,19 @@ class Remove extends Command implements CompletionAwareInterface { protected $manager; /** @var Installer */ private $installer; + /** @var ILogger */ + private $logger; /** * @param IAppManager $manager * @param Installer $installer + * @param ILogger $logger */ - public function __construct(IAppManager $manager, Installer $installer) { + public function __construct(IAppManager $manager, Installer $installer, ILogger $logger) { parent::__construct(); $this->manager = $manager; $this->installer = $installer; + $this->logger = $logger; } protected function configure() { @@ -90,6 +95,10 @@ class Remove extends Command implements CompletionAwareInterface { $output->writeln($appId . ' disabled'); } catch(\Exception $e) { $output->writeln('Error: ' . $e->getMessage()); + $this->logger->logException($e, [ + 'app' => 'CLI', + 'level' => ILogger::ERROR + ]); return 1; } } @@ -99,6 +108,10 @@ class Remove extends Command implements CompletionAwareInterface { $result = $this->installer->removeApp($appId); } catch(\Exception $e) { $output->writeln('Error: ' . $e->getMessage()); + $this->logger->logException($e, [ + 'app' => 'CLI', + 'level' => ILogger::ERROR + ]); return 1; } diff --git a/core/register_command.php b/core/register_command.php index 02cc450c24..9c3591c8af 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -65,7 +65,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\App\Install()); $application->add(new OC\Core\Command\App\GetPath()); $application->add(new OC\Core\Command\App\ListApps(\OC::$server->getAppManager())); - $application->add(new OC\Core\Command\App\Remove(\OC::$server->getAppManager(), \OC::$server->query(\OC\Installer::class))); + $application->add(new OC\Core\Command\App\Remove(\OC::$server->getAppManager(), \OC::$server->query(\OC\Installer::class), \OC::$server->getLogger())); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Cleanup::class)); $application->add(\OC::$server->query(\OC\Core\Command\TwoFactorAuth\Enforce::class)); From 4cd7cf86db3c2bdabe02a7820f23ea6b41874886 Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Wed, 10 Oct 2018 17:32:50 +0200 Subject: [PATCH 6/7] Using Throwable instead of Exception Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php index dc8ea7c220..3264a2223d 100644 --- a/core/Command/App/Remove.php +++ b/core/Command/App/Remove.php @@ -22,6 +22,7 @@ namespace OC\Core\Command\App; +use Throwable; use OC\Installer; use OCP\App\IAppManager; use OCP\ILogger; @@ -93,7 +94,7 @@ class Remove extends Command implements CompletionAwareInterface { try { $this->manager->disableApp($appId); $output->writeln($appId . ' disabled'); - } catch(\Exception $e) { + } catch(Throwable $e) { $output->writeln('Error: ' . $e->getMessage()); $this->logger->logException($e, [ 'app' => 'CLI', @@ -106,7 +107,7 @@ class Remove extends Command implements CompletionAwareInterface { // Let's try to remove the app... try { $result = $this->installer->removeApp($appId); - } catch(\Exception $e) { + } catch(Throwable $e) { $output->writeln('Error: ' . $e->getMessage()); $this->logger->logException($e, [ 'app' => 'CLI', From 6a00521128594058720f6ad35acd2b01e1910820 Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Wed, 10 Oct 2018 18:28:26 +0200 Subject: [PATCH 7/7] Use error styling for exception message Signed-off-by: Patrik Kernstock --- core/Command/App/Remove.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Command/App/Remove.php b/core/Command/App/Remove.php index 3264a2223d..71d5cef229 100644 --- a/core/Command/App/Remove.php +++ b/core/Command/App/Remove.php @@ -95,7 +95,7 @@ class Remove extends Command implements CompletionAwareInterface { $this->manager->disableApp($appId); $output->writeln($appId . ' disabled'); } catch(Throwable $e) { - $output->writeln('Error: ' . $e->getMessage()); + $output->writeln('Error: ' . $e->getMessage() . ''); $this->logger->logException($e, [ 'app' => 'CLI', 'level' => ILogger::ERROR @@ -108,7 +108,7 @@ class Remove extends Command implements CompletionAwareInterface { try { $result = $this->installer->removeApp($appId); } catch(Throwable $e) { - $output->writeln('Error: ' . $e->getMessage()); + $output->writeln('Error: ' . $e->getMessage() . ''); $this->logger->logException($e, [ 'app' => 'CLI', 'level' => ILogger::ERROR