Add support for TAR in Artifact packages (#9105)
parent
ff757e649c
commit
657ae5519e
|
@ -516,7 +516,7 @@ There are some cases, when there is no ability to have one of the previously
|
||||||
mentioned repository types online, even the VCS one. Typical example could be
|
mentioned repository types online, even the VCS one. Typical example could be
|
||||||
cross-organisation library exchange through built artifacts. Of course, most
|
cross-organisation library exchange through built artifacts. Of course, most
|
||||||
of the times they are private. To simplify maintenance, one can simply use a
|
of the times they are private. To simplify maintenance, one can simply use a
|
||||||
repository of type `artifact` with a folder containing ZIP archives of those
|
repository of type `artifact` with a folder containing ZIP or TAR archives of those
|
||||||
private packages:
|
private packages:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Composer\IO\IOInterface;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\Loader\ArrayLoader;
|
use Composer\Package\Loader\ArrayLoader;
|
||||||
use Composer\Package\Loader\LoaderInterface;
|
use Composer\Package\Loader\LoaderInterface;
|
||||||
|
use Composer\Util\Tar;
|
||||||
use Composer\Util\Zip;
|
use Composer\Util\Zip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +67,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
|
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
|
||||||
$iterator = new \RecursiveIteratorIterator($directory);
|
$iterator = new \RecursiveIteratorIterator($directory);
|
||||||
$regex = new \RegexIterator($iterator, '/^.+\.(zip|phar)$/i');
|
$regex = new \RegexIterator($iterator, '/^.+\.(zip|phar|tar|gz|tgz)$/i');
|
||||||
foreach ($regex as $file) {
|
foreach ($regex as $file) {
|
||||||
/* @var $file \SplFileInfo */
|
/* @var $file \SplFileInfo */
|
||||||
if (!$file->isFile()) {
|
if (!$file->isFile()) {
|
||||||
|
@ -89,8 +90,22 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
private function getComposerInformation(\SplFileInfo $file)
|
private function getComposerInformation(\SplFileInfo $file)
|
||||||
{
|
{
|
||||||
$json = null;
|
$json = null;
|
||||||
|
$fileType = null;
|
||||||
|
$fileExtension = pathinfo($file->getPathname(), PATHINFO_EXTENSION);
|
||||||
|
if (in_array($fileExtension, array('gz', 'tar', 'tgz'), true)) {
|
||||||
|
$fileType = 'tar';
|
||||||
|
} else if ($fileExtension === 'zip') {
|
||||||
|
$fileType = 'zip';
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException('Files with "'.$fileExtension.'" extensions aren\'t supported. Only ZIP and TAR/TAR.GZ/TGZ archives are supported.');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$json = Zip::getComposerJson($file->getPathname());
|
if ($fileType === 'tar') {
|
||||||
|
$json = Tar::getComposerJson($file->getPathname());
|
||||||
|
} else {
|
||||||
|
$json = Zip::getComposerJson($file->getPathname());
|
||||||
|
}
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
$this->io->write('Failed loading package '.$file->getPathname().': '.$exception->getMessage(), false, IOInterface::VERBOSE);
|
$this->io->write('Failed loading package '.$file->getPathname().': '.$exception->getMessage(), false, IOInterface::VERBOSE);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +116,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
$package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
|
$package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
|
||||||
$package['dist'] = array(
|
$package['dist'] = array(
|
||||||
'type' => 'zip',
|
'type' => $fileType,
|
||||||
'url' => strtr($file->getPathname(), '\\', '/'),
|
'url' => strtr($file->getPathname(), '\\', '/'),
|
||||||
'shasum' => sha1_file($file->getRealPath()),
|
'shasum' => sha1_file($file->getRealPath()),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?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\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wissem Riahi <wissemr@gmail.com>
|
||||||
|
*/
|
||||||
|
class Tar
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $pathToArchive
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function getComposerJson($pathToArchive)
|
||||||
|
{
|
||||||
|
$phar = new \PharData($pathToArchive);
|
||||||
|
|
||||||
|
if (!$phar->valid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::extractComposerJsonFromFolder($phar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \PharData $phar
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function extractComposerJsonFromFolder(\PharData $phar)
|
||||||
|
{
|
||||||
|
if (isset($phar['composer.json'])) {
|
||||||
|
return $phar['composer.json']->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
$topLevelPaths = array();
|
||||||
|
foreach ($phar as $folderFile) {
|
||||||
|
$name = $folderFile->getBasename();
|
||||||
|
|
||||||
|
if ($folderFile->isDir()) {
|
||||||
|
$topLevelPaths[$name] = true;
|
||||||
|
if (\count($topLevelPaths) > 1) {
|
||||||
|
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$composerJsonPath = key($topLevelPaths).'/composer.json';
|
||||||
|
if ($topLevelPaths && isset($phar[$composerJsonPath])) {
|
||||||
|
return $phar[$composerJsonPath]->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('No composer.json found either at the top level or within the topmost directory');
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ class ArtifactRepositoryTest extends TestCase
|
||||||
'vendor1/package2-4.3.2',
|
'vendor1/package2-4.3.2',
|
||||||
'vendor3/package1-5.4.3',
|
'vendor3/package1-5.4.3',
|
||||||
'test/jsonInRoot-1.0.0',
|
'test/jsonInRoot-1.0.0',
|
||||||
|
'test/jsonInRootTarFile-1.0.0',
|
||||||
'test/jsonInFirstLevel-1.0.0',
|
'test/jsonInFirstLevel-1.0.0',
|
||||||
//The files not-an-artifact.zip and jsonSecondLevel are not valid
|
//The files not-an-artifact.zip and jsonSecondLevel are not valid
|
||||||
//artifacts and do not get detected.
|
//artifacts and do not get detected.
|
||||||
|
@ -52,6 +53,13 @@ class ArtifactRepositoryTest extends TestCase
|
||||||
sort($foundPackages);
|
sort($foundPackages);
|
||||||
|
|
||||||
$this->assertSame($expectedPackages, $foundPackages);
|
$this->assertSame($expectedPackages, $foundPackages);
|
||||||
|
|
||||||
|
$tarPackage = array_filter($repo->getPackages(), function (BasePackage $package) {
|
||||||
|
return $package->getPrettyName() === 'test/jsonInRootTarFile';
|
||||||
|
});
|
||||||
|
$this->assertCount(1, $tarPackage);
|
||||||
|
$tarPackage = array_pop($tarPackage);
|
||||||
|
$this->assertSame('tar', $tarPackage->getDistType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAbsoluteRepoUrlCreatesAbsoluteUrlPackages()
|
public function testAbsoluteRepoUrlCreatesAbsoluteUrlPackages()
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,71 @@
|
||||||
|
<?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\Test\Util;
|
||||||
|
|
||||||
|
use Composer\Util\Tar;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wissem Riahi <wissemr@gmail.com>
|
||||||
|
*/
|
||||||
|
class TarTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testReturnsNullifTheTarIsNotFound()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/invalid.zip');
|
||||||
|
|
||||||
|
$this->assertNull($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsNullIfTheTarIsEmpty()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/empty.tar.gz');
|
||||||
|
$this->assertNull($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheTarHasNoComposerJson()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/nojson.tar.gz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testThrowsExceptionIfTheComposerJsonIsInASubSubfolder()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/subfolders.tar.gz');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsComposerJsonInTarRoot()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/root.tar.gz');
|
||||||
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsComposerJsonInFirstFolder()
|
||||||
|
{
|
||||||
|
$result = Tar::getComposerJson(__DIR__.'/Fixtures/Tar/folder.tar.gz');
|
||||||
|
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \RuntimeException
|
||||||
|
*/
|
||||||
|
public function testMultipleTopLevelDirsIsInvalid()
|
||||||
|
{
|
||||||
|
Tar::getComposerJson(__DIR__.'/Fixtures/Tar/multiple.tar.gz');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue