Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8ce5892
feat: add get-grpc-tools command
roxblnfk Oct 28, 2026
a61f8b5
build(deps): require DLoad 1.19.1 to select protoc on macOS and Linux…
roxblnfk Oct 29, 2026
6d254f1
refactor: share the existing-binary check between the install commands
roxblnfk Oct 29, 2026
f49913b
fix: keep the default versions for an empty --buf or --plugin
roxblnfk Oct 29, 2026
a354cf8
fix: prefer ext-protobuf and find the project up from the working dir…
roxblnfk Oct 29, 2026
fe63d0d
test: serve the real buf release assets and cover macOS
roxblnfk Oct 29, 2026
0fe574d
fix: ignore the running installation when the project has installed.php
roxblnfk Oct 29, 2026
ec2b780
fix: tell which tools get-grpc-tools kept instead of reporting them r…
roxblnfk Oct 29, 2026
4017995
docs: state where get-grpc-tools installs and which commands take --f…
roxblnfk Oct 29, 2026
2f1e9be
fix: let DLoad keep a buf or protoc that already satisfies the version
roxblnfk Oct 29, 2026
593e40f
fix: keep the get alias when get-binary is registered under another name
roxblnfk Oct 29, 2026
1e283ab
feat: warn when ext-protobuf and the project need different protoc ma…
roxblnfk Oct 29, 2026
6dcc87e
fix: match the buf binary on GitHub Enterprise as DLoad's own registr…
roxblnfk Oct 29, 2026
ef07356
feat: print a deprecation warning when download-protoc-binary runs
roxblnfk Oct 29, 2026
a8c3edb
fix: expand a leading environment variable in vendor-dir as Composer …
roxblnfk Oct 30, 2026
1c2fa3e
docs: tell when get-grpc-tools keeps a binary and that protoc comes w…
roxblnfk Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<br />

