1
0
Fork 0

Add support for TAR in Artifact packages (#9105)

pull/9122/head
Wissem Riahi 2020-08-12 20:30:58 +02:00 committed by GitHub
parent ff757e649c
commit 657ae5519e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 166 additions and 4 deletions

View File

@ -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

View File

@ -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()),
); );

68
src/Composer/Util/Tar.php Normal file
View File

@ -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');
}
}

View File

@ -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.

View File

@ -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');
}
}