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

Commit

Permalink
Merge pull request #18 from tienvx/split-archive-handlers
Browse files Browse the repository at this point in the history
Split archive handlers
  • Loading branch information
tienvx authored Nov 25, 2022
2 parents c7ecabd + 2206ccd commit 0f0b5b3
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 86 deletions.
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,15 @@ When a downstream user of `foo/bar` runs `composer require foo/bar`, it will dow
* `path`: The releative path where content will be extracted.

* `type`: (*Optional*) Determines how the download is handled. If omit, the extension in `url` will be used to detect.
* `archive`: The archive file `url` will be downloaded and extracted to `path`. Supported extensions:
* `*.zip`
* `*.rar`
* `*.tar.xz`
* `*.tar.gz`
* `*.tar.bz2`
* `*.tar`
* `*.gz`
* `*.tgz`
* `file`: The file `url` will be downloaded and placed at `path`.
* `phar`: The PHP executable file `url` will be downloaded and placed at `path`.
* `gzip`: The gzip file `url` will be downloaded and extracted to a file and that file will be placed at `path`.
* Archive types (The archive file `url` will be downloaded and extracted to `path`):
* `zip`: . Support extension `*.zip`
* `rar`: Support extension `*.rar`
* `xz`: Support extension `*.tar.xz`
* `tar`: Support extensions `*.tar.gz`, `*.tar.bz2`, `*.tar`, `*.tgz`
* File types (The file `url` will be downloaded and placed at `path`):
* `file`
* `phar`: The file will be mark as executable.
* `gzip`: The `*.gz` file will be extracted to a file that will be placed at `path`.

* `ignore`: (*Optional*) A list of a files that should be omited from the extracted folder. (This supports a subset of `.gitignore` notation.)

Expand Down
8 changes: 0 additions & 8 deletions doc/TIPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,3 @@
* Download each extra file to a distinct `path`. Don't try to download into overlapping paths. (*This has not been tested, but I expect downloads are not well-ordered, and you may find that updates require re-downloading.*)

* What should you do if you *normally* download the extra-file as `*.tar.gz` but sometimes (for local dev) need to grab bleeding edge content from somewhere else? Simply delete the autodownloaded folder and replace it with your own. `composer-downloads-plugin` will detect that conflict (by virtue of the absent `.composer-downloads`) and leave your code in place (until you choose to get rid of it). To switch back, you can simply delete the code and run `composer install` again.

* The handler for `archive` type will extract files into `path` directory
* The handlers for `file` and `phar` types will put the file at `path` (not into `path` directory)
* The handler for `gzip` type will decompress the file and put the decompressed file at `path` (not into `path` directory)

* The different between using `archive` type and `gzip` type for the file `exactly-this-file-name.gz` is:
* The handler for `archive` type will put the decompressed file at `/path/to/exactly-this-file-name`
* The handler for `gzip` type will put the decompressed file at `/path/to/any-thing-you-want`
50 changes: 37 additions & 13 deletions src/DownloadsParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,36 @@
namespace LastCall\DownloadsPlugin;

use Composer\Package\PackageInterface;
use LastCall\DownloadsPlugin\Handler\ArchiveHandler;
use LastCall\DownloadsPlugin\Handler\BaseHandler;
use LastCall\DownloadsPlugin\Handler\FileHandler;
use LastCall\DownloadsPlugin\Handler\GzipHandler;
use LastCall\DownloadsPlugin\Handler\PharHandler;
use LastCall\DownloadsPlugin\Handler\RarHandler;
use LastCall\DownloadsPlugin\Handler\TarHandler;
use LastCall\DownloadsPlugin\Handler\XzHandler;
use LastCall\DownloadsPlugin\Handler\ZipHandler;
use Le\SMPLang\SMPLang;

class DownloadsParser
{
public const EXTENSION_TO_TYPE_MAP = [
'zip' => 'zip',
'rar' => 'rar',
'tgz' => 'tar',
'tar' => 'tar',
'gz' => 'gzip',
'phar' => 'phar',
];
public const TYPE_TO_HANDLER_CLASS_MAP = [
'zip' => ZipHandler::class,
'rar' => RarHandler::class,
'tar' => TarHandler::class,
'xz' => XzHandler::class,
'file' => FileHandler::class,
'phar' => PharHandler::class,
'gzip' => GzipHandler::class,
];

/**
* @return baseHandler[] Each item is a specification of an extra file, with defaults and variables evaluated
*/
Expand Down Expand Up @@ -55,23 +76,26 @@ public function parse(PackageInterface $package, string $basePath): array

private function pickClass(array $extraFile): string
{
$types = [
'archive' => ArchiveHandler::class,
'file' => FileHandler::class,
'phar' => PharHandler::class,
'gzip' => GzipHandler::class,
];
if (isset($extraFile['type'], $types[$extraFile['type']])) {
return $types[$extraFile['type']];
if (isset($extraFile['type'], self::TYPE_TO_HANDLER_CLASS_MAP[$extraFile['type']])) {
return self::TYPE_TO_HANDLER_CLASS_MAP[$extraFile['type']];
}

$parts = parse_url($extraFile['url']);
return self::TYPE_TO_HANDLER_CLASS_MAP[$this->parseType($extraFile['url'])];
}

private function parseType(string $url): string
{
$parts = parse_url($url);
$filename = pathinfo($parts['path'], \PATHINFO_BASENAME);
if (preg_match('/\.(tar\.bz2|tar\.xz|tar\.gz|zip|rar|tar|gz|tgz)$/', $filename)) {
return $types['archive'];
if (preg_match('/\.(tar\.gz|tar\.bz2)$/', $filename)) {
return 'tar';
}
if (preg_match('/\.tar\.xz$/', $filename)) {
return 'xz';
}
$extension = pathinfo($parts['path'], \PATHINFO_EXTENSION);

return $types['file'];
return self::EXTENSION_TO_TYPE_MAP[$extension] ?? 'file';
}

private function getVariables(array $extraFile): array
Expand Down
32 changes: 4 additions & 28 deletions src/Handler/ArchiveHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,16 @@
use LastCall\DownloadsPlugin\GlobCleaner;
use LastCall\DownloadsPlugin\Subpackage;

class ArchiveHandler extends BaseHandler
abstract class ArchiveHandler extends BaseHandler
{
public const EXTENSION_TO_DIST_TYPE_MAP = [
'zip' => 'zip',
'rar' => 'rar',
'tgz' => 'tar',
'tar' => 'tar',
'gz' => 'gzip',
];

protected function createSubpackage(): Subpackage
{
$pkg = parent::createSubpackage();
$pkg->setDistType($this->parseDistType($this->extraFile['url']));
$pkg->setDistType($this->getDistType());

return $pkg;
}

private function parseDistType(string $url): string
{
$parts = parse_url($url);
$filename = pathinfo($parts['path'], \PATHINFO_BASENAME);
if (preg_match('/\.(tar\.gz|tar\.bz2)$/', $filename)) {
return 'tar';
}
if (preg_match('/\.tar\.xz$/', $filename)) {
return 'xz';
}
$extension = pathinfo($parts['path'], \PATHINFO_EXTENSION);
if (isset(self::EXTENSION_TO_DIST_TYPE_MAP[$extension])) {
return self::EXTENSION_TO_DIST_TYPE_MAP[$extension];
} else {
throw new \RuntimeException("Failed to determine archive type for $filename");
}
}

public function getTrackingFile(): string
{
$file = basename($this->extraFile['id']).'-'.md5($this->extraFile['id']).'.json';
Expand Down Expand Up @@ -98,4 +72,6 @@ public function download(Composer $composer, IOInterface $io): void
}
GlobCleaner::clean($io, $targetPath, $this->findIgnores());
}

abstract protected function getDistType(): string;
}
5 changes: 4 additions & 1 deletion src/Handler/GzipHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public function download(Composer $composer, IOInterface $io): void
$cfs->rename($target, $gzip);
$process = new ProcessExecutor($io);
$command = 'gzip -d '.ProcessExecutor::escape($gzip);
$process->execute($command);
if (0 !== $process->execute($command)) {
$processError = 'Failed to execute '.$command."\n\n".$process->getErrorOutput();
throw new \RuntimeException($processError);
}
}
}
11 changes: 11 additions & 0 deletions src/Handler/RarHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LastCall\DownloadsPlugin\Handler;