This package provides the `rr` console command for PHP projects that run on [RoadRunner](https://roadrunner.dev): it downloads the RoadRunner server binary and `protoc-gen-php-grpc` plugin built for your environment and generates a starter `.rr.yaml` configuration.
This package provides the `rr` console command for PHP projects that run on [RoadRunner](https://roadrunner.dev): it downloads the RoadRunner server binary and the gRPC code generation tools (`buf`, `protoc`, `protoc-gen-php-grpc`) built for your environment and generates a starter `.rr.yaml` configuration.

> [!NOTE]
> Version 3.x of `roadrunner/cli` targets RoadRunner v3: `rr get` downloads RoadRunner 3 by default. For RoadRunner 2025.x, use [`spiral/roadrunner-cli`](https://packagist.org/packages/spiral/roadrunner-cli) 2.x.
Expand Down Expand Up @@ -54,6 +54,25 @@ Then start the server:

See the [RoadRunner documentation](https://docs.roadrunner.dev) for the configuration reference.

### gRPC tools

Generating gRPC code with `buf generate` takes three binaries: [`buf`](https://buf.build), `protoc` (buf runs its built-in PHP generator) and `protoc-gen-php-grpc`. Download all of them into the current directory (`--location` picks another one):

```bash
vendor/bin/rr get-grpc-tools
```

```
google/protobuf v5.36.2 -> protoc ^36.0

Versions:
- buf: ^1
- protoc: ^36.0
- protoc-gen-php-grpc: 3.*
```

The protoc version follows the protobuf runtime of the project, since the generated code is only guaranteed to work with the runtime of the same protoc major: the PHP runtime minor is the protoc major (`google/protobuf` 4.33.x takes protoc 33.x, 5.36.x takes 36.x; up to 3.20 both shared one version). The runtime is `ext-protobuf` when it is loaded in the PHP running `rr`, or else the `google/protobuf` package installed in the project (the nearest directory up from the working one with a `composer.json`, read from its `vendor/composer/installed.json`; a project that is not installed yet counts as having none); when the two need different protoc majors, a warning names both, as the project may run on a PHP without the extension; without either, or when its version does not tell the protoc major (e.g. a branch), a protoc already in the location stays whatever its version, or else the latest one is installed. `--protoc`, `--buf` and `--plugin` (for `protoc-gen-php-grpc`) take a version constraint each, e.g. `--protoc=^33.0`. A `buf` or `protoc` already in the location stays only when it runs and the version it reports with `--version` satisfies the constraint; otherwise it is replaced. `protoc-gen-php-grpc` is downloaded on every run. Only the `protoc` executable is installed, without the `include/` directory of its release: `buf` supplies the well-known types (`google/protobuf/timestamp.proto` and the like) itself, while a direct `protoc` call that imports them needs `-I` pointing to them.

## Commands

- `get-binary` (or `get`) - allows to install the latest version of the RoadRunner compatible with
Expand All @@ -70,19 +89,24 @@ See the [RoadRunner documentation](https://docs.roadrunner.dev) for the configur
Available presets: `web` (contains plugins `http`, `jobs`).
Use `--no-config` to skip the configuration file.
- `make-config` - creates the `.rr.yaml` configuration file without downloading the binary. Accepts the same `plugin` and `preset` options.
- `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...).
- `get-grpc-tools` - installs `buf`, `protoc` and `protoc-gen-php-grpc`, the binaries `buf generate` needs to
generate gRPC code, see [gRPC tools](#grpc-tools). The versions default to buf 1.x, the protoc major the project's
protobuf runtime supports and `protoc-gen-php-grpc` of the RoadRunner line `get-binary` installs; `--buf`, `--protoc`
and `--plugin` override them. Without asking, it keeps a binary already in the location only when its version
satisfies the constraint, see [gRPC tools](#grpc-tools).
- `download-protoc-binary` - **deprecated**, use `get-grpc-tools`. Installs only the `protoc-gen-php-grpc` plugin,
not protoc itself.
- `versions` - displays a list of available RoadRunner binary versions.

### Common options

The binary commands accept `--filter` (`-f`, version constraint; for `get-binary` and `download-protoc-binary` `*` means `>=3.0 <2000` and any other `||` alternative without an upper bound gets `<2000`, so the calendar releases have to be named explicitly, e.g. `2025.*` or `>=2025`), `--stability` (`-s`), `--os` (`-o`), `--arch` (`-a`) and `--location` (`-l`, target directory) options. `get-binary` and `download-protoc-binary` download the binaries with [DLoad](https://github.com/php-internal/dload); a project's `dload.xml` does not affect them.
The binary commands accept `--stability` (`-s`), `--os` (`-o`) and `--arch` (`-a`) options. `get-binary`, `download-protoc-binary` and `versions` take the version constraint as `--filter` (`-f`), `get-grpc-tools` as `--buf`, `--protoc` and `--plugin`. For `get-binary` and `download-protoc-binary` `*` means `>=3.0 <2000` and any other `||` alternative without an upper bound gets `<2000`, so the calendar releases have to be named explicitly, e.g. `2025.*` or `>=2025`; `get-grpc-tools` applies the same to `--plugin` and takes `--buf`/`--protoc` as they are. `get-binary`, `get-grpc-tools` and `download-protoc-binary` also accept `--location` (`-l`, target directory, the current one by default) and download the binaries with [DLoad](https://github.com/php-internal/dload); a project's `dload.xml` does not affect them.

On a musl host such as Alpine they install the musl build of Linux on their own, even with `--os=linux`; `--os=unknown-musl` forces the musl build from any host.

DLoad comes with its own `vendor/bin/dload` and keeps a version registry in the user cache: `~/.cache/dload`, `$XDG_CACHE_HOME/dload` when that is set, `%LOCALAPPDATA%\dload\cache` on Windows. `DLOAD_CACHE_DIR` moves it, `DLOAD_CACHE_TTL=0` turns it off.

Releases are fetched from the GitHub API at `RR_GITHUB_API_URL`, `https://api.github.com` by default. For `get-binary` and `download-protoc-binary` it is either `https://api.github.com` or a GitHub Enterprise Server API URL ending with `/api/v3` (e.g. `https://ghe.example.com/api/v3`); `versions` also accepts a mirror.
Releases are fetched from the GitHub API at `RR_GITHUB_API_URL`, `https://api.github.com` by default. For `get-binary`, `get-grpc-tools` and `download-protoc-binary` it is either `https://api.github.com` or a GitHub Enterprise Server API URL ending with `/api/v3` (e.g. `https://ghe.example.com/api/v3`), which then has to serve `bufbuild/buf` and `protocolbuffers/protobuf` for `get-grpc-tools` too; `versions` also accepts a mirror.

All commands pick the API token the same way, to avoid the rate limits:

Expand Down
3 changes: 3 additions & 0 deletions bin/rr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare(strict_types=1);

use Spiral\RoadRunner\Console\DownloadProtocBinaryCommand;
use Spiral\RoadRunner\Console\GetBinaryCommand;
use Spiral\RoadRunner\Console\GetGrpcToolsCommand;
use Spiral\RoadRunner\Console\MakeConfigCommand;
use Spiral\RoadRunner\Console\VersionsCommand;
use Spiral\RoadRunner\Version;
Expand Down Expand Up @@ -72,12 +73,14 @@ if (\method_exists($app, 'addCommand')) {
$app->addCommand(new GetBinaryCommand());
$app->addCommand(new VersionsCommand());
$app->addCommand(new DownloadProtocBinaryCommand());
$app->addCommand(new GetGrpcToolsCommand());
$app->addCommand(new MakeConfigCommand());
} else {
// For Symfony 7.3 and lower
$app->add(new GetBinaryCommand());
$app->add(new VersionsCommand());
$app->add(new DownloadProtocBinaryCommand());
$app->add(new GetGrpcToolsCommand());
$app->add(new MakeConfigCommand());
}

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"php": ">=8.2",
"composer-runtime-api": "^2.0",
"composer/semver": "^3.4",
"internal/dload": "^1.19.0",
"internal/dload": "^1.19.1",
"roadrunner/worker": "^3.8",
"symfony/console": "^6.4 || ^7.0 || ^8.0",
"symfony/http-client": "^6.4.17 || ^7.2 || ^8.0",
Expand Down
22 changes: 4 additions & 18 deletions src/DownloadProtocBinaryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Spiral\RoadRunner\Console\Environment\OperatingSystem;
use Spiral\RoadRunner\Console\Internal\Downloader\DLoadDownloader;
use Spiral\RoadRunner\Console\Internal\Installation\ExistingBinary;
use Spiral\RoadRunner\Console\Internal\Option\ArchitectureOption;
use Spiral\RoadRunner\Console\Internal\Option\InstallationLocationOption;
use Spiral\RoadRunner\Console\Internal\Option\OperatingSystemOption;
Expand All @@ -14,7 +15,6 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
Expand All @@ -34,7 +34,7 @@
public function __construct(?string $name = null, ?DLoadDownloader $downloader = null)
{
$this->downloader = $downloader ?? new DLoadDownloader();
parent::__construct($name ?? 'download-protoc-binary');

Check warning on line 37 in src/DownloadProtocBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "Coalesce": @@ @@ public function __construct(?string $name = null, ?DLoadDownloader $downloader = null) { $this->downloader = $downloader ?? new DLoadDownloader(); - parent::__construct($name ?? 'download-protoc-binary'); + parent::__construct('download-protoc-binary' ?? $name); $this->os = new OperatingSystemOption($this); $this->arch = new ArchitectureOption($this);

$this->os = new OperatingSystemOption($this);
$this->arch = new ArchitectureOption($this);
Expand All @@ -46,28 +46,29 @@
#[\Override]
public function getDescription(): string
{
return 'Install or update protoc-gen-php-grpc binary';
return 'Install or update protoc-gen-php-grpc binary (deprecated, use get-grpc-tools)';
}

#[\Override]
public function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->warning('download-protoc-binary is deprecated: use get-grpc-tools, which also installs protoc and buf.');

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

$output->writeln('');

Check warning on line 61 in src/DownloadProtocBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $target = $this->location->get($input, $io); $os = $this->os->get($input, $io); - $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(' Environment:');

Check warning on line 62 in src/DownloadProtocBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $os = $this->os->get($input, $io); $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>', $os));
$output->writeln(\sprintf(' - Version: <info>%s</info>', $this->version->get($input, $io)));
$output->writeln(\sprintf(' - Stability: <info>%s</info>', $this->stability->get($input, $io)));

Check warning on line 64 in src/DownloadProtocBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $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>', $os)); $output->writeln(\sprintf(' - Architecture: <info>%s</info>', $this->arch->get($input, $io))); $output->writeln('');
$output->writeln(\sprintf(' - Operating System: <info>%s</info>', $os));
$output->writeln(\sprintf(' - Architecture: <info>%s</info>', $this->arch->get($input, $io)));
$output->writeln('');

Check warning on line 67 in src/DownloadProtocBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $output->writeln(\sprintf(' - Stability: <info>%s</info>', $this->stability->get($input, $io))); $output->writeln(\sprintf(' - Operating System: <info>%s</info>', $os)); $output->writeln(\sprintf(' - Architecture: <info>%s</info>', $this->arch->get($input, $io))); - $output->writeln(''); + $binary = $target . ($os === OperatingSystem::OS_WINDOWS ? '/protoc-gen-php-grpc.exe' : '/protoc-gen-php-grpc');

$binary = $target . ($os === OperatingSystem::OS_WINDOWS ? '/protoc-gen-php-grpc.exe' : '/protoc-gen-php-grpc');

if (!$this->checkExisting($binary, $io)) {
if (!ExistingBinary::mayInstall($binary, 'protoc-gen-php-grpc', $io)) {
$io->warning('protoc-gen-php-grpc has not been installed');

return 1;
Expand All @@ -84,19 +85,4 @@
output: $output,
);
}

private function checkExisting(string $binary, StyleInterface $io): bool
{
if (\is_file($binary)) {
$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;
}
}
20 changes: 4 additions & 16 deletions src/GetBinaryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Spiral\RoadRunner\Console\Internal\Configuration\Generator;
use Spiral\RoadRunner\Console\Internal\Configuration\Plugins;
use Spiral\RoadRunner\Console\Internal\Downloader\DLoadDownloader;
use Spiral\RoadRunner\Console\Internal\Installation\ExistingBinary;
use Spiral\RoadRunner\Console\Internal\Option\ArchitectureOption;
use Spiral\RoadRunner\Console\Internal\Option\InstallationLocationOption;
use Spiral\RoadRunner\Console\Internal\Option\OperatingSystemOption;
Expand Down Expand Up @@ -43,6 +44,8 @@
{
$this->downloader = $downloader ?? new DLoadDownloader();
parent::__construct($name ?? 'get-binary');
// Not left to Symfony's abbreviation lookup: `get` also abbreviates get-grpc-tools
$this->getName() === 'get' or $this->setAliases(['get']);

$this->os = new OperatingSystemOption($this);
$this->arch = new ArchitectureOption($this);
Expand All @@ -69,7 +72,7 @@
$target = $this->location->get($input, $io);
$os = $this->os->get($input, $io);

$output->writeln('');

Check warning on line 75 in src/GetBinaryCommand.php

View workflow job for this annotation

GitHub Actions / Infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $target = $this->location->get($input, $io); $os = $this->os->get($input, $io); - $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(' 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)));
Expand All @@ -80,7 +83,7 @@
$binary = $target . ($os === OperatingSystem::OS_WINDOWS ? '/rr.exe' : '/rr');
$installed = false;

if ($this->checkExisting($binary, $io)) {
if (ExistingBinary::mayInstall($binary, 'RoadRunner', $io)) {
$code = $this->downloader->download(
software: 'rr',
constraint: $this->version->get($input, $io),
Expand Down Expand Up @@ -180,19 +183,4 @@

\file_put_contents($to, $config);
}

private function checkExisting(string $binary, StyleInterface $io): bool
{
if (\is_file($binary)) {
$io->warning('RoadRunner binary file already exists!');

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

return false;
}
}

return true;
}
}
Loading
Loading