An error occurred while loading the file. Please try again.
-
Remi Cresson authored07c5d714
<?php declare(strict_types=1);
/*
* This file is part of "irstea/make-shim".
* (c) 2019-2020 Irstea <dsi.poleis@irstea.fr>
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/
namespace Irstea\MakeShim\Builder;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\StreamWrapper;
use Irstea\MakeShim\Packagist\Package;
use Irstea\MakeShim\Signature\NullVerifier;
use Irstea\MakeShim\Signature\VerifierInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use function Safe\file_put_contents;
use function Safe\json_encode;
use Webmozart\PathUtil\Path;
/**
* Class Builder.
*/
class Builder implements BuilderInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* @var string
*/
private $archiveUrlTemplate;
/**
* @var string|null
*/
private $signatureUrltemplate;
/**
* @var string
*/
private $path;
/**
* @var string
*/
private $packageName;
/**
* @var VerifierInterface
*/
private $verifier;
/**
* @var ClientInterface
*/
private $client;
/**
* Builder constructor.
*
* @param string $packageName
* @param string $path
* @param string $archiveUrlTemplate
* @param string|null $signatureUrltemplate
* @param ClientInterface $client
* @param VerifierInterface|null $verifier
* @param LoggerInterface|null $logger
*/
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
public function __construct(string $packageName, string $path, string $archiveUrlTemplate, ?string $signatureUrltemplate, ClientInterface $client, VerifierInterface $verifier = null, LoggerInterface $logger = null)
{
$this->path = $path;
$this->archiveUrlTemplate = $archiveUrlTemplate;
$this->signatureUrltemplate = $signatureUrltemplate;
$this->setLogger($logger ?: new NullLogger());
$this->packageName = $packageName;
$this->verifier = $verifier ?: new NullVerifier();
$this->client = $client;
}
/**
* {@inheritdoc}
*/
public function build(Package $package): void
{
$this->logger->notice(sprintf('Building shim for %s', $package));
$gitAttributes = [];
$pharURL = $this->formatUrl($this->archiveUrlTemplate, ['version' => $package->getVersion()]);
$binPath = Path::join($this->path, $package->getBinary());
$changed = $this->download($pharURL, $binPath);
\Safe\chmod($binPath, 0755);
$gitAttributes[] = Path::makeRelative($binPath, $this->path) . ' binary';
if ($this->signatureUrltemplate) {
$sigUrl = $this->formatUrl($this->signatureUrltemplate, ['version' => $package->getVersion()]);
$sigPath = Path::join($this->path, $package->getBinary() . '.asc');
$changed = $this->download($sigUrl, $sigPath) || $changed;
$gitAttributes[] = Path::makeRelative($sigPath, $this->path) . ' binary';
if ($changed) {
$this->verifier->verify($sigPath, $binPath);
} else {
$this->logger->debug('neither phar nor signature changed, skipping verification');
}
}
file_put_contents(Path::join($this->path, '.gitattributes'), implode("\n", $gitAttributes));
$this->generateReadMe($package);
$this->generateComposerConfig($package);
}
/**
* @param string $template
* @param array $values
*
* @return string
*/
private function formatUrl(string $template, array $values): string
{
return str_replace(
array_map(
static function (string $name): string {
return '%' . $name . '%';
},
array_keys($values)
),
array_values($values),
$template
);
}
/**
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
* @param string $source
* @param string $dest
*
* @return bool
*/
private function download(string $source, string $dest): bool
{
$checksum = \file_exists($dest) ? sha1_file($dest) : '';
try {
$response = $this->client->request('GET', $source);
$sourceStream = StreamWrapper::getResource($response->getBody());
try {
$destDir = Path::getDirectory($dest);
if (!is_dir($destDir)) {
\Safe\mkdir($destDir, 0750, true);
}
$destStream = \Safe\fopen($dest, 'wb');
try {
$size = \Safe\stream_copy_to_stream($sourceStream, $destStream);
$this->logger->debug("received $size bytes from $source");
} finally {
fclose($destStream);
}
} finally {
fclose($sourceStream);
}
} catch (GuzzleException $exception) {
throw new \RuntimeException(sprintf('could not fetch %s: %s', $source, $exception->getMessage()));
}
return $checksum !== sha1_file($dest);
}
/**
* @param Package $package
*/
private function generateReadMe(Package $package): void
{
$conf = $package->getConfiguration();
$url = $conf['homepage'] ?: sprintf('https://packagist.org/packages/%s', $package->getName());
$licenses = implode(', ', (array) $conf['license']);
$lines = [
"# {$this->packageName} - " . $this->description($package) . '.',
"This package is a drop-in replacement for [{$package->getName()}]($url), which provides its PHAR archive as a binary.",
'It is built automatically from the official PHAR.',
'## Installation',
"\tcomposer require {$this->packageName}",
'or:',
"\tcomposer require --dev {$this->packageName}",
'',
'## Usage',
'As you would use the original package, i.e. something like:',
"\tvendor/bin/" . Path::getFilename($package->getBinary()) . ' [options] [arguments]',
'## License',
'This distribution retains the license of the original software: ' . $licenses,
];
file_put_contents(
Path::join($this->path, 'README.md'),
implode("\n\n", $lines)
);
}
/**
* @param Package $package
*/
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
private function generateComposerConfig(Package $package): void
{
$config = $package->getConfiguration();
$config['name'] = $this->packageName;
$config['description'] = ucfirst($this->description($package));
$config['archive'] = ['exclude' => ['.git*']];
$config['keywords'][] = 'shim';
$config['readme'] = 'README.md';
$config['replace'][$package->getName()] = 'self.version';
$config['bin'] = [$package->getBinary()];
$config['autoload'] = ['exclude-from-classmap' => $config['bin']];
unset($config['autoload-dev'], $config['require-dev']);
$config = array_filter($config);
$json = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
file_put_contents(Path::join($this->path, 'composer.json'), $json);
}
/**
* @param Package $package
*
* @return string
*/
private function description(Package $package): string
{
return "shim repository for {$package->getName()}";
}
}