Skip to content
This repository has been archived by the owner on Mar 17, 2020. It is now read-only.

Commit

Permalink
Introduced Process Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
Mickaël Andrieu committed Jan 25, 2019
1 parent 9654358 commit ce368d1
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/Actions/CreateProject.php
Expand Up @@ -20,7 +20,7 @@ public function __construct(array $actionsArguments = [])
$actionsArguments = [
'--no-scripts',
'--no-progress',
'--no-interaction'
'--no-interaction',
];
}
$this->actionsArguments = $actionsArguments;
Expand Down
39 changes: 6 additions & 33 deletions src/ProcessExecutor.php → src/CommandBuilder.php
Expand Up @@ -3,15 +3,14 @@
namespace PrestaShop\Composer;

use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use PrestaShop\Composer\Contracts\ExecutorInterface;
use PrestaShop\Composer\Contracts\CommandBuilderInterface;
use PrestaShop\Composer\Contracts\PackageInterface;
use PrestaShop\Composer\Contracts\ActionInterface;

/**
* Using Symfony Process Component, executes Composer actions
*/
final class ProcessExecutor implements ExecutorInterface
final class CommandBuilder implements CommandBuilderInterface
{
/**
* @var string the Composer executable
Expand All @@ -35,7 +34,7 @@ public function __construct($rootPath)
/**
* {@inheritdoc}
*/
public function execute(ActionInterface $action, $location)
public function getCommand(ActionInterface $action)
{
$command = $this->composer . ' '
. $action->getName()
Expand All @@ -47,13 +46,13 @@ public function execute(ActionInterface $action, $location)
$command .= ' ' . implode(' ', $actionArgs);
}

return $this->executeProcess($command, $location);
return $command;
}

/**
* {@inheritdoc}
*/
public function executeOnPackage(ActionInterface $action, PackageInterface $package)
public function getCommandOnPackage(ActionInterface $action, PackageInterface $package)
{
$command = $this->composer . ' '
. $action->getName() . ' '
Expand All @@ -66,32 +65,6 @@ public function executeOnPackage(ActionInterface $action, PackageInterface $pack
$command .= ' ' . implode(' ', $actionArgs);
}

return $this->executeProcess($command, $package->getDestination());
}

/**
* @param string $command the Process command
* @param string $location the Process location
*
* @throws ProcessFailedException
*
* @return string
*/
private function executeProcess($command, $location)
{
gc_collect_cycles();
$process = (new Process($command))
->setWorkingDirectory($location)
->setTimeout(60)
->disableOutput()
;

try {
$process->mustRun();

return '[OK]';
} catch (ProcessFailedException $exception) {
return $exception->getMessage();
}
return $command;
}
}
19 changes: 12 additions & 7 deletions src/ConfigurationProcessor.php
Expand Up @@ -4,8 +4,9 @@

use Composer\IO\IOInterface;
use Symfony\Component\Filesystem\Filesystem;
use PrestaShop\Composer\ProcessManager\ProcessManager;
use PrestaShop\Composer\Actions\CreateProject;
use PrestaShop\Composer\Contracts\ExecutorInterface;
use PrestaShop\Composer\Contracts\CommandBuilderInterface;

/**
* According to the configuration of "extras > prestashop" in Composer json file,
Expand All @@ -21,9 +22,9 @@ final class ConfigurationProcessor
private $io;

/**
* @var ExecutorInterface the Composer command executor
* @var CommandBuilderInterface the Composer command builder
*/
private $composerExecutor;
private $commandBuilder;

/**
* @var string the modules folder path of the Shop
Expand All @@ -34,13 +35,13 @@ final class ConfigurationProcessor
* ConfigurationProcessor constructor.
*
* @param IOInterface $io the CLI IO interface
* @param ExecutorInterface $composerExecutor the Composer command executor
* @param CommandBuilderInterface $commandBuilder the Composer command executor
* @param string $rootPath the Shop root path
*/
public function __construct(IOInterface $io, ExecutorInterface $composerExecutor, $rootPath)
public function __construct(IOInterface $io, CommandBuilderInterface $commandBuilder, $rootPath)
{
$this->io = $io;
$this->composerExecutor = $composerExecutor;
$this->commandBuilder = $commandBuilder;
$this->modulesLocation = $rootPath . self::MODULES_PATH;
}

Expand All @@ -61,6 +62,7 @@ public function processInstallation(array $configuration)
}

$nativeModules = $configuration['modules'];
$processManager = new ProcessManager(100, 8);

foreach ($nativeModules as $moduleName => $moduleVersion) {
$this->io->write(sprintf('<info>Looked into "%s" module (version %s)</info>', $moduleName, $moduleVersion));
Expand All @@ -73,7 +75,10 @@ public function processInstallation(array $configuration)
$this->io->write(sprintf('Module "%s" is already installed, deleted.', $package->getName()));
}

$this->io->write($this->composerExecutor->executeOnPackage(new CreateProject(), $package));
$command = $this->commandBuilder->getCommandOnPackage(new CreateProject(), $package);
$processManager->add($command, $this->modulesLocation);
}