class RarHandler extends ArchiveHandler
{
protected function getDistType(): string
{
return 'rar';
}
}
11 changes: 11 additions & 0 deletions src/Handler/TarHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LastCall\DownloadsPlugin\Handler;

class TarHandler extends ArchiveHandler
{
protected function getDistType(): string
{
return 'tar';
}
}
11 changes: 11 additions & 0 deletions src/Handler/XzHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LastCall\DownloadsPlugin\Handler;

class XzHandler extends ArchiveHandler
{
protected function getDistType(): string
{
return 'xz';
}
}
11 changes: 11 additions & 0 deletions src/Handler/ZipHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace LastCall\DownloadsPlugin\Handler;

class ZipHandler extends ArchiveHandler
{
protected function getDistType(): string
{
return 'zip';
}
}
7 changes: 3 additions & 4 deletions tests/Integration/DownloadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ private static function getComposerJson(): array
'extra' => [
'downloads' => [
'*' => [
'type' => 'archive',
'path' => 'files/{$id}',
],
'file' => [
Expand Down Expand Up @@ -69,12 +68,10 @@ private static function getComposerJson(): array
'url' => 'http://localhost:8000/archive/image.rar',
],
'xml' => [
'type' => 'gzip',
'url' => 'http://localhost:8000/archive/empty.xml.gz',
'path' => 'files/markup/empty.xml',
],
'html' => [
'type' => 'gzip',
'url' => 'http://localhost:8000/archive/empty.html.gz',
'path' => 'files/markup/empty.html',
],
Expand Down Expand Up @@ -110,7 +107,9 @@ public function getFileChecksums(): array
{
return [
'files/phar/hello' => '047d6ea435e107c214073c58794efc2e6ca1ec8ebcf9b68de21735e5460224c5',
'files/file/ipsum' => \PHP_OS_FAMILY === 'Windows' ? '77559b8e3cf8082554f5cb314729363017de998b63f0ab9cb751246c167d7bdd' : '77bdfb1d37ee5a5e6d08d0bd8f2d4abfde6b673422364ba9ad432deb2d9c6e4d',
'files/file/ipsum' => \PHP_OS_FAMILY === 'Windows'
? '77559b8e3cf8082554f5cb314729363017de998b63f0ab9cb751246c167d7bdd'
: '77bdfb1d37ee5a5e6d08d0bd8f2d4abfde6b673422364ba9ad432deb2d9c6e4d', // New line chars are replaced in Windows
'files/doc/v1.2.3/empty.doc' => '60b5e45db3b51c38a5b762e771ee2f19692f52186c42c3930d56bbdf04d21f4e',
'files/doc/v1.2.3/empty.docx' => '61cdb4b8b9067ab1f4eaa5ba782007c81bdd04283a228b5076aeeb4c9362020b',
'files/doc/v1.3.0/empty.doc' => '60b5e45db3b51c38a5b762e771ee2f19692f52186c42c3930d56bbdf04d21f4e',
Expand Down
46 changes: 26 additions & 20 deletions tests/Unit/DownloadsParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@

use Composer\Package\Package;
use LastCall\DownloadsPlugin\DownloadsParser;
use LastCall\DownloadsPlugin\Handler\ArchiveHandler;
use LastCall\DownloadsPlugin\Handler\FileHandler;
use LastCall\DownloadsPlugin\Handler\GzipHandler;
use LastCall\DownloadsPlugin\Handler\PharHandler;
use LastCall\DownloadsPlugin\Handler\RarHandler;
use LastCall\DownloadsPlugin\Handler\TarHandler;
use LastCall\DownloadsPlugin\Handler\XzHandler;
use LastCall\DownloadsPlugin\Handler\ZipHandler;
use LastCall\DownloadsPlugin\Subpackage;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -51,22 +54,25 @@ public function testIgnoresPackagesWithDefaultDownloads(): void
public function getExplicitDownloadTypeTests(): array
{
return [
['archive', 'foo.zip', 'zip', ArchiveHandler::class],
['file', 'foo', 'file', FileHandler::class],
['phar', 'foo', 'file', PharHandler::class],
['gzip', 'foo', 'file', GzipHandler::class],
['zip', ZipHandler::class, 'zip'],
['rar', RarHandler::class, 'rar'],
['tar', TarHandler::class, 'tar'],
['xz', XzHandler::class, 'xz'],
['file', FileHandler::class, 'file'],
['phar', PharHandler::class, 'file'],
['gzip', GzipHandler::class, 'file'],
];
}

/**
* @dataProvider getExplicitDownloadTypeTests
*/
public function testExplicitDownloadType(string $downloadType, string $url, string $expectedDistType, string $expectedHandlerClass): void
public function testExplicitDownloadType(string $downloadType, string $expectedHandlerClass, string $expectedDistType): void
{
$package = $this->getPackageWithExtraDownloads([
'bar' => ['type' => $downloadType, 'url' => $url, 'path' => 'baz'],
'bar' => ['type' => $downloadType, 'url' => 'foo', 'path' => 'baz'],
]);
$expectSubpackage = new Subpackage($package, 'bar', $url, $expectedDistType, 'baz');
$expectSubpackage = new Subpackage($package, 'bar', 'foo', $expectedDistType, 'baz');
$parsed = (new DownloadsParser())->parse($package, '/EXAMPLE');
$this->assertCount(1, $parsed);
$this->assertInstanceOf($expectedHandlerClass, $parsed[0]);
Expand All @@ -76,19 +82,19 @@ public function testExplicitDownloadType(string $downloadType, string $url, stri
public function getMissingDownloadTypeTests(): array
{
return [
['foo.zip', ArchiveHandler::class, 'zip'],
['foo.zip?foo', ArchiveHandler::class, 'zip'],
['http://example.com/foo.zip?abc#def', ArchiveHandler::class, 'zip'],
['foo.rar', ArchiveHandler::class, 'rar'],
['foo.tar.xz', ArchiveHandler::class, 'xz'],
['foo.tar.gz', ArchiveHandler::class, 'tar'],
['http://example.com/foo.tar.gz?abc#def', ArchiveHandler::class, 'tar'],
['foo.tar.bz2', ArchiveHandler::class, 'tar'],
['foo.tgz', ArchiveHandler::class, 'tar'],
['foo.tar', ArchiveHandler::class, 'tar'],
['foo.gz', ArchiveHandler::class, 'gzip'],
['foo.zip', ZipHandler::class, 'zip'],
['foo.zip?foo', ZipHandler::class, 'zip'],
['http://example.com/foo.zip?abc#def', ZipHandler::class, 'zip'],
['foo.rar', RarHandler::class, 'rar'],
['foo.tar.xz', XzHandler::class, 'xz'],
['foo.tar.gz', TarHandler::class, 'tar'],
['http://example.com/foo.tar.gz?abc#def', TarHandler::class, 'tar'],
['foo.tar.bz2', TarHandler::class, 'tar'],
['foo.tgz', TarHandler::class, 'tar'],
['foo.tar', TarHandler::class, 'tar'],
['foo.gz', GzipHandler::class, 'file'],
['foo', FileHandler::class, 'file'],
['foo.phar', FileHandler::class, 'file'],
['foo.phar', PharHandler::class, 'file'],
];
}

Expand Down

0 comments on commit 0f0b5b3

Please sign in to comment.