1
0
Fork 0

Allow notification from locked installs, fixes #1368, fixes #1372, fixes #1369

pull/1367/merge
Jordi Boggiano 2012-11-29 09:24:28 +01:00
parent e868c9706b
commit a8f74a0983
11 changed files with 154 additions and 111 deletions

View File

@ -16,14 +16,15 @@ use Composer\Config;
use Composer\Factory; use Composer\Factory;
use Composer\Installer; use Composer\Installer;
use Composer\Installer\ProjectInstaller; use Composer\Installer\ProjectInstaller;
use Composer\Installer\InstallationManager;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Package\BasePackage; use Composer\Package\BasePackage;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\Repository\ComposerRepository; use Composer\Repository\ComposerRepository;
use Composer\Repository\CompositeRepository; use Composer\Repository\CompositeRepository;
use Composer\Repository\FilesystemRepository; use Composer\Repository\FilesystemRepository;
use Composer\Repository\NotifiableRepositoryInterface;
use Composer\Repository\InstalledFilesystemRepository; use Composer\Repository\InstalledFilesystemRepository;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -117,11 +118,6 @@ EOT
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities))); throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
} }
$dm = $this->createDownloadManager($io, $config);
if ($preferSource) {
$dm->setPreferSource(true);
}
if (null === $repositoryUrl) { if (null === $repositoryUrl) {
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) {
@ -179,13 +175,16 @@ EOT
$package->setSourceReference(substr($package->getPrettyVersion(), 4)); $package->setSourceReference(substr($package->getPrettyVersion(), 4));
} }
$dm = $this->createDownloadManager($io, $config);
$dm->setPreferSource($preferSource) $dm->setPreferSource($preferSource)
->setPreferDist($preferDist); ->setPreferDist($preferDist);
$projectInstaller = new ProjectInstaller($directory, $dm); $projectInstaller = new ProjectInstaller($directory, $dm);
$projectInstaller->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), $package); $im = $this->createInstallationManager();
if ($package->getRepository() instanceof NotifiableRepositoryInterface) { $im->addInstaller($projectInstaller);
$package->getRepository()->notifyInstall($package); $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
} $im->notifyInstalls();
$installedFromVcs = 'source' === $package->getInstallationSource(); $installedFromVcs = 'source' === $package->getInstallationSource();
$io->write('<info>Created project in ' . $directory . '</info>'); $io->write('<info>Created project in ' . $directory . '</info>');
@ -194,7 +193,7 @@ EOT
putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion()); putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion());
// clean up memory // clean up memory
unset($dm, $config, $projectInstaller, $sourceRepo, $package); unset($dm, $im, $config, $projectInstaller, $sourceRepo, $package);
// install dependencies of the created project // install dependencies of the created project
$composer = Factory::create($io); $composer = Factory::create($io);
@ -248,4 +247,9 @@ EOT
return $factory->createDownloadManager($io, $config); return $factory->createDownloadManager($io, $config);
} }
protected function createInstallationManager()
{
return new InstallationManager();
}
} }

View File

@ -223,7 +223,7 @@ class Factory
$dm = $this->createDownloadManager($io, $config); $dm = $this->createDownloadManager($io, $config);
// initialize installation manager // initialize installation manager
$im = $this->createInstallationManager($config); $im = $this->createInstallationManager();
// initialize composer // initialize composer
$composer = new Composer(); $composer = new Composer();
@ -305,12 +305,11 @@ class Factory
} }
/** /**
* @param Config $config
* @return Installer\InstallationManager * @return Installer\InstallationManager
*/ */
protected function createInstallationManager(Config $config) protected function createInstallationManager()
{ {
return new Installer\InstallationManager($config->get('vendor-dir')); return new Installer\InstallationManager();
} }
/** /**

View File

@ -15,7 +15,6 @@ namespace Composer\Installer;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
use Composer\Repository\NotifiableRepositoryInterface;
use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepositoryInterface;
use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\InstallOperation;
@ -23,6 +22,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation; use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation; use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
use Composer\Util\StreamContextFactory;
/** /**
* Package operation manager. * Package operation manager.
@ -52,6 +52,19 @@ class InstallationManager
$this->cache = array(); $this->cache = array();
} }
/**
* Removes installer
*
* @param InstallerInterface $installer installer instance
*/
public function removeInstaller(InstallerInterface $installer)
{
if (false !== ($key = array_search($installer, $this->installers, true))) {
array_splice($this->installers, $key, 1);
$this->cache = array();
}
}
/** /**
* Disables custom installers. * Disables custom installers.
* *
@ -219,16 +232,60 @@ class InstallationManager
public function notifyInstalls() public function notifyInstalls()
{ {
foreach ($this->notifiablePackages as $packages) { foreach ($this->notifiablePackages as $repoUrl => $packages) {
$repo = reset($packages)->getRepository(); // non-batch API, deprecated
$repo->notifyInstalls($packages); if (strpos($repoUrl, '%package%')) {
foreach ($packages as $package) {
$url = str_replace('%package%', $package->getPrettyName(), $repoUrl);
$params = array(
'version' => $package->getPrettyVersion(),
'version_normalized' => $package->getVersion(),
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($params, '', '&'),
'timeout' => 3,
)
);
$context = StreamContextFactory::getContext($opts);
@file_get_contents($url, false, $context);
} }
return;
}
$postData = array('downloads' => array());
foreach ($packages as $package) {
$postData['downloads'][] = array(
'name' => $package->getPrettyName(),
'version' => $package->getVersion(),
);
}
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode($postData),
'timeout' => 6,
)
);
$context = StreamContextFactory::getContext($opts);
@file_get_contents($repoUrl, false, $context);
}
$this->reset();
} }
private function markForNotification(PackageInterface $package) private function markForNotification(PackageInterface $package)
{ {
if ($package->getRepository() instanceof NotifiableRepositoryInterface) { if ($package->getNotificationUrl()) {
$this->notifiablePackages[spl_object_hash($package->getRepository())][$package->getName()] = $package; $this->notifiablePackages[$package->getNotificationUrl()][$package->getName()] = $package;
} }
} }
} }

View File

@ -307,6 +307,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{ {
return $this->aliasOf->getSupport(); return $this->aliasOf->getSupport();
} }
public function getNotificationUrl()
{
return $this->aliasOf->getNotificationUrl();
}
public function __toString() public function __toString()
{ {
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')'; return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';

View File

@ -31,6 +31,7 @@ class ArrayDumper
'extra', 'extra',
'installationSource' => 'installation-source', 'installationSource' => 'installation-source',
'autoload', 'autoload',
'notificationUrl' => 'notification-url',
'includePaths' => 'include-path', 'includePaths' => 'include-path',
); );

View File

@ -142,6 +142,10 @@ class ArrayLoader implements LoaderInterface
} }
} }
if (!empty($config['notification-url'])) {
$package->setNotificationUrl($config['notification-url']);
}
if ($package instanceof Package\CompletePackageInterface) { if ($package instanceof Package\CompletePackageInterface) {
if (isset($config['scripts']) && is_array($config['scripts'])) { if (isset($config['scripts']) && is_array($config['scripts'])) {
foreach ($config['scripts'] as $event => $listeners) { foreach ($config['scripts'] as $event => $listeners) {

View File

@ -41,6 +41,7 @@ class Package extends BasePackage
protected $prettyAlias; protected $prettyAlias;
protected $dev; protected $dev;
protected $stability; protected $stability;
protected $notificationUrl;
protected $requires = array(); protected $requires = array();
protected $conflicts = array(); protected $conflicts = array();
@ -506,4 +507,22 @@ class Package extends BasePackage
{ {
return $this->includePaths; return $this->includePaths;
} }
/**
* Sets the notification URL
*
* @param string $notificationUrl
*/
public function setNotificationUrl($notificationUrl)
{
$this->notificationUrl = $notificationUrl;
}
/**
* {@inheritDoc}
*/
public function getNotificationUrl()
{
return $this->notificationUrl;
}
} }

View File

@ -288,6 +288,13 @@ interface PackageInterface
*/ */
public function getUniqueName(); public function getUniqueName();
/**
* Returns the package notification url
*
* @return string
*/
public function getNotificationUrl();
/** /**
* Converts the package into a readable and unique string * Converts the package into a readable and unique string
* *

View File

@ -22,12 +22,11 @@ use Composer\Cache;
use Composer\Config; use Composer\Config;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Util\RemoteFilesystem; use Composer\Util\RemoteFilesystem;
use Composer\Util\StreamContextFactory;
/** /**
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface, StreamableRepositoryInterface class ComposerRepository extends ArrayRepository implements StreamableRepositoryInterface
{ {
protected $config; protected $config;
protected $options; protected $options;
@ -77,61 +76,6 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
$this->loader = new ArrayLoader(); $this->loader = new ArrayLoader();
} }
/**
* {@inheritDoc}
*/
public function notifyInstalls(array $packages)
{
if (!$this->notifyUrl || !$this->config->get('notify-on-install')) {
return;
}
// non-batch API, deprecated
if (strpos($this->notifyUrl, '%package%')) {
foreach ($packages as $package) {
$url = str_replace('%package%', $package->getPrettyName(), $this->notifyUrl);
$params = array(
'version' => $package->getPrettyVersion(),
'version_normalized' => $package->getVersion(),
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($params, '', '&'),
'timeout' => 3,
)
);
$context = StreamContextFactory::getContext($opts);
@file_get_contents($url, false, $context);
}
return;
}
$postData = array('downloads' => array());
foreach ($packages as $package) {
$postData['downloads'][] = array(
'name' => $package->getPrettyName(),
'version' => $package->getVersion(),
);
}
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode($postData),
'timeout' => 6,
)
);
$context = StreamContextFactory::getContext($opts);
@file_get_contents($this->notifyUrl, false, $context);
}
public function setRootAliases(array $rootAliases) public function setRootAliases(array $rootAliases)
{ {
$this->rootAliases = $rootAliases; $this->rootAliases = $rootAliases;
@ -459,6 +403,8 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
protected function createPackage(array $data, $class) protected function createPackage(array $data, $class)
{ {
try { try {
$data['notification-url'] = $this->notifyUrl;
return $this->loader->load($data, 'Composer\Package\CompletePackage'); return $this->loader->load($data, 'Composer\Package\CompletePackage');
} catch (\Exception $e) { } catch (\Exception $e) {
throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e); throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e);

View File

@ -1,28 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Repository;
use Composer\Package\PackageInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface NotifiableRepositoryInterface extends RepositoryInterface
{
/**
* Notify this repository about the installation of a package
*
* @param PackageInterface[] $packages Packages that were installed
*/
public function notifyInstalls(array $packages);
}

View File

@ -35,7 +35,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
return $arg === 'vendor'; return $arg === 'vendor';
})); }));
$manager = new InstallationManager('vendor'); $manager = new InstallationManager();
$manager->addInstaller($installer); $manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor')); $this->assertSame($installer, $manager->getInstaller('vendor'));
@ -44,6 +44,36 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
$manager->getInstaller('unregistered'); $manager->getInstaller('unregistered');
} }
public function testAddRemoveInstaller()
{
$installer = $this->createInstallerMock();
$installer
->expects($this->exactly(2))
->method('supports')
->will($this->returnCallback(function ($arg) {
return $arg === 'vendor';
}));
$installer2 = $this->createInstallerMock();
$installer2
->expects($this->exactly(1))
->method('supports')
->will($this->returnCallback(function ($arg) {
return $arg === 'vendor';
}));
$manager = new InstallationManager();
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
$manager->addInstaller($installer2);
$this->assertSame($installer2, $manager->getInstaller('vendor'));
$manager->removeInstaller($installer2);
$this->assertSame($installer, $manager->getInstaller('vendor'));
}
public function testExecute() public function testExecute()
{ {
$manager = $this->getMockBuilder('Composer\Installer\InstallationManager') $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')
@ -77,7 +107,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testInstall() public function testInstall()
{ {
$installer = $this->createInstallerMock(); $installer = $this->createInstallerMock();
$manager = new InstallationManager('vendor'); $manager = new InstallationManager();
$manager->addInstaller($installer); $manager->addInstaller($installer);
$package = $this->createPackageMock(); $package = $this->createPackageMock();
@ -105,7 +135,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testUpdateWithEqualTypes() public function testUpdateWithEqualTypes()
{ {
$installer = $this->createInstallerMock(); $installer = $this->createInstallerMock();
$manager = new InstallationManager('vendor'); $manager = new InstallationManager();
$manager->addInstaller($installer); $manager->addInstaller($installer);
$initial = $this->createPackageMock(); $initial = $this->createPackageMock();
@ -139,7 +169,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
{ {
$libInstaller = $this->createInstallerMock(); $libInstaller = $this->createInstallerMock();
$bundleInstaller = $this->createInstallerMock(); $bundleInstaller = $this->createInstallerMock();
$manager = new InstallationManager('vendor'); $manager = new InstallationManager();
$manager->addInstaller($libInstaller); $manager->addInstaller($libInstaller);
$manager->addInstaller($bundleInstaller); $manager->addInstaller($bundleInstaller);
@ -185,7 +215,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase
public function testUninstall() public function testUninstall()
{ {
$installer = $this->createInstallerMock(); $installer = $this->createInstallerMock();
$manager = new InstallationManager('vendor'); $manager = new InstallationManager();
$manager->addInstaller($installer); $manager->addInstaller($installer);
$package = $this->createPackageMock(); $package = $this->createPackageMock();