$processManager->run();
}
}
Expand Up @@ -7,25 +7,25 @@
* Basically, a Composer command is composed of an action ("create-project")
* and can include package information ("owner/package-name:version").
*/
interface ExecutorInterface
interface CommandBuilderInterface
{
/**
* Will execute an action, like "composer show"
* Will retrieve a command from an action, like "composer show"
*
* @param ActionInterface $action the Composer action
* @param string $location the Composer json file location directory
*
* @return string the execution output
*/
public function execute(ActionInterface $action, $location);
public function getCommand(ActionInterface $action);

/**
* Will execute an action on a package, like "composer install owner/package-name:version".
* Will retrieve a command from an action on a package,
* like "composer install owner/package-name:version".
*
* @param ActionInterface $action the Composer action
* @param PackageInterface $package the Composer package
*
* @return string the execution output
*/
public function executeOnPackage(ActionInterface $action, PackageInterface $package);
public function getCommandOnPackage(ActionInterface $action, PackageInterface $package);
}
Empty file.
74 changes: 74 additions & 0 deletions src/ProcessManager/ProcessManager.php
@@ -0,0 +1,74 @@
<?php

namespace PrestaShop\Composer\ProcessManager;

use Symfony\Component\Process\Process;
use PrestaShop\Composer\Contracts\ProcessManagerInterface;

/**
* Main implementation of Process Manager, used to
* parallelize Process calls.
*/
final class ProcessManager implements ProcessManagerInterface
{
/**
* @var int
*/
private $timestamp;

/**
* @var int
*/
private $maxParallelProcesses;

/**
* @var Process[]
*/
private $processes;

/**
* @var int
*/
private $runningProcesses;

public function __construct($timestamp, $maxParallelProcesses)
{
$this->timestamp = $timestamp;
$this->maxParallelProcesses = $maxParallelProcesses;
}

public function add($command, $location)
{
$this->processes[] = (new Process($command))
->setWorkingDirectory($location)
;
}

public function run()
{
$batchOfProcesses = array_chunk($this->processes, $this->maxParallelProcesses);

foreach ($batchOfProcesses as $processes) {
$this->runProcesses($processes);
}
}

private function runProcesses(array $processes)
{
$runningProcesses = count($processes);

foreach ($processes as $process) {
$process->start();
}

while ($runningProcesses > 0) {
foreach ($processes as $process) {
if (!$process->isRunning()) {
--$runningProcesses;
}

usleep($this->timestamp);
}
}
}
}
4 changes: 2 additions & 2 deletions src/ScriptHandler.php
Expand Up @@ -27,8 +27,8 @@ public static function install(Event $event)
if (self::validateConfiguration($extras)) {
$config = $extras['prestashop'];

$processExecutor = new ProcessExecutor($rootPath);
$processor = new ConfigurationProcessor($event->getIO(), $processExecutor, $rootPath);
$commandBuilder = new CommandBuilder($rootPath);
$processor = new ConfigurationProcessor($event->getIO(), $commandBuilder, $rootPath);

$processor->processInstallation($config);
}
Expand Down
6 changes: 5 additions & 1 deletion tests/Actions/CreateProjectTest.php
Expand Up @@ -26,7 +26,11 @@ public function testGetArguments()
{
$actionsWithoutArguments = new CreateProject();

$this->assertSame([], $actionsWithoutArguments->getArguments());
$this->assertSame([
'--no-scripts',
'--no-progress',
'--no-interaction',
], $actionsWithoutArguments->getArguments());

$action = new CreateProject(['foo' => 'bar']);

Expand Down
10 changes: 5 additions & 5 deletions tests/ConfigurationProcessorTest.php
Expand Up @@ -3,7 +3,7 @@
namespace Tests\PrestaShop\Composer;

use PrestaShop\Composer\ConfigurationProcessor;
use PrestaShop\Composer\Contracts\ExecutorInterface;
use PrestaShop\Composer\Contracts\CommandBuilderInterface;
use Composer\IO\IOInterface;
use PHPUnit\Framework\TestCase;

Expand All @@ -15,9 +15,9 @@ final class ConfigurationProcessorTest extends TestCase
private $io;

/**
* @var ExecutorInterface the process executor
* @var CommandBuilderInterface the commandBuilder
*/
private $executor;
private $commandBuilder;

/**
* @var ConfigurationProcessor
Expand All @@ -31,7 +31,7 @@ protected function setUp()
{
parent::setUp();
$this->io = $this->prophesize(IOInterface::class);
$this->executor = $this->prophesize(ExecutorInterface::class);
$this->commandBuilder = $this->prophesize(CommandBuilderInterface::class);
}

/**
Expand All @@ -46,7 +46,7 @@ public function testCreation()
{
$this->processor = new ConfigurationProcessor(
$this->io->reveal(),
$this->executor->reveal(),
$this->commandBuilder->reveal(),
'/tmp'
);

Expand Down

0 comments on commit ce368d1

Please sign in to comment.