Add Upgrade Command

- Add Upgrade Package handler

-- Add base package handler service with re-usable api handle packages
-- Re-usable service to extract and apply upgrade packages
-- Add upgrade settings to configs

- Add Base command

-- Add abstract class with common input processing logic

- Add Upgrade Command

-- Add re-usable UpgradeHandler with api for applying upgrades
-- Add command to call upgrade handler
-- Add service feedback
-- Call clear cache on upgrade command

- Add Upgrade step by step execution

-- Add process step interface and executor
--- add common logging to each step execution
--- add debug logging for each step execution
--- allow running processes step by step
--- allow configuring steps independently

-- Move upgrade handler and upgrade command to use step executor
-- Add upgrade log
-- Improve upgrade command output
This commit is contained in:
Clemente Raposo 2021-05-31 12:52:33 +01:00
parent dc27646e31
commit d22cfbbf8f
22 changed files with 1610 additions and 4 deletions

View file

@ -24,6 +24,7 @@
"prefer-stable": true,
"require": {
"ext-json": "*",
"ext-zip": "*",
"api-platform/api-pack": "^1.2",
"elasticsearch/elasticsearch": "^5.3",
"ezyang/htmlpurifier": "^4.10",

View file

@ -6,6 +6,7 @@ services:
public: false # Allows optimizing the container by removing unused services.
bind:
$projectDir: '%kernel.project_dir%'
$upgradePackageDir: '%packages.upgrade.dir%'
$legacyDir: '%legacy.dir%'
$legacyPath: '%legacy.path%'
$legacySessionName: '%legacy.session_name%'
@ -38,6 +39,7 @@ services:
$legacyToFrontEndFieldsMap: '%record.fields.legacy_to_frontend_fields_map%'
$uiConfigs: '%ui%'
$extensions: '%extensions%'
$upgradeConfig: '%upgrades%'
_instanceof:
App\Process\Service\ProcessHandlerInterface:
tags: [ 'app.process.handler' ]
@ -59,6 +61,11 @@ services:
tags: [ 'app.data.statistics.handler' ]
App\ViewDefinitions\LegacyHandler\SubpanelButtonMapperInterface:
tags: [ 'subpanel.button.definition.mapper' ]
# Always instantiate new instance for implementing classes
App\Engine\Model\ProcessStepInterface:
shared: false
App\Install\Service\Upgrade\UpgradeStepInterface:
tags: [ 'app.upgrade.step' ]
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
@ -187,6 +194,12 @@ services:
arguments:
- !tagged { tag: 'subpanel.button.definition.mapper' }
App\Install\Service\Upgrade\UpgradeHandler:
# inject all services tagged with app.upgrade.step as first argument
arguments:
- !tagged { tag: 'app.upgrade.step' }
- '@monolog.logger.upgrade'
App\Process\Service\ActionNameMapperInterface: '@App\Engine\LegacyHandler\ActionNameMapperHandler'
App\Process\Service\BulkActionDefinitionProviderInterface: '@App\Process\Service\BulkActionDefinitionProvider'
App\FieldDefinitions\Service\FieldDefinitionsProviderInterface: '@App\FieldDefinitions\LegacyHandler\FieldDefinitionsHandler'

View file

@ -4,7 +4,7 @@ monolog:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
channels: ["!event", "!upgrade"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
@ -16,4 +16,4 @@ monolog:
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
channels: ["!event", "!doctrine", "!console", "!upgrade"]

View file

@ -0,0 +1,8 @@
monolog:
channels: ['upgrade']
handlers:
upgrade:
type: stream
path: "%kernel.project_dir%/logs/upgrade.log"
level: debug
channels: ["upgrade"]

View file

@ -7,6 +7,7 @@ monolog:
excluded_http_codes: [404, 405]
# Prevents memory leaks
buffer_size: 50
channels: ["!upgrade"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
@ -14,7 +15,7 @@ monolog:
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
channels: ["!event", "!doctrine", "!upgrade"]
deprecation:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"

View file

@ -5,7 +5,7 @@ monolog:
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
channels: ["!event", "!upgrade"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"

View file

@ -4,6 +4,7 @@ framework:
parameters:
secret: '%env(APP_SECRET)%'
legacy.dir: '%kernel.project_dir%/public/legacy'
packages.upgrade.dir: '%kernel.project_dir%/tmp/package/upgrade'
legacy.path: '/legacy'
legacy.session_name: 'LEGACYSESSID'
default_session_name: 'PHPSESSID'

View file

@ -0,0 +1,27 @@
parameters:
upgrades:
toKeep:
- 'cache'
- 'extensions'
- 'public/extensions'
- 'public/legacy/modules'
- 'public/legacy/custom'
- 'public/legacy/cache'
- 'public/legacy/upload'
- 'public/legacy/Api/V8/OAuth2/private.key'
- 'public/legacy/Api/V8/OAuth2/public.key'
- 'public/legacy/config.php'
- 'public/legacy/config_override.php'
- 'public/legacy/config_si.php'
- 'public/legacy/suitecrm.log'
- 'public/legacy/install.log'
- 'logs'
- '.env.local'
- '.env.local.php'
toExpand:
- 'public'
- 'public/legacy'
- 'public/legacy/modules'
- 'public/legacy/Api'
- 'public/legacy/Api/V8'
- 'public/legacy/Api/V8/OAuth2'

View file

@ -0,0 +1,103 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Model;
class Feedback
{
/**
* @var bool
*/
public $success;
/**
* @var string[]
*/
public $messages = [];
/**
* @var string[]
*/
public $debug = [];
/**
* @return bool
*/
public function isSuccess(): bool
{
return $this->success;
}
/**
* @param bool $success
* @return Feedback
*/
public function setSuccess(bool $success): Feedback
{
$this->success = $success;
return $this;
}
/**
* @return string[]
*/
public function getMessages(): array
{
return $this->messages ?? [];
}
/**
* @param string[] $messages
* @return Feedback
*/
public function setMessages(array $messages): Feedback
{
$this->messages = $messages;
return $this;
}
/**
* @return string[]
*/
public function getDebug(): array
{
return $this->debug;
}
/**
* @param string[] $debug
* @return Feedback
*/
public function setDebug(array $debug): Feedback
{
$this->debug = $debug;
return $this;
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Model;
class MultiFeedback
{
/**
* @var bool
*/
public $success;
/**
* @var Feedback[]
*/
public $feedback = [];
/**
* @return bool
*/
public function isSuccess(): bool
{
return $this->success;
}
/**
* @param bool $success
* @return MultiFeedback
*/
public function setSuccess(bool $success): MultiFeedback
{
$this->success = $success;
return $this;
}
/**
* @return Feedback[]
*/
public function getFeedback(): array
{
return $this->feedback;
}
/**
* @param Feedback[] $feedback
* @return MultiFeedback
*/
public function setFeedback(array $feedback): MultiFeedback
{
$this->feedback = $feedback;
return $this;
}
}

View file

@ -0,0 +1,69 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Model;
use Psr\Log\LoggerInterface;
interface ProcessStepInterface
{
/**
* Get step key
* @return string
*/
public function getKey(): string;
/**
* Get execution order
* @return int
*/
public function getOrder(): int;
/**
* Execute step
* @param array $context
* @return Feedback
*/
public function execute(array &$context): Feedback;
/**
* Run step
* @param array $context
* @return Feedback
*/
public function run(array &$context): Feedback;
/**
* @return LoggerInterface|null
*/
public function getLogger(): ?LoggerInterface;
/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger): void;
}

View file

@ -0,0 +1,160 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Model;
use Psr\Log\LoggerInterface;
trait ProcessStepTrait
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* Run step
* @param array $context
* @return Feedback
*/
public function run(array &$context): Feedback
{
$this->logStart();
$feedback = $this->execute($context);
$this->logStepFeedback($feedback);
return $feedback;
}
/**
* Log step start
*/
protected function logStart(): void
{
if ($this->getLogger() === null) {
return;
}
$this->logger->info('Running step: ' . $this->getKey());
}
/**
* @return LoggerInterface|null
*/
public function getLogger(): ?LoggerInterface
{
return $this->logger;
}
/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
/**
* Get step key
* @return string
*/
abstract public function getKey(): string;
/**
* Execute step
* @param array $context
* @return Feedback
*/
abstract protected function execute(array &$context): Feedback;
/**
* Log step feedback
* @param Feedback $feedback
*/
protected function logStepFeedback(Feedback $feedback): void
{
if ($this->getLogger() === null) {
return;
}
if ($feedback->isSuccess() === false) {
$this->logger->error('step: ' . $this->getKey() . ' | status: failed');
} else {
$this->logger->info('step: ' . $this->getKey() . ' | status: done');
}
$this->logFeedbackMessages($feedback);
$this->logFeedbackDebug($feedback);
}
/**
* Log feedback messages
* @param Feedback $feedback
*/
protected function logFeedbackMessages(Feedback $feedback): void
{
$messages = $feedback->getMessages() ?? [];
if (empty($messages)) {
$this->logger->info('step: ' . $this->getKey() . ' | messages: no messages');
return;
}
$this->logger->info('step: ' . $this->getKey() . ' | messages:');
foreach ($messages as $message) {
$this->logger->info($message);
}
}
/**
* Log feedback debug
* @param Feedback $feedback
*/
protected function logFeedbackDebug(Feedback $feedback): void
{
$debugInfo = $feedback->getDebug() ?? [];
if (empty($debugInfo)) {
$this->logger->info('step: ' . $this->getKey() . ' | debug: no debug info');
return;
}
$this->logger->info('step: ' . $this->getKey() . ' | debug:');
foreach ($debugInfo as $debug) {
$this->logger->info($debug);
}
}
}

View file

@ -0,0 +1,207 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Service\ProcessSteps;
use App\Engine\Model\Feedback;
use App\Engine\Model\MultiFeedback;
use App\Engine\Model\ProcessStepInterface;
use BadMethodCallException;
use Psr\Log\LoggerInterface;
abstract class ProcessStepExecutor implements ProcessStepExecutorInterface
{
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var ProcessStepInterface[]
*/
protected $steps;
/**
* @var ProcessStepInterface[][]
*/
protected $orderedSteps;
/**
* Run all steps in order
* @param array $context
* @return MultiFeedback feedback
*/
public function runAll(array $context): MultiFeedback
{
$result = new MultiFeedback();
$result->setSuccess(true);
$context = $context ?? [];
$context['stepsFeedback'] = [];
foreach ($this->orderedSteps as $positionSteps) {
foreach ($positionSteps as $step) {
$feedback = $step->run($context);
$context['stepsFeedback'][$step->getKey()] = $feedback;
if ($feedback->isSuccess() === false) {
$result->setSuccess(false);
$result->setFeedback($context['stepsFeedback']);
return $result;
}
}
}
$result->setFeedback($context['stepsFeedback']);
return $result;
}
/**
* @param string $stepKey
* @param array $context
* @return Feedback feedback
*/
public function run(string $stepKey, array $context): Feedback
{
if (empty($this->steps[$stepKey])) {
throw new BadMethodCallException("$stepKey not found");
}
$context = $context ?? [];
$context['stepsFeedback'] = [];
return $this->steps[$stepKey]->run($context);
}
/**
* Has position
* @param int $position
* @return bool
*/
public function hasPosition(int $position): bool
{
$positionSteps = $this->orderedSteps[$position] ?? [];
if (empty($positionSteps)) {
return false;
}
return true;
}
/**
* Run next
* @param int $position
* @param array $context
* @return MultiFeedback
*/
public function runPosition(int $position, array $context): MultiFeedback
{
$positionSteps = $this->orderedSteps[$position] ?? [];
if (empty($positionSteps)) {
$result = new MultiFeedback();
$result->setSuccess(false);
return $result;
}
$result = new MultiFeedback();
$result->setSuccess(true);
$context = $context ?? [];
$context['stepsFeedback'] = [];
foreach ($positionSteps as $step) {
$feedback = $step->run($context);
$context['stepsFeedback'][$step->getKey()] = $feedback;
if ($feedback->isSuccess() === false) {
$result->setSuccess(false);
break;
}
}
$result->setFeedback($context['stepsFeedback']);
return $result;
}
/**
* Get position keys
* @param int $position
* @return string[]
*/
public function getPositionKeys(int $position): array
{
$positionSteps = $this->orderedSteps[$position] ?? [];
if (empty($positionSteps)) {
return [];
}
return array_keys($positionSteps);
}
/**
* @param iterable $handlers
* @param LoggerInterface $logger
*/
protected function initSteps(iterable $handlers, LoggerInterface $logger): void
{
/**
* @var $ordered ProcessStepInterface[][]
*/
$ordered = [];
/**
* @var $handlers ProcessStepInterface[]
*/
foreach ($handlers as $step) {
$key = $step->getKey() ?? '';
$order = $step->getOrder() ?? count($this->orderedSteps);
$step->setLogger($logger);
$positionSteps = $ordered[$order] ?? [];
$positionSteps[$key] = $step;
$ordered[$order] = $positionSteps;
$this->steps[$key] = $step;
}
ksort($ordered, SORT_NUMERIC);
$this->orderedSteps = [];
foreach ($ordered as $item) {
$this->orderedSteps[] = $item;
}
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Engine\Service\ProcessSteps;
use App\Engine\Model\Feedback;
use App\Engine\Model\MultiFeedback;
interface ProcessStepExecutorInterface
{
/**
* Run all steps in order
* @param array $context
* @return MultiFeedback feedback
*/
public function runAll(array $context): MultiFeedback;
/**
* @param string $stepKey
* @param array $context
* @return Feedback feedback
*/
public function run(string $stepKey, array $context): Feedback;
/**
* Has position
* @param int $position
* @return bool
*/
public function hasPosition(int $position): bool;
/**
* Run position
* @param int $position
* @param array $context
* @return MultiFeedback
*/
public function runPosition(int $position, array $context): MultiFeedback;
/**
* Get position keys
* @param int $position
* @return string[]
*/
public function getPositionKeys(int $position): array;
}

View file

@ -0,0 +1,200 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Command;
use App\Engine\Model\Feedback;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
abstract class BaseCommand extends Command
{
/**
* @var array
*/
protected $inputConfig = [];
/**
* @var bool
*/
protected $initSession = false;
/**
* @var string
*/
protected $defaultSessionName;
/**
* @var SessionInterface
*/
protected $session;
/**
* @var string
*/
protected $legacySessionName;
/**
* @required
* @param string $defaultSessionName
*/
public function setDefaultSessionName(string $defaultSessionName): void
{
$this->defaultSessionName = $defaultSessionName;
}
/**
* @required
* @param string $legacySessionName
*/
public function setLegacySessionName(string $legacySessionName): void
{
$this->legacySessionName = $legacySessionName;
}
/**
* @required
* @param SessionInterface $session
*/
public function setSession(SessionInterface $session): void
{
$this->session = $session;
}
/**
* @inheritDoc
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$arguments = [];
$helper = $this->getHelper('question');
foreach ($this->inputConfig as $key => $option) {
$value = $input->getOption($key);
if (empty($value)) {
$value = $helper->ask($input, $output, $option['question']);
}
$arguments[$key] = $value;
}
if ($this->initSession) {
$this->startSession();
}
return $this->executeCommand($input, $output, $arguments);
}
/**
* Execute command actions
* @param InputInterface $input
* @param OutputInterface $output
* @param array $inputs
* @return int
*/
abstract protected function executeCommand(InputInterface $input, OutputInterface $output, array $inputs): int;
/**
* @inheritDoc
*/
protected function configure(): void
{
$inputs = [];
foreach ($this->inputConfig as $key => $item) {
$inputs[$key] = $item['argument'];
}
$this->setDefinition(
$inputs
);
}
/**
* Start Session
*/
protected function startSession(): void
{
if ($this->session->isStarted()) {
return;
}
$this->session->setName($this->defaultSessionName);
$this->session->start();
}
/**
* Write feedback for a given step
* @param string $step
* @param OutputInterface $output
* @param Feedback $feedback
*/
protected function writeStepFeedback(string $step, OutputInterface $output, Feedback $feedback): void
{
$status = '<info>done</info>';
if ($feedback->isSuccess() === false) {
$status = '<error>failed</error>';
}
$output->writeln('step: ' . $step . ' | status: ' . $status);
$this->writeFeedbackMessages($output, $feedback);
}
/**
* Write feedback messages
* @param OutputInterface $output
* @param Feedback $feedback
*/
protected function writeFeedbackMessages(OutputInterface $output, Feedback $feedback): void
{
$messages = $feedback->getMessages() ?? [];
foreach ($messages as $message) {
$output->writeln($this->colorMessage($feedback, $message));
}
}
/**
* @param Feedback $feedback
* @param string $message
* @return string
*/
protected function colorMessage(Feedback $feedback, string $message): string
{
$colorTag = 'info';
if ($feedback->isSuccess() === false) {
$colorTag = 'error';
}
return implode('', ["<$colorTag>", $message, "</$colorTag>"]);
}
}

View file

@ -0,0 +1,133 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Command;
use App\Install\Service\Upgrade\UpgradeHandlerInterface;
use Exception;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Class UpgradeCommand
* @package App\Install\Command
*/
class UpgradeCommand extends BaseCommand
{
/**
* @var string
*/
protected static $defaultName = 'suitecrm:app:upgrade';
/**
* @var UpgradeHandlerInterface
*/
protected $handler;
/**
* UpgradeCommand constructor.
* @param UpgradeHandlerInterface $handler
*/
public function __construct(UpgradeHandlerInterface $handler)
{
$this->handler = $handler;
$this->inputConfig['target-version'] = [
'question' => new Question('Please enter the version to move to: '),
'argument' => new InputOption(
'target-version',
't',
InputOption::VALUE_REQUIRED,
'Target Version'
)
];
$this->initSession = true;
parent::__construct();
}
/**
* @inheritDoc
* @throws Exception
*/
public function executeCommand(InputInterface $input, OutputInterface $output, array $arguments): int
{
$output->writeln([
'',
'SuiteCRM Upgrade',
'============',
'',
]);
$version = $arguments['target-version'];
$context = [
'version' => $version
];
$position = 0;
$success = true;
do {
$keys = $this->handler->getPositionKeys($position);
$output->writeln('Running: ' . implode(' | ', $keys));
$result = $this->handler->runPosition($position, $context);
foreach ($result->getFeedback() as $key => $feedback) {
$this->writeStepFeedback($key, $output, $feedback);
}
$success = $success && $result->isSuccess();
$position++;
} while ($success === true && $this->handler->hasPosition($position));
$output->writeln([
'',
'============',
]);
if ($success === false) {
return 1;
}
return 0;
}
protected function configure(): void
{
parent::configure();
$this->setDescription('Upgrade the application')
->setHelp('This command will upgrade suite 8 and legacy application');
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Cache;
use App\Engine\Model\Feedback;
use Exception;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Class CacheClear
* @package App\Install\Service\Cache
*/
class CacheClear
{
/**
* @var KernelInterface
*/
protected $kernel;
/**
* CacheClear constructor.
* @param KernelInterface $kernel
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
/**
* @return Feedback
* @throws Exception
*/
public function run(): Feedback
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'cache:clear',
]);
$output = new NullOutput();
$result = $application->run($input, $output);
$feedback = new Feedback();
if ($result === 0) {
$feedback->setSuccess(true)->setMessages(['Successfully cleared cache']);
return $feedback;
}
$feedback->setSuccess(false)->setMessages(['Error clearing cache']);
return $feedback;
}
}

View file

@ -0,0 +1,78 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Package;
use Symfony\Component\Filesystem\Filesystem;
use ZipArchive;
class PackageHandler
{
/**
* @var Filesystem
*/
protected $filesystem;
public function __construct()
{
$this->filesystem = new Filesystem();
}
/**
* @param $path
* @return bool
*/
public function exists(string $path): bool
{
if (!$this->filesystem->exists($path)) {
return false;
}
return true;
}
/**
* Extract package to
* @param string $packagePath
* @param string $outputPath
* @return bool
*/
public function extract(string $packagePath, string $outputPath): bool
{
$zip = new ZipArchive();
if ($zip->open($packagePath) !== true) {
return false;
}
$result = $zip->extractTo($outputPath);
$result = $zip->close() && $result;
return $result;
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Upgrade;
use App\Engine\Service\ProcessSteps\ProcessStepExecutor;
use Psr\Log\LoggerInterface;
class UpgradeHandler extends ProcessStepExecutor implements UpgradeHandlerInterface
{
/**
* UpgradeHandler constructor.
* @param iterable $handlers
* @param LoggerInterface $upgradeLogger
*/
public function __construct(
iterable $handlers,
LoggerInterface $upgradeLogger
) {
$this->logger = $upgradeLogger;
$this->initSteps($handlers, $this->logger);
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Upgrade;
use App\Engine\Service\ProcessSteps\ProcessStepExecutorInterface;
/**
* Interface UpgradeStepInterface
* Used to allow extensibility
* @package App\Install\Service\Upgrade
*/
interface UpgradeHandlerInterface extends ProcessStepExecutorInterface
{
}

View file

@ -0,0 +1,243 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Upgrade;
use App\Engine\Model\Feedback;
use App\Engine\Service\FolderSync\FolderComparatorInterface;
use App\Engine\Service\FolderSync\FolderSync;
use App\Install\Service\Package\PackageHandler;
use Psr\Log\LoggerInterface;
class UpgradePackageHandler extends PackageHandler
{
/**
* @var string
*/
protected $projectDir;
/**
* @var string
*/
protected $upgradePackageDir;
/**
* @var FolderSync
*/
protected $sync;
/**
* @var FolderComparatorInterface
*/
protected $compare;
/**
* @var LoggerInterface
*/
protected $upgradeLogger;
/**
* UpgradeHandler constructor.
* @param string $projectDir
* @param string $upgradePackageDir
* @param FolderSync $sync
* @param FolderComparatorInterface $compare
* @param LoggerInterface $upgradeLogger
* @param array $upgradeConfig
*/
public function __construct(
string $projectDir,
string $upgradePackageDir,
FolderSync $sync,
FolderComparatorInterface $compare,
LoggerInterface $upgradeLogger,
array $upgradeConfig
) {
parent::__construct();
$this->projectDir = $projectDir;
$this->upgradePackageDir = $upgradePackageDir;
$this->sync = $sync;
$this->compare = $compare;
$this->compare->setToKeep($upgradeConfig['toKeep']);
$this->compare->setPathsToExpand($upgradeConfig['toExpand']);
$this->upgradeLogger = $upgradeLogger;
}
/**
* Check if package exists
* @param string $version
* @return Feedback
*/
public function checkPackage(string $version): Feedback
{
$packagePath = $this->getPackagePath($version);
$feedback = new Feedback();
$feedback->setSuccess(true)->setMessages(['Package found in path']);
$feedback->setDebug(['Check package existence in: ' . $packagePath]);
if (!$this->exists($packagePath)) {
$feedback->setSuccess(false)->setMessages(['Package not found in path: ' . $packagePath]);
}
return $feedback;
}
/**
* Extract package
* @param string $version
* @return Feedback
*/
public function extractPackage(string $version): Feedback
{
$packagePath = $this->getPackagePath($version);
$extractPath = $this->getPackageExtractPath($version);
$feedback = new Feedback();
$feedback->setSuccess(true)->setMessages(['Package extracted']);
$feedback->setDebug(['Trying to extract to: ' . $extractPath]);
$extracted = $this->extract($packagePath, $extractPath);
if ($extracted === false) {
$feedback = new Feedback();
$feedback->setSuccess(false)->setMessages(['Error while trying to extract package to: ' . $extractPath]);
}
return $feedback;
}
/**
* Run compare and check permissions
* @param string $version
* @return Feedback
*/
public function checkPermissions(string $version): Feedback
{
$extractPath = $this->getPackageExtractPath($version);
$manifest = $this->runCompare($extractPath);
$messages = [];
$success = true;
foreach ($manifest as $path => $entry) {
$originAllowed = true;
if ($entry->action !== 'delete') {
$originResult = $this->sync->checkOriginPermissions($extractPath, $path);
$originAllowed = $originResult['allowed'] ?? false;
$originPath = $originResult['path'] ?? '';
if ($originAllowed === false) {
$messages[] = 'Insufficient permissions to read from: ' . $originPath;
}
}
$destinationResult = $this->sync->checkDestinationPermissions($this->projectDir, $path);
$destinationAllowed = $destinationResult['allowed'] ?? false;
$destinationPath = $destinationResult['path'] ?? '';
if ($destinationAllowed === false) {
$messages[] = 'Insufficient permissions to read from: ' . $destinationPath;
}
$success = $success && $originAllowed && $destinationAllowed;
}
$feedback = new Feedback();
$feedback->setSuccess($success);
if ($success) {
$messages = ['Permissions checked'];
}
$feedback->setMessages($messages);
return $feedback;
}
/**
* Run compare and install package
* @param string $version
* @return Feedback
*/
public function install(string $version): Feedback
{
$extractPath = $this->getPackageExtractPath($version);
$manifest = $this->runCompare($extractPath);
$feedback = new Feedback();
if (empty($manifest)) {
$feedback->setSuccess(true)->setMessages(['Sync diff did not return results']);
return $feedback;
}
$feedback->setDebug([
'sync manifest generated: ' . json_encode($manifest, JSON_THROW_ON_ERROR)
]);
$this->sync->run($extractPath, $this->projectDir, $manifest);
$feedback->setSuccess(true)->setMessages(['Successfully installed package']);
return $feedback;
}
/**
* @param string $extractPath
* @return array
*/
public function runCompare(string $extractPath): array
{
return $this->compare->run($extractPath, $this->projectDir);
}
/**
* Get package path
* @param string $version
* @return string
*/
protected function getPackagePath(string $version): string
{
return $this->upgradePackageDir . '/' . $version . '.zip';
}
/**
* Get package extract output path
* @param string $version
* @return string
*/
protected function getPackageExtractPath(string $version): string
{
return $this->upgradePackageDir . '/' . $version . '-extracted';
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2021 SalesAgility Ltd.
*
* This program 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 with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Upgrade;
use App\Engine\Model\ProcessStepInterface;
/**
* Interface UpgradeStepInterface
* Used to auto-configure upgrade steps
* @package App\Install\Service\Upgrade
*/
interface UpgradeStepInterface extends ProcessStepInterface
{
}