Skip to content

Commit

Permalink
Merge pull request #34 from msmakouz/feature/get-binary-command
Browse files Browse the repository at this point in the history
Adding get-protoc-binary console command
  • Loading branch information
butschster authored Jul 26, 2022
2 parents 6560595 + cde8320 commit 36878c8
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ This repository contains commands to help you work with the RoadRunner, such as:
Using the `preset` option can create an example configuration file with popular plugins for different typical tasks.
For example, with web preset: `get-binary --preset web`.
Available presets: `web` (contains plugins `http`, `jobs`).
- `download-protoc-binary` - allows to install the latest version of the `protoc-gen-php-grpc` file compatible with
your environment (operating system, processor architecture, runtime, etc...).
- `versions` - displays a list of available RoadRunner binary versions.

Testing:
Expand Down
2 changes: 2 additions & 0 deletions bin/rr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

declare(strict_types=1);

use Spiral\RoadRunner\Console\DownloadProtocBinaryCommand;
use Spiral\RoadRunner\Console\GetBinaryCommand;
use Spiral\RoadRunner\Console\VersionsCommand;
use Spiral\RoadRunner\Version;
Expand Down Expand Up @@ -68,5 +69,6 @@ $app = new Symfony\Component\Console\Application('RoadRunner CLI', Version::curr

$app->add(new GetBinaryCommand());
$app->add(new VersionsCommand());
$app->add(new DownloadProtocBinaryCommand());

$app->run();
233 changes: 233 additions & 0 deletions src/DownloadProtocBinaryCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\Console;

use Spiral\RoadRunner\Console\Archive\ArchiveInterface;
use Spiral\RoadRunner\Console\Archive\Factory;
use Spiral\RoadRunner\Console\Command\ArchitectureOption;
use Spiral\RoadRunner\Console\Command\InstallationLocationOption;
use Spiral\RoadRunner\Console\Command\OperatingSystemOption;
use Spiral\RoadRunner\Console\Command\StabilityOption;
use Spiral\RoadRunner\Console\Command\VersionFilterOption;
use Spiral\RoadRunner\Console\Repository\AssetInterface;
use Spiral\RoadRunner\Console\Repository\ReleaseInterface;
use Spiral\RoadRunner\Console\Repository\ReleasesCollection;
use Spiral\RoadRunner\Console\Repository\RepositoryInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface;

/**
* protoc-gen-php-grpc file download command.
*/
final class DownloadProtocBinaryCommand extends Command
{
private const ERROR_ENVIRONMENT = 'Could not find any available protoc-gen-php-grpc binary version which meets criterion (--%s=%s --%s=%s --%s=%s). Available: %s';

private OperatingSystemOption $os;
private ArchitectureOption $arch;
private VersionFilterOption $version;
private StabilityOption $stability;
private InstallationLocationOption $location;

public function __construct(string $name = null)
{
parent::__construct($name ?? 'download-protoc-binary');

$this->os = new OperatingSystemOption($this);
$this->arch = new ArchitectureOption($this);
$this->version = new VersionFilterOption($this);
$this->location = new InstallationLocationOption($this);
$this->stability = new StabilityOption($this);
}

public function getDescription(): string
{
return 'Install or update protoc-gen-php-grpc binary';
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$io = $this->io($input, $output);

$target = $this->location->get($input, $io);
$repository = $this->getRepository();

$output->writeln('');
$output->writeln(' Environment:');
$output->writeln(\sprintf(' - Version: <info>%s</info>', $this->version->get($input, $io)));
$output->writeln(\sprintf(' - Stability: <info>%s</info>', $this->stability->get($input, $io)));
$output->writeln(\sprintf(' - Operating System: <info>%s</info>', $this->os->get($input, $io)));
$output->writeln(\sprintf(' - Architecture: <info>%s</info>', $this->arch->get($input, $io)));
$output->writeln('');

// List of all available releases
$releases = $this->version->find($input, $io, $repository);

/**
* @var AssetInterface $asset
* @var ReleaseInterface $release
*/
[$asset, $release] = $this->findAsset($repository, $releases, $input, $io);

// Installation
$output->writeln(
\sprintf(" - <info>%s</info>", $release->getRepositoryName()) .
\sprintf(' (<comment>%s</comment>):', $release->getVersion()) .
' Downloading...'
);

if ($output->isVerbose()) {
$output->writeln(\sprintf(" -- <info>%s</info>", $asset->getName()));
}

// Install rr binary
$file = $this->installBinary($target, $release, $asset, $io, $output);

// Success
if ($file === null) {
$io->warning('protoc-gen-php-grpc has not been installed');

return 1;
}

return 0;
}

private function installBinary(
string $target,
ReleaseInterface $release,
AssetInterface $asset,
StyleInterface $io,
OutputInterface $out
): ?\SplFileInfo {
$extractor = $this->assetToArchive($asset, $out)
->extract([
'protoc-gen-php-grpc.exe' => $target . '/protoc-gen-php-grpc.exe',
'protoc-gen-php-grpc' => $target . '/protoc-gen-php-grpc',
]);

$file = null;
while ($extractor->valid()) {
$file = $extractor->current();

if (!$this->checkExisting($file, $io)) {
$extractor->send(false);
continue;
}

// Success
$path = $file->getRealPath() ?: $file->getPathname();
$message = 'protoc-gen-php-grpc (<comment>%s</comment>) has been installed into <info>%s</info>';
$message = \sprintf($message, $release->getVersion(), $path);
$out->writeln($message);

$extractor->next();

if (!$file->isExecutable()) {
@chmod($file->getRealPath(), 0755);
}
}

return $file;
}

private function checkExisting(\SplFileInfo $bin, StyleInterface $io): bool
{
if (\is_file($bin->getPathname())) {
$io->warning('protoc-gen-php-grpc binary file already exists!');

if (!$io->confirm('Do you want overwrite it?', false)) {
$io->note('Skipping protoc-gen-php-grpc installation...');

return false;
}
}

return true;
}

private function findAsset(
RepositoryInterface $repo,
ReleasesCollection $releases,
InputInterface $in,
StyleInterface $io
): array {
$osOption = $this->os->get($in, $io);
$archOption = $this->arch->get($in, $io);
$stabilityOption = $this->stability->get($in, $io);

/** @var ReleaseInterface[] $filtered */
$filtered = $releases
->minimumStability($stabilityOption)
->withAssets();

foreach ($filtered as $release) {
$asset = $release->getAssets()
->filter(static fn (AssetInterface $asset): bool =>
0 === \strncmp($asset->getName(), 'protoc-gen-php-grpc', \strlen('protoc-gen-php-grpc'))
)
->whereArchitecture($archOption)
->whereOperatingSystem($osOption)
->first();

if ($asset === null) {
$io->warning(
\vsprintf('%s %s does not contain available assembly (further search in progress)', [
$repo->getName(),
$release->getVersion(),
])
);

continue;
}

return [$asset, $release];
}

$message = \vsprintf(self::ERROR_ENVIRONMENT, [
$this->os->getName(),
$osOption,
$this->arch->getName(),
$archOption,
$this->stability->getName(),
$stabilityOption,
$this->version->choices($releases),
]);

throw new \UnexpectedValueException($message);
}

private function assetToArchive(AssetInterface $asset, OutputInterface $out, string $temp = null): ArchiveInterface
{
$factory = new Factory();

$progress = new ProgressBar($out);
$progress->setFormat(' [%bar%] %percent:3s%% (%size%Kb/%total%Kb)');
$progress->setMessage('0.00', 'size');
$progress->setMessage('?.??', 'total');
$progress->display();

try {
return $factory->fromAsset($asset, function (int $size, int $total) use ($progress) {
if ($progress->getMaxSteps() !== $total) {
$progress->setMaxSteps($total);
}

if ($progress->getStartTime() === 0) {
$progress->start();
}

$progress->setMessage(\number_format($size / 1000, 2), 'size');
$progress->setMessage(\number_format($total / 1000, 2), 'total');

$progress->setProgress($size);
}, $temp);
} finally {
$progress->clear();
}
}
}

0 comments on commit 36878c8

Please sign in to comment.