diff --git a/composer.json b/composer.json
index ae036f6..3f4931f 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,8 @@
},
"require-dev": {
"phpunit/phpunit": "^5.4.7",
- "composer/composer": "^1.2.0"
+ "composer/composer": "^1.3",
+ "ext-zip": "*"
},
"autoload": {
"psr-4": {
diff --git a/src/PackageVersions/Installer.php b/src/PackageVersions/Installer.php
index 7756ce7..81ff5b4 100644
--- a/src/PackageVersions/Installer.php
+++ b/src/PackageVersions/Installer.php
@@ -80,14 +80,20 @@ public static function getSubscribedEvents()
*/
public static function dumpVersionsClass(Event $composerEvent)
{
- $io = $composerEvent->getIO();
+ $composer = $composerEvent->getComposer();
+ $versions = iterator_to_array(self::getVersions($composer->getLocker(), $composer->getPackage()));
- $io->write('ocramius/package-versions: Generating version class...');
+ if (!array_key_exists('ocramius/package-versions', $versions)) {
+ //plugin must be globally installed - we only want to generate versions for projects which specifically
+ //require ocramius/package-versions
+ return;
+ }
- $composer = $composerEvent->getComposer();
+ $io = $composerEvent->getIO();
+ $io->write('ocramius/package-versions: Generating version class...');
self::writeVersionClassToFile(
- self::generateVersionsClass($composer),
+ self::generateVersionsClass($versions),
$composer->getConfig(),
$composer->getPackage()
);
@@ -95,12 +101,12 @@ public static function dumpVersionsClass(Event $composerEvent)
$io->write('ocramius/package-versions: ...done generating version class');
}
- private static function generateVersionsClass(Composer $composer) : string
+ private static function generateVersionsClass(array $versions) : string
{
return sprintf(
self::$generatedClassTemplate,
'fin' . 'al ' . 'cla' . 'ss ' . 'Versions', // note: workaround for regex-based code parsers :-(
- var_export(iterator_to_array(self::getVersions($composer->getLocker(), $composer->getPackage())), true)
+ var_export($versions, true)
);
}
diff --git a/test/PackageVersionsTest/E2EInstallerTest.php b/test/PackageVersionsTest/E2EInstallerTest.php
new file mode 100644
index 0000000..8694edd
--- /dev/null
+++ b/test/PackageVersionsTest/E2EInstallerTest.php
@@ -0,0 +1,245 @@
+tempGlobalComposerHome = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/global';
+ $this->tempLocalComposerHome = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/local';
+ $this->tempArtifact = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/artifacts';
+ mkdir($this->tempGlobalComposerHome, 0700, true);
+ mkdir($this->tempLocalComposerHome, 0700, true);
+ mkdir($this->tempArtifact, 0700, true);
+
+ putenv('COMPOSER_HOME=' . $this->tempGlobalComposerHome);
+ }
+
+ public function tearDown()
+ {
+ $this->rmDir($this->tempGlobalComposerHome);
+ $this->rmDir($this->tempLocalComposerHome);
+ $this->rmDir($this->tempArtifact);
+
+ putenv('COMPOSER_HOME');
+ }
+
+ public function testGloballyInstalledPluginDoesNotGenerateVersionsForLocalProject()
+ {
+ $this->createPackageVersionsArtifact();
+
+ $this->writeComposerJsonFile(
+ [
+ 'name' => 'package-versions/e2e-global',
+ 'require' => [
+ 'ocramius/package-versions' => '1.0.0'
+ ],
+ 'repositories' => [
+ [
+ 'packagist' => false,
+ ],
+ [
+ 'type' => 'artifact',
+ 'url' => $this->tempArtifact,
+ ]
+ ]
+ ],
+ $this->tempGlobalComposerHome
+ );
+
+ $this->execComposerInDir('global update', $this->tempGlobalComposerHome);
+
+ $this->createArtifact();
+ $this->writeComposerJsonFile(
+ [
+ 'name' => 'package-versions/e2e-local',
+ 'require' => [
+ 'test/package' => '2.0.0'
+ ],
+ 'repositories' => [
+ [
+ 'packagist' => false,
+ ],
+ [
+ 'type' => 'artifact',
+ 'url' => $this->tempArtifact,
+ ]
+ ]
+ ],
+ $this->tempLocalComposerHome
+ );
+
+ $this->execComposerInDir('update', $this->tempLocalComposerHome);
+ $this->assertFileNotExists(
+ $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
+ );
+ }
+
+ public function testRemovingPluginDoesNotAttemptToGenerateVersions()
+ {
+ $this->createPackageVersionsArtifact();
+ $this->createArtifact();
+
+ $this->writeComposerJsonFile(
+ [
+ 'name' => 'package-versions/e2e-local',
+ 'require' => [
+ 'test/package' => '2.0.0',
+ 'ocramius/package-versions' => '1.0.0'
+ ],
+ 'repositories' => [
+ [
+ 'packagist' => false,
+ ],
+ [
+ 'type' => 'artifact',
+ 'url' => $this->tempArtifact,
+ ]
+ ]
+ ],
+ $this->tempLocalComposerHome
+ );
+
+ $this->execComposerInDir('update', $this->tempLocalComposerHome);
+ $this->assertFileExists(
+ $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
+ );
+
+ $this->execComposerInDir('remove ocramius/package-versions', $this->tempLocalComposerHome);
+
+ $this->assertFileNotExists(
+ $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
+ );
+ }
+
+ private function createPackageVersionsArtifact()
+ {
+ $zip = new ZipArchive();
+
+ $zip->open($this->tempArtifact . '/ocramius-package-versions-1.0.0.zip', ZipArchive::CREATE);
+
+ $files = array_filter(
+ iterator_to_array(new RecursiveIteratorIterator(
+ new RecursiveCallbackFilterIterator(
+ new RecursiveDirectoryIterator(realpath(__DIR__ . '/../../'), RecursiveDirectoryIterator::SKIP_DOTS),
+ function (SplFileInfo $file, $key, RecursiveDirectoryIterator $iterator) {
+ return $iterator->getSubPathname()[0] !== '.' && $iterator->getSubPathname() !== 'vendor';
+ }
+ ),
+ RecursiveIteratorIterator::LEAVES_ONLY
+ )),
+ function (SplFileInfo $file) {
+ return !$file->isDir();
+ }
+ );
+
+ array_walk(
+ $files,
+ function (SplFileInfo $file) use ($zip) {
+ if ($file->getFilename() === 'composer.json') {
+ $contents = json_decode(file_get_contents($file->getRealPath()), true);
+ $contents['version'] = '1.0.0';
+
+ return $zip->addFromString('composer.json', json_encode($contents));
+ }
+
+ $zip->addFile(
+ $file->getRealPath(),
+ substr($file->getRealPath(), strlen(realpath(__DIR__ . '/../../')) + 1)
+ );
+ }
+ );
+
+ $zip->close();
+ }
+
+ private function createArtifact()
+ {
+ $zip = new ZipArchive();
+
+ $zip->open($this->tempArtifact . '/test-package-2.0.0.zip', ZipArchive::CREATE);
+ $zip->addFromString(
+ 'composer.json',
+ json_encode(
+ [
+ 'name' => 'test/package',
+ 'version' => '2.0.0'
+ ],
+ JSON_PRETTY_PRINT
+ )
+ );
+ $zip->close();
+ }
+
+ private function writeComposerJsonFile(array $config, string $directory)
+ {
+ file_put_contents(
+ $directory . '/composer.json',
+ json_encode($config, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
+ );
+ }
+
+ private function execComposerInDir(string $command, string $dir) : array
+ {
+ $currentDir = getcwd();
+ chdir($dir);
+ exec(__DIR__ . '/../../vendor/bin/composer ' . $command . ' 2> /dev/null', $output, $exitCode);
+ $this->assertEquals(0, $exitCode);
+ chdir($currentDir);
+ return $output;
+ }
+
+ /**
+ * @param string $directory
+ *
+ * @return void
+ */
+ private function rmDir(string $directory)
+ {
+ if (! is_dir($directory)) {
+ unlink($directory);
+
+ return;
+ }
+
+ array_map(
+ function ($item) use ($directory) {
+ $this->rmDir($directory . '/' . $item);
+ },
+ array_filter(
+ scandir($directory),
+ function (string $dirItem) {
+ return ! in_array($dirItem, ['.', '..'], true);
+ }
+ )
+ );
+
+ rmdir($directory);
+ }
+}
diff --git a/test/PackageVersionsTest/InstallerTest.php b/test/PackageVersionsTest/InstallerTest.php
index c7cac80..bebd0ab 100644
--- a/test/PackageVersionsTest/InstallerTest.php
+++ b/test/PackageVersionsTest/InstallerTest.php
@@ -96,6 +96,10 @@ public function testDumpVersionsClass()
->method('getLockData')
->willReturn([
'packages' => [
+ [
+ 'name' => 'ocramius/package-versions',
+ 'version' => '1.0.0',
+ ],
[
'name' => 'foo/bar',
'version' => '1.2.3',
@@ -156,6 +160,7 @@ public function testDumpVersionsClass()
final class Versions
{
const VERSIONS = array (
+ 'ocramius/package-versions' => '1.0.0@',
'foo/bar' => '1.2.3@abc123',
'baz/tab' => '4.5.6@def456',
'tar/taz' => '7.8.9@ghi789',
@@ -208,6 +213,10 @@ public function testDumpVersionsClassNoDev()
->method('getLockData')
->willReturn([
'packages' => [
+ [
+ 'name' => 'ocramius/package-versions',
+ 'version' => '1.0.0',
+ ],
[
'name' => 'foo/bar',
'version' => '1.2.3',
@@ -259,6 +268,7 @@ public function testDumpVersionsClassNoDev()
final class Versions
{
const VERSIONS = array (
+ 'ocramius/package-versions' => '1.0.0@',
'foo/bar' => '1.2.3@abc123',
'baz/tab' => '4.5.6@def456',
'root/package' => '1.3.5@aaabbbcccddd',
@@ -315,6 +325,10 @@ public function testDumpVersionsWithoutPackageSourceDetails()
->method('getLockData')
->willReturn([
'packages' => [
+ [
+ 'name' => 'ocramius/package-versions',
+ 'version' => '1.0.0',
+ ],
[
'name' => 'foo/bar',
'version' => '1.2.3',
@@ -363,6 +377,7 @@ public function testDumpVersionsWithoutPackageSourceDetails()
final class Versions
{
const VERSIONS = array (
+ 'ocramius/package-versions' => '1.0.0@',
'foo/bar' => '1.2.3@abc123',
'baz/tab' => '4.5.6@',
'root/package' => '1.3.5@aaabbbcccddd',
@@ -424,7 +439,12 @@ public function testDumpsVersionsClassToSpecificLocation(RootPackageInterface $r
->expects(self::any())
->method('getLockData')
->willReturn([
- 'packages' => [],
+ 'packages' => [
+ [
+ 'name' => 'ocramius/package-versions',
+ 'version' => '1.0.0',
+ ]
+ ],
'packages-dev' => [],
]);
@@ -497,6 +517,65 @@ public function rootPackageProvider() : array
];
}
+ public function testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired()
+ {
+ $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
+ $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
+ $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
+ $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
+ $repository = $this->createMock(InstalledRepositoryInterface::class);
+ $package = $this->createMock(RootPackageInterface::class);
+
+ $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
+
+ $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
+
+ mkdir($expectedPath, 0777, true);
+
+ $locker
+ ->expects(self::any())
+ ->method('getLockData')
+ ->willReturn([
+ 'packages' => [
+ [
+ 'name' => 'foo/bar',
+ 'version' => '1.2.3',
+ 'dist' => [
+ 'reference' => 'abc123', // version defined in the dist, this time
+ ],
+ ],
+ [
+ 'name' => 'baz/tab',
+ 'version' => '4.5.6', // source missing
+ ],
+ ],
+ ]);
+
+ $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
+
+ $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
+ $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
+ $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
+ $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
+ $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
+
+ $package->expects(self::any())->method('getName')->willReturn('root/package');
+ $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
+ $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
+
+ $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
+
+ Installer::dumpVersionsClass(new Event(
+ 'post-install-cmd',
+ $this->composer,
+ $this->io
+ ));
+
+ self::assertFileNotExists($expectedPath . '/Versions.php');
+
+ $this->rmDir($vendorDir);
+ }
+
/**
* @param string $directory
*