├── LICENSE
├── README.md
├── bin
└── rr
├── composer.json
└── src
├── Archive
├── Archive.php
├── ArchiveInterface.php
├── Factory.php
├── FactoryInterface.php
├── PharArchive.php
├── PharAwareArchive.php
├── TarPharArchive.php
└── ZipPharArchive.php
├── Command.php
├── Command
├── ArchitectureOption.php
├── InstallationLocationOption.php
├── OperatingSystemOption.php
├── Option.php
├── OptionInterface.php
├── StabilityOption.php
└── VersionFilterOption.php
├── Configuration
├── Generator.php
├── Plugins.php
├── Presets.php
└── Section
│ ├── AbstractSection.php
│ ├── Amqp.php
│ ├── Beanstalk.php
│ ├── Boltdb.php
│ ├── Broadcast.php
│ ├── Endure.php
│ ├── Fileserver.php
│ ├── Grpc.php
│ ├── Http.php
│ ├── Jobs.php
│ ├── Kv.php
│ ├── Logs.php
│ ├── Metrics.php
│ ├── Nats.php
│ ├── Otel.php
│ ├── Redis.php
│ ├── Reload.php
│ ├── Rpc.php
│ ├── SectionInterface.php
│ ├── Server.php
│ ├── Service.php
│ ├── Sqs.php
│ ├── Status.php
│ ├── Tcp.php
│ ├── Temporal.php
│ ├── Version.php
│ └── Websockets.php
├── DownloadProtocBinaryCommand.php
├── Environment
├── Architecture.php
├── Architecture
│ └── Factory.php
├── Enum.php
├── Environment.php
├── OperatingSystem.php
├── OperatingSystem
│ └── Factory.php
└── Stability.php
├── GetBinaryCommand.php
├── MakeConfigCommand.php
├── Repository
├── Asset.php
├── AssetInterface.php
├── AssetsCollection.php
├── Collection.php
├── GitHub
│ ├── GitHubAsset.php
│ ├── GitHubRelease.php
│ └── GitHubRepository.php
├── Release.php
├── ReleaseInterface.php
├── ReleasesCollection.php
├── RepositoriesCollection.php
├── RepositoryInterface.php
└── Version1
│ └── StaticRepository.php
└── VersionsCommand.php
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Spiral Scout
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager.
19 | It supports running as a service with the ability to extend its functionality on a per-project basis.
20 |
21 | RoadRunner includes PSR-7/PSR-17 compatible HTTP and HTTP/2 server and can be used to replace classic Nginx+FPM setup with much greater performance and flexibility.
22 |
23 |
24 | Official Website |
25 | Documentation
26 |
27 |
28 | ## RoadRunner CLI
29 |
30 | This repository contains commands to help you work with the RoadRunner, such as:
31 |
32 | - `get-binary` (or `get`) - allows to install the latest version of the RoadRunner compatible with
33 | your environment (operating system, processor architecture, runtime, etc...).
34 | Also, this command creates an example `.rr.yaml` configuration file. If don't use the command without additional options
35 | `plugin` and `preset`, an example with a complete configuration file will be created.
36 | Using the `plugin` option (shortcut `p`) can create an example configuration file with only plugins needed.
37 | For example, with http plugin only: `get-binary -p http`, http and jobs: `get-binary -p http -p jobs`.
38 | Available plugins: `amqp`, `beanstalk`, `boltdb`, `broadcast`, `endure`, `fileserver`, `grpc`, `http`, `jobs`, `kv`,
39 | `logs`, `metrics`, `nats`, `redis`, `reload`, `rpc`, `server`, `service`, `sqs`, `status`, `tcp`, `temporal`, `websockets`.
40 | Using the `preset` option can create an example configuration file with popular plugins for different typical tasks.
41 | For example, with web preset: `get-binary --preset web`.
42 | Available presets: `web` (contains plugins `http`, `jobs`).
43 | - `download-protoc-binary` - allows to install the latest version of the `protoc-gen-php-grpc` file compatible with
44 | your environment (operating system, processor architecture, runtime, etc...).
45 | - `versions` - displays a list of available RoadRunner binary versions.
46 |
47 | Testing:
48 | --------
49 |
50 | This codebase is automatically tested via host repository - [roadrunner-server/roadrunner](https://github.com/roadrunner-server/roadrunner).
51 |
52 | License:
53 | --------
54 |
55 | The MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information.
56 | Maintained by [Spiral Scout](https://spiralscout.com).
57 |
--------------------------------------------------------------------------------
/bin/rr:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new GetBinaryCommand());
72 | $app->add(new VersionsCommand());
73 | $app->add(new DownloadProtocBinaryCommand());
74 | $app->add(new MakeConfigCommand());
75 |
76 | $app->run();
77 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spiral/roadrunner-cli",
3 | "type": "library",
4 | "description": "RoadRunner: Command Line Interface",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Anton Titov (wolfy-j)",
9 | "email": "wolfy-j@spiralscout.com"
10 | },
11 | {
12 | "name": "RoadRunner Community",
13 | "homepage": "https://github.com/spiral/roadrunner/graphs/contributors"
14 | }
15 | ],
16 | "bin": [
17 | "bin/rr"
18 | ],
19 | "homepage": "https://roadrunner.dev",
20 | "support": {
21 | "docs": "https://docs.roadrunner.dev",
22 | "issues": "https://github.com/roadrunner-server/roadrunner/issues",
23 | "chat": "https://discord.gg/V6EK4he"
24 | },
25 | "funding": [
26 | {
27 | "type": "github",
28 | "url": "https://github.com/sponsors/roadrunner-server"
29 | }
30 | ],
31 | "require": {
32 | "php": ">=8.1",
33 | "ext-json": "*",
34 | "composer/semver": "^3.4",
35 | "spiral/roadrunner-worker": "^2 || ^3",
36 | "spiral/tokenizer": "^2.13 || ^3.15",
37 | "symfony/console": "^5.3 || ^6.0 || ^7.0",
38 | "symfony/http-client": "^4.4.51 || ^5.4.49 || ^6.4.17 || ^7.2",
39 | "symfony/yaml": "^5.4.49 || ^6.4.17 || ^7.2"
40 | },
41 | "require-dev": {
42 | "jetbrains/phpstorm-attributes": "^1.2",
43 | "spiral/code-style": "^2.2.2",
44 | "spiral/dumper": "^3.3",
45 | "vimeo/psalm": "^6.0"
46 | },
47 | "autoload": {
48 | "psr-4": {
49 | "Spiral\\RoadRunner\\Console\\": "src"
50 | }
51 | },
52 | "scripts": {
53 | "cs:diff": "php-cs-fixer fix --dry-run -v --diff --show-progress dots",
54 | "cs:fix": "php-cs-fixer fix -v",
55 | "psalm": "psalm",
56 | "psalm:baseline": "psalm --set-baseline=psalm-baseline.xml"
57 | },
58 | "config": {
59 | "sort-packages": true
60 | },
61 | "minimum-stability": "dev",
62 | "prefer-stable": true
63 | }
64 |
--------------------------------------------------------------------------------
/src/Archive/Archive.php:
--------------------------------------------------------------------------------
1 | assertArchiveValid($archive);
19 | }
20 |
21 | private function assertArchiveValid(\SplFileInfo $archive): void
22 | {
23 | if (! $archive->isFile()) {
24 | throw new \InvalidArgumentException(
25 | \sprintf('Archive "%s" is not a file', $archive->getFilename()),
26 | );
27 | }
28 |
29 | if (! $archive->isReadable()) {
30 | throw new \InvalidArgumentException(
31 | \sprintf('Archive file "%s" is not readable', $archive->getFilename()),
32 | );
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Archive/ArchiveInterface.php:
--------------------------------------------------------------------------------
1 | $mappings
18 | * @return \Generator
19 | */
20 | public function extract(iterable $mappings): \Generator;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Archive/Factory.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | private array $matchers = [];
25 |
26 | /**
27 | * FactoryTrait constructor.
28 | */
29 | public function __construct()
30 | {
31 | $this->bootDefaultMatchers();
32 | }
33 |
34 | public function extend(\Closure $matcher): self
35 | {
36 | \array_unshift($this->matchers, $matcher);
37 |
38 | return $this;
39 | }
40 |
41 | public function create(\SplFileInfo $file): ArchiveInterface
42 | {
43 | $errors = [];
44 |
45 | foreach ($this->matchers as $matcher) {
46 | try {
47 | if ($archive = $matcher($file)) {
48 | return $archive;
49 | }
50 | } catch (\Throwable $e) {
51 | $errors[] = ' - ' . $e->getMessage();
52 | continue;
53 | }
54 | }
55 |
56 | $error = \sprintf('Can not open the archive "%s":%s', $file->getFilename(), \PHP_EOL) .
57 | \implode(\PHP_EOL, $errors)
58 | ;
59 |
60 | throw new \InvalidArgumentException($error);
61 | }
62 |
63 | public function fromAsset(AssetInterface $asset, ?\Closure $progress = null, ?string $temp = null): ArchiveInterface
64 | {
65 | $temp = $this->getTempDirectory($temp) . '/' . $asset->getName();
66 |
67 | $file = new \SplFileObject($temp, 'wb+');
68 |
69 | try {
70 | foreach ($asset->download($progress) as $chunk) {
71 | $file->fwrite($chunk);
72 | }
73 | } catch (\Throwable $e) {
74 | @\unlink($temp);
75 |
76 | throw $e;
77 | }
78 |
79 | return $this->create($file);
80 | }
81 |
82 | private function bootDefaultMatchers(): void
83 | {
84 | $this->extend($this->matcher(
85 | 'zip',
86 | static fn(\SplFileInfo $info): ArchiveInterface => new ZipPharArchive($info),
87 | ));
88 |
89 | $this->extend($this->matcher(
90 | 'tar.gz',
91 | static fn(\SplFileInfo $info): ArchiveInterface => new TarPharArchive($info),
92 | ));
93 |
94 | $this->extend($this->matcher(
95 | 'phar',
96 | static fn(\SplFileInfo $info): ArchiveInterface => new PharArchive($info),
97 | ));
98 | }
99 |
100 | /**
101 | * @param ArchiveMatcher $then
102 | * @return ArchiveMatcher
103 | */
104 | private function matcher(string $extension, \Closure $then): \Closure
105 | {
106 | return static fn(\SplFileInfo $info): ?ArchiveInterface =>
107 | \str_ends_with(\strtolower($info->getFilename()), '.' . $extension) ? $then($info) : null
108 | ;
109 | }
110 |
111 | private function getTempDirectory(?string $temp): string
112 | {
113 | if ($temp) {
114 | if (! \is_dir($temp) || ! \is_writable($temp)) {
115 | throw new \LogicException(\sprintf('Directory "%s" is not writeable', $temp));
116 | }
117 |
118 | return $temp;
119 | }
120 |
121 | return \sys_get_temp_dir();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/Archive/FactoryInterface.php:
--------------------------------------------------------------------------------
1 | getPathname());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Archive/PharAwareArchive.php:
--------------------------------------------------------------------------------
1 | archive = $this->open($archive);
23 | }
24 |
25 | /**
26 | * @param iterable $mappings
27 | * @return \Generator
28 | */
29 | public function extract(iterable $mappings): \Generator
30 | {
31 | $phar = $this->open($this->archive);
32 |
33 | if (! $phar->isReadable()) {
34 | throw new \LogicException(\sprintf('Could not open "%s" for reading', $this->archive->getPathname()));
35 | }
36 |
37 | /** @var \PharFileInfo $file */
38 | foreach (new \RecursiveIteratorIterator($phar) as $file) {
39 | foreach ($mappings as $from => $to) {
40 | if ($file->getFilename() === $from && (yield $file => new \SplFileInfo($to)) !== false) {
41 | \copy($file->getPathname(), $to);
42 | }
43 | }
44 | }
45 | }
46 |
47 | abstract protected function open(\SplFileInfo $file): \PharData;
48 | }
49 |
--------------------------------------------------------------------------------
/src/Archive/TarPharArchive.php:
--------------------------------------------------------------------------------
1 | getPathname());
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Archive/ZipPharArchive.php:
--------------------------------------------------------------------------------
1 | getPathname(), 0, null, $format);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Command.php:
--------------------------------------------------------------------------------
1 | \array_filter([
40 | 'authorization' => $token ? 'token ' . $token : null,
41 | ]),
42 | ]);
43 |
44 | return new RepositoriesCollection([
45 | GitHubRepository::create('roadrunner-server', 'roadrunner', $client),
46 | ]);
47 | }
48 |
49 | protected function io(InputInterface $input, OutputInterface $output): StyleInterface
50 | {
51 | return new SymfonyStyle($input, $output);
52 | }
53 |
54 | protected function confirm(InputInterface $input, OutputInterface $out, string $message): bool
55 | {
56 | $question = new ConfirmationQuestion($message);
57 |
58 | /** @var QuestionHelper $helper */
59 | $helper = $this->getHelper('question');
60 |
61 | return (bool) $helper->ask($input, $out, $question);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Command/ArchitectureOption.php:
--------------------------------------------------------------------------------
1 | warning(\sprintf($message, $this->name, $architecture, $this->choices()));
33 | }
34 |
35 | return $architecture;
36 | }
37 |
38 | protected function getDescription(): string
39 | {
40 | return 'Required processor architecture (one of: ' . $this->choices() . ')';
41 | }
42 |
43 | protected function default(): string
44 | {
45 | return Architecture::createFromGlobals();
46 | }
47 |
48 | private function choices(): string
49 | {
50 | return \implode(', ', Architecture::all());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/InstallationLocationOption.php:
--------------------------------------------------------------------------------
1 | name, $location);
32 |
33 | $io->warning($message);
34 |
35 | throw new \InvalidArgumentException('Installation directory not found or not writable');
36 | }
37 |
38 | return $location;
39 | }
40 |
41 | protected function getDescription(): string
42 | {
43 | return 'Installation directory';
44 | }
45 |
46 | protected function default(): string
47 | {
48 | return \getcwd() ?: '.';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Command/OperatingSystemOption.php:
--------------------------------------------------------------------------------
1 | warning(\sprintf($message, $this->name, $os, $this->choices()));
33 | }
34 |
35 | return $os;
36 | }
37 |
38 | protected function getDescription(): string
39 | {
40 | return 'Required operating system (one of: ' . $this->choices() . ')';
41 | }
42 |
43 | protected function default(): string
44 | {
45 | return OperatingSystem::createFromGlobals();
46 | }
47 |
48 | private function choices(): string
49 | {
50 | return \implode(', ', OperatingSystem::all());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/Option.php:
--------------------------------------------------------------------------------
1 | name = $name;
29 |
30 | $this->register($command, $name, $short ?? $name);
31 | }
32 |
33 | public function getName(): string
34 | {
35 | return $this->name;
36 | }
37 |
38 | public function get(InputInterface $input, StyleInterface $io): string
39 | {
40 | $result = $input->getOption($this->name) ?: $this->default();
41 |
42 | return \is_string($result) ? $result : '';
43 | }
44 |
45 | /**
46 | * @return InputOptionType
47 | */
48 | protected function getMode(): int
49 | {
50 | return InputOption::VALUE_OPTIONAL;
51 | }
52 |
53 | abstract protected function getDescription(): string;
54 |
55 | abstract protected function default(): ?string;
56 |
57 | private function register(Command $command, string $name, string $short): void
58 | {
59 | $command->addOption($name, $short, $this->getMode(), $this->getDescription(), $this->default());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Command/OptionInterface.php:
--------------------------------------------------------------------------------
1 | warning(\sprintf($message, $this->name, $stability, $this->choices()));
40 | }
41 |
42 | return $stability;
43 | }
44 |
45 | protected function getDescription(): string
46 | {
47 | return 'Release minimum stability flag';
48 | }
49 |
50 | protected function default(): string
51 | {
52 | return Stability::STABILITY_STABLE;
53 | }
54 |
55 | private function choices(): string
56 | {
57 | return \implode(', ', Stability::all());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Command/VersionFilterOption.php:
--------------------------------------------------------------------------------
1 | map(static fn(ReleaseInterface $release): string => $release->getVersion())
33 | ->toArray()
34 | ;
35 |
36 | return \implode(', ', \array_unique($versions));
37 | }
38 |
39 | public function find(InputInterface $input, StyleInterface $io, RepositoryInterface $repo): ReleasesCollection
40 | {
41 | $constraint = $this->get($input, $io);
42 |
43 | // All available releases
44 | $available = $repo->getReleases()
45 | ->sortByVersion()
46 | ;
47 |
48 | // With constraints
49 | $filtered = $available->satisfies($constraint);
50 |
51 | $this->validateNotEmpty($filtered, $available, $constraint);
52 |
53 | return $filtered;
54 | }
55 |
56 | protected function getDescription(): string
57 | {
58 | return 'Required version of RoadRunner binaries';
59 | }
60 |
61 | protected function default(): string
62 | {
63 | return RoadRunnerVersion::constraint();
64 | }
65 |
66 | private function validateNotEmpty(ReleasesCollection $filtered, ReleasesCollection $all, string $constraint): void
67 | {
68 | if ($filtered->empty()) {
69 | $header = 'Could not find any available RoadRunner binary version which meets version criterion (--%s=%s)';
70 | $header = \sprintf($header, $this->name, $constraint);
71 |
72 | $footer = 'Available: ' . $this->choices($all);
73 |
74 | throw new \UnexpectedValueException(\implode("\n", [$header, $footer]));
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Configuration/Generator.php:
--------------------------------------------------------------------------------
1 | > */
15 | protected const REQUIRED_SECTIONS = [
16 | Version::class,
17 | Rpc::class,
18 | ];
19 |
20 | /** @var SectionInterface[] */
21 | protected array $sections = [];
22 |
23 | public function generate(Plugins $plugins): string
24 | {
25 | $this->collectSections($plugins->getPlugins());
26 |
27 | return Yaml::dump($this->getContent(), 10);
28 | }
29 |
30 | protected function getContent(): array
31 | {
32 | $content = [];
33 | foreach ($this->sections as $section) {
34 | $content += $section->render();
35 | }
36 |
37 | return $content;
38 | }
39 |
40 | protected function collectSections(array $plugins): void
41 | {
42 | $sections = \array_merge(self::REQUIRED_SECTIONS, $plugins);
43 |
44 | foreach ($sections as $section) {
45 | $this->fromSection(new $section());
46 | }
47 | }
48 |
49 | /**
50 | * @psalm-return non-empty-array
51 | */
52 | protected function fromSection(SectionInterface $section): void
53 | {
54 | if (!isset($this->sections[\get_class($section)])) {
55 | $this->sections[\get_class($section)] = $section;
56 | }
57 |
58 | foreach ($section->getRequired() as $required) {
59 | $this->fromSection(new $required());
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Configuration/Plugins.php:
--------------------------------------------------------------------------------
1 |
22 | *
23 | * Default plugins in a class-string format.
24 | */
25 | private array $defaultPlugins = [
26 | Version::class,
27 | Rpc::class,
28 | Server::class,
29 | Http::class,
30 | Jobs::class,
31 | Kv::class,
32 | Metrics::class,
33 | ];
34 |
35 | /**
36 | * @var string[]
37 | *
38 | * Requested plugins in a shortname format.
39 | */
40 | private array $requestedPlugins;
41 |
42 | /**
43 | * @psalm-var non-empty-array>
44 | *
45 | * All plugins.
46 | */
47 | private array $available;
48 |
49 | private function __construct(array $plugins)
50 | {
51 | $this->available = $this->getAvailable();
52 | $this->requestedPlugins = $plugins;
53 | }
54 |
55 | public static function fromPlugins(array $plugins): self
56 | {
57 | return new self($plugins);
58 | }
59 |
60 | public static function fromPreset(string $preset): self
61 | {
62 | $plugins = [];
63 | switch ($preset) {
64 | case Presets::WEB_PRESET_NANE:
65 | $plugins = Presets::WEB_PLUGINS;
66 | }
67 |
68 | return new self(\array_map(static function (string $plugin) {
69 | return $plugin::getShortName();
70 | }, $plugins));
71 | }
72 |
73 | public function getPlugins(): array
74 | {
75 | if ($this->requestedPlugins === []) {
76 | return $this->defaultPlugins;
77 | }
78 |
79 | $plugins = [];
80 | foreach ($this->available as $plugin) {
81 | if (\in_array($plugin::getShortName(), $this->requestedPlugins, true)) {
82 | $plugins[] = $plugin;
83 | }
84 | }
85 |
86 | return $plugins;
87 | }
88 |
89 | private function getAvailable(): array
90 | {
91 | $finder = new Finder();
92 | $finder->files()->in(__DIR__ . '/Section')->name('*.php');
93 |
94 | $locator = new ClassLocator($finder);
95 |
96 | /** @var SectionInterface[] $available */
97 | $available = [];
98 | foreach ($locator->getClasses() as $class) {
99 | if ($this->isPlugin($class)) {
100 | $available[] = $class->getName();
101 | }
102 | }
103 |
104 | return $available;
105 | }
106 |
107 | private function isPlugin(\ReflectionClass $class): bool
108 | {
109 | return $class->implementsInterface(SectionInterface::class) && !$class->isAbstract() && !$class->isInterface();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Configuration/Presets.php:
--------------------------------------------------------------------------------
1 | [
20 | 'addr' => 'amqp://guest:guest@127.0.0.1:5672/',
21 | ],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Beanstalk.php:
--------------------------------------------------------------------------------
1 | [
20 | 'addr' => 'tcp://127.0.0.1:11300',
21 | 'timeout' => '10s',
22 | ],
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Boltdb.php:
--------------------------------------------------------------------------------
1 | [
20 | 'permissions' => 0777,
21 | ],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Broadcast.php:
--------------------------------------------------------------------------------
1 | [
20 | 'default' => [
21 | 'driver' => 'memory',
22 | 'config' => [],
23 | ],
24 | // 'default-redis' => [
25 | // 'driver' => 'redis',
26 | // 'config' => [
27 | // 'addrs' => [
28 | // 'localhost:6379'
29 | // ],
30 | // 'master_name' => '',
31 | // 'username' => '',
32 | // 'password' => '',
33 | // 'db' => 0,
34 | // 'sentinel_password' => '',
35 | // 'route_by_latency' => false,
36 | // 'route_randomly' => false,
37 | // 'dial_timeout' => 0,
38 | // 'max_retries' => 1,
39 | // 'min_retry_backoff' => 0,
40 | // 'max_retry_backoff' => 0,
41 | // 'pool_size' => 0,
42 | // 'min_idle_conns' => 0,
43 | // 'max_conn_age' => 0,
44 | // 'read_timeout' => 0,
45 | // 'write_timeout' => 0,
46 | // 'pool_timeout' => 0,
47 | // 'idle_timeout' => 0,
48 | // 'idle_check_freq' => 0,
49 | // 'read_only' => false
50 | // ]
51 | // ]
52 | ],
53 | ];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Endure.php:
--------------------------------------------------------------------------------
1 | [
20 | 'grace_period' => '30s',
21 | 'print_graph' => false,
22 | 'log_level' => 'error',
23 | ],
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Fileserver.php:
--------------------------------------------------------------------------------
1 | [
20 | 'address' => '127.0.0.1:10101',
21 | 'calculate_etag' => true,
22 | 'weak' => false,
23 | 'stream_request_body' => true,
24 | // 'serve' => [
25 | // [
26 | // 'prefix' => '/foo',
27 | // 'root' => '../../../tests',
28 | // 'compress' => false,
29 | // 'cache_duration' => 10,
30 | // 'max_age' => 10,
31 | // 'bytes_range' => true
32 | // ],
33 | // [
34 | // 'prefix' => '/foo/bar',
35 | // 'root' => '../../../tests',
36 | // 'compress' => false,
37 | // 'cache_duration' => 10,
38 | // 'max_age' => 10,
39 | // 'bytes_range' => true
40 | // ]
41 | // ]
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Grpc.php:
--------------------------------------------------------------------------------
1 | [
20 | 'listen' => 'tcp://127.0.0.1:9001',
21 | 'proto' => [
22 | 'first.proto',
23 | 'second.proto',
24 | ],
25 | // 'tls' => [
26 | // 'key' => '',
27 | // 'cert' => '',
28 | // 'root_ca' => '',
29 | // 'client_auth_type' => 'no_client_certs'
30 | // ],
31 | // 'max_send_msg_size' => 50,
32 | // 'max_recv_msg_size' => 50,
33 | // 'max_connection_idle' => '0s',
34 | // 'max_connection_age' => '0s',
35 | // 'max_connection_age_grace' => '0s8h',
36 | // 'max_concurrent_streams' => 10,
37 | // 'ping_time' => '1s',
38 | // 'timeout' => '200s',
39 | // 'pool' => [
40 | // 'num_workers' => 2,
41 | // 'max_jobs' => 0,
42 | // 'allocate_timeout' => '60s',
43 | // 'destroy_timeout' => 60
44 | // ]
45 | ],
46 | ];
47 | }
48 |
49 | public function getRequired(): array
50 | {
51 | return [
52 | Server::class,
53 | ];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Http.php:
--------------------------------------------------------------------------------
1 | [
20 | 'address' => '0.0.0.0:8080',
21 | 'middleware' => [
22 | 'gzip',
23 | 'static',
24 | ],
25 | 'static' => [
26 | 'dir' => 'public',
27 | 'forbid' => ['.php', '.htaccess'],
28 | ],
29 | 'pool' => [
30 | 'num_workers' => 1,
31 | 'supervisor' => [
32 | 'max_worker_memory' => 100,
33 | ],
34 | ],
35 | ],
36 | ];
37 | }
38 |
39 | public function getRequired(): array
40 | {
41 | return [
42 | Server::class,
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Jobs.php:
--------------------------------------------------------------------------------
1 | [
20 | 'pool' => [
21 | 'num_workers' => 2,
22 | 'max_worker_memory' => 100,
23 | ],
24 | 'consume' => [],
25 | ],
26 | ];
27 | }
28 |
29 | public function getRequired(): array
30 | {
31 | return [
32 | Server::class,
33 | ];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Kv.php:
--------------------------------------------------------------------------------
1 | [
20 | 'local' => [
21 | 'driver' => 'memory',
22 | 'config' => [
23 | 'interval' => 60,
24 | ],
25 | ],
26 | // 'redis' => [
27 | // 'driver' => 'redis',
28 | // 'config' => [
29 | // 'addrs' => [
30 | // 'localhost:6379'
31 | // ]
32 | // ]
33 | // ]
34 | ],
35 | ];
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Logs.php:
--------------------------------------------------------------------------------
1 | [
20 | 'mode' => 'development',
21 | 'level' => 'debug',
22 | // 'encoding' => 'console',
23 | // 'line_ending' => '\n',
24 | // 'output' => 'stderr',
25 | // 'err_output' => 'stderr',
26 | // 'file_logger_options' => [
27 | // 'log_output' => '/tmp/my.log',
28 | // 'max_size' => 100,
29 | // 'max_age' => 1,
30 | // 'max_backups' => 5,
31 | // 'compress' => false
32 | // ],
33 | // 'channels' => [
34 | // 'http' => [
35 | // 'mode' => 'development',
36 | // 'level' => 'panic',
37 | // 'encoding' => 'console',
38 | // 'output' => 'stdout',
39 | // 'err_output' => 'stderr'
40 | // ],
41 | // 'server' => [
42 | // 'mode' => 'production',
43 | // 'level' => 'info',
44 | // 'encoding' => 'json',
45 | // 'output' => 'stdout',
46 | // 'err_output' => 'stdout'
47 | // ],
48 | // 'rpc' => [
49 | // 'mode' => 'raw',
50 | // 'level' => 'debug',
51 | // 'encoding' => 'console',
52 | // 'output' => 'stderr',
53 | // 'err_output' => 'stdout'
54 | // ]
55 | // ]
56 | ],
57 | ];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Metrics.php:
--------------------------------------------------------------------------------
1 | [
20 | 'address' => '127.0.0.1:2112',
21 | ],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Nats.php:
--------------------------------------------------------------------------------
1 | [
20 | 'addr' => 'demo.nats.io',
21 | ],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Otel.php:
--------------------------------------------------------------------------------
1 | [
20 | 'insecure' => false,
21 | 'compress' => false,
22 | 'client' => 'http',
23 | 'exporter' => 'otlp',
24 | 'custom_url' => '',
25 | 'service_name' => 'RoadRunner',
26 | 'service_version' => '1.0.0',
27 | 'endpoint' => '127.0.0.1:4318',
28 | ],
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Redis.php:
--------------------------------------------------------------------------------
1 | [
20 | 'addrs' => [
21 | 'localhost:6379',
22 | ],
23 | // 'master_name' => '',
24 | // 'username' => '',
25 | // 'password' => '',
26 | // 'db' => 0,
27 | // 'sentinel_password' => '',
28 | // 'route_by_latency' => false,
29 | // 'route_randomly' => false,
30 | // 'dial_timeout' => 0,
31 | // 'max_retries' => 1,
32 | // 'min_retry_backoff' => 0,
33 | // 'max_retry_backoff' => 0,
34 | // 'pool_size' => 0,
35 | // 'min_idle_conns' => 0,
36 | // 'max_conn_age' => 0,
37 | // 'read_timeout' => 0,
38 | // 'write_timeout' => 0,
39 | // 'pool_timeout' => 0,
40 | // 'idle_timeout' => 0,
41 | // 'idle_check_freq' => 0,
42 | // 'read_only' => false
43 | ],
44 | ];
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Reload.php:
--------------------------------------------------------------------------------
1 | [
20 | 'interval' => '1s',
21 | 'patterns' => [
22 | '.php',
23 | ],
24 | 'services' => [
25 | 'http' => [
26 | 'dirs' => [
27 | '.',
28 | ],
29 | 'recursive' => true,
30 | 'ignore' => [
31 | 'vendor',
32 | ],
33 | 'patterns' => [
34 | '.php',
35 | '.go',
36 | '.md',
37 | ],
38 | ],
39 | ],
40 | ],
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Rpc.php:
--------------------------------------------------------------------------------
1 | [
20 | 'listen' => 'tcp://127.0.0.1:6001',
21 | ],
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Configuration/Section/SectionInterface.php:
--------------------------------------------------------------------------------
1 | [
20 | 'command' => 'php app.php',
21 | 'relay' => 'pipes',
22 | ],
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Service.php:
--------------------------------------------------------------------------------
1 | [
20 | 'some_service_1' => [
21 | 'command' => 'php tests/plugins/service/test_files/loop.php',
22 | 'env' => [
23 | 'foo' => 'BAR',
24 | 'foo2' => 'BAR2',
25 | ],
26 | 'process_num' => 1,
27 | 'exec_timeout' => 0,
28 | 'remain_after_exit' => true,
29 | 'restart_sec' => 1,
30 | ],
31 | 'some_service_2' => [
32 | 'command' => 'binary',
33 | 'env' => [
34 | 'foo' => 'BAR',
35 | 'foo2' => 'BAR2',
36 | ],
37 | 'process_num' => 1,
38 | 'exec_timeout' => 0,
39 | 'remain_after_exit' => true,
40 | 'restart_sec' => 1,
41 | ],
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Sqs.php:
--------------------------------------------------------------------------------
1 | [
20 | 'key' => 'api-key',
21 | 'secret' => 'api-secret',
22 | 'region' => 'us-west-1',
23 | 'session_token' => 'test',
24 | 'endpoint' => 'http://127.0.0.1:9324',
25 | ],
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Status.php:
--------------------------------------------------------------------------------
1 | [
20 | 'address' => '127.0.0.1:2114',
21 | 'unavailable_status_code' => 503,
22 | ],
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Tcp.php:
--------------------------------------------------------------------------------
1 | [
20 | 'servers' => [
21 | 'server1' => [
22 | 'addr' => '127.0.0.1:7778',
23 | // 'delimiter' => '\r\n',
24 | // 'read_buf_size' => 1
25 | ],
26 | // 'server2' => [
27 | // 'addr' => '127.0.0.1:8811',
28 | // 'read_buf_size' => 10
29 | // ],
30 | // 'server3' => [
31 | // 'addr' => '127.0.0.1:8812',
32 | // 'delimiter' => '\r\n',
33 | // 'read_buf_size' => 1
34 | // ]
35 | ],
36 | 'pool' => [
37 | 'command' => '',
38 | 'num_workers' => 5,
39 | 'max_jobs' => 0,
40 | 'allocate_timeout' => '60s',
41 | 'destroy_timeout' => '60s',
42 | ],
43 | ],
44 | ];
45 | }
46 |
47 | public function getRequired(): array
48 | {
49 | return [
50 | Server::class,
51 | ];
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Temporal.php:
--------------------------------------------------------------------------------
1 | [
20 | 'address' => '127.0.0.1:7233',
21 | // 'cache_size' => 10000,
22 | // 'namespace' => 'default',
23 | // 'codec' => 'proto',
24 | // 'debug_level' => 2,
25 | // 'metrics' => [
26 | // 'address' => '127.0.0.1:9091',
27 | // 'type' => 'summary',
28 | // 'prefix' => 'foobar'
29 | // ],
30 | // 'activities' => [
31 | // 'debug' => false,
32 | // 'command' => 'php my-super-app.php',
33 | // 'num_workers' => 0,
34 | // 'max_jobs' => 64,
35 | // 'allocate_timeout' => '60s',
36 | // 'destroy_timeout' => '60s',
37 | // 'supervisor' => [
38 | // 'watch_tick' => '1s',
39 | // 'ttl' => '0s',
40 | // 'idle_ttl' => '10s',
41 | // 'max_worker_memory' => 128,
42 | // 'exec_ttl' => '60s'
43 | // ]
44 | // ]
45 | ],
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Version.php:
--------------------------------------------------------------------------------
1 | self::CONFIG_VERSION,
21 | ];
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Configuration/Section/Websockets.php:
--------------------------------------------------------------------------------
1 | [
20 | 'broker' => 'default-redis',
21 | 'allowed_origin' => '*',
22 | 'path' => '/ws',
23 | ],
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/DownloadProtocBinaryCommand.php:
--------------------------------------------------------------------------------
1 | os = new OperatingSystemOption($this);
41 | $this->arch = new ArchitectureOption($this);
42 | $this->version = new VersionFilterOption($this);
43 | $this->location = new InstallationLocationOption($this);
44 | $this->stability = new StabilityOption($this);
45 | }
46 |
47 | public function getDescription(): string
48 | {
49 | return 'Install or update protoc-gen-php-grpc binary';
50 | }
51 |
52 | public function execute(InputInterface $input, OutputInterface $output): int
53 | {
54 | $io = $this->io($input, $output);
55 |
56 | $target = $this->location->get($input, $io);
57 | $repository = $this->getRepository();
58 |
59 | $output->writeln('');
60 | $output->writeln(' Environment:');
61 | $output->writeln(\sprintf(' - Version: %s ', $this->version->get($input, $io)));
62 | $output->writeln(\sprintf(' - Stability: %s ', $this->stability->get($input, $io)));
63 | $output->writeln(\sprintf(' - Operating System: %s ', $this->os->get($input, $io)));
64 | $output->writeln(\sprintf(' - Architecture: %s ', $this->arch->get($input, $io)));
65 | $output->writeln('');
66 |
67 | // List of all available releases
68 | $releases = $this->version->find($input, $io, $repository);
69 |
70 | /**
71 | * @var AssetInterface $asset
72 | * @var ReleaseInterface $release
73 | */
74 | [$asset, $release] = $this->findAsset($repository, $releases, $input, $io);
75 |
76 | // Installation
77 | $output->writeln(
78 | \sprintf(" - %s ", $release->getRepositoryName()) .
79 | \sprintf(' (%s ):', $release->getVersion()) .
80 | ' Downloading...',
81 | );
82 |
83 | if ($output->isVerbose()) {
84 | $output->writeln(\sprintf(" -- %s ", $asset->getName()));
85 | }
86 |
87 | // Install rr binary
88 | $file = $this->installBinary($target, $release, $asset, $io, $output);
89 |
90 | // Success
91 | if ($file === null) {
92 | $io->warning('protoc-gen-php-grpc has not been installed');
93 |
94 | return 1;
95 | }
96 |
97 | return 0;
98 | }
99 |
100 | private function installBinary(
101 | string $target,
102 | ReleaseInterface $release,
103 | AssetInterface $asset,
104 | StyleInterface $io,
105 | OutputInterface $out,
106 | ): ?\SplFileInfo {
107 | $extractor = $this->assetToArchive($asset, $out)
108 | ->extract([
109 | 'protoc-gen-php-grpc.exe' => $target . '/protoc-gen-php-grpc.exe',
110 | 'protoc-gen-php-grpc' => $target . '/protoc-gen-php-grpc',
111 | ]);
112 |
113 | $file = null;
114 | while ($extractor->valid()) {
115 | $file = $extractor->current();
116 |
117 | if (!$this->checkExisting($file, $io)) {
118 | $extractor->send(false);
119 | continue;
120 | }
121 |
122 | // Success
123 | $path = $file->getRealPath() ?: $file->getPathname();
124 | $message = 'protoc-gen-php-grpc (%s ) has been installed into %s ';
125 | $message = \sprintf($message, $release->getVersion(), $path);
126 | $out->writeln($message);
127 |
128 | $extractor->next();
129 |
130 | if (!$file->isExecutable()) {
131 | @\chmod($file->getRealPath(), 0755);
132 | }
133 | }
134 |
135 | return $file;
136 | }
137 |
138 | private function checkExisting(\SplFileInfo $bin, StyleInterface $io): bool
139 | {
140 | if (\is_file($bin->getPathname())) {
141 | $io->warning('protoc-gen-php-grpc binary file already exists!');
142 |
143 | if (!$io->confirm('Do you want overwrite it?', false)) {
144 | $io->note('Skipping protoc-gen-php-grpc installation...');
145 |
146 | return false;
147 | }
148 | }
149 |
150 | return true;
151 | }
152 |
153 | private function findAsset(
154 | RepositoryInterface $repo,
155 | ReleasesCollection $releases,
156 | InputInterface $in,
157 | StyleInterface $io,
158 | ): array {
159 | $osOption = $this->os->get($in, $io);
160 | $archOption = $this->arch->get($in, $io);
161 | $stabilityOption = $this->stability->get($in, $io);
162 |
163 | /** @var ReleaseInterface[] $filtered */
164 | $filtered = $releases
165 | ->minimumStability($stabilityOption)
166 | ->withAssets();
167 |
168 | foreach ($filtered as $release) {
169 | $asset = $release->getAssets()
170 | ->filter(
171 | static fn(AssetInterface $asset): bool =>
172 | \str_starts_with($asset->getName(), 'protoc-gen-php-grpc'),
173 | )
174 | ->whereArchitecture($archOption)
175 | ->whereOperatingSystem($osOption)
176 | ->first();
177 |
178 | if ($asset === null) {
179 | $io->warning(
180 | \vsprintf('%s %s does not contain available assembly (further search in progress)', [
181 | $repo->getName(),
182 | $release->getVersion(),
183 | ]),
184 | );
185 |
186 | continue;
187 | }
188 |
189 | return [$asset, $release];
190 | }
191 |
192 | $message = \vsprintf(self::ERROR_ENVIRONMENT, [
193 | $this->os->getName(),
194 | $osOption,
195 | $this->arch->getName(),
196 | $archOption,
197 | $this->stability->getName(),
198 | $stabilityOption,
199 | $this->version->choices($releases),
200 | ]);
201 |
202 | throw new \UnexpectedValueException($message);
203 | }
204 |
205 | private function assetToArchive(AssetInterface $asset, OutputInterface $out, ?string $temp = null): ArchiveInterface
206 | {
207 | $factory = new Factory();
208 |
209 | $progress = new ProgressBar($out);
210 | $progress->setFormat(' [%bar%] %percent:3s%% (%size%Kb/%total%Kb)');
211 | $progress->setMessage('0.00', 'size');
212 | $progress->setMessage('?.??', 'total');
213 | $progress->display();
214 |
215 | try {
216 | return $factory->fromAsset($asset, static function (int $size, int $total) use ($progress): void {
217 | if ($progress->getMaxSteps() !== $total) {
218 | $progress->setMaxSteps($total);
219 | }
220 |
221 | if ($progress->getStartTime() === 0) {
222 | $progress->start();
223 | }
224 |
225 | $progress->setMessage(\number_format($size / 1000, 2), 'size');
226 | $progress->setMessage(\number_format($total / 1000, 2), 'total');
227 |
228 | $progress->setProgress($size);
229 | }, $temp);
230 | } finally {
231 | $progress->clear();
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/src/Environment/Architecture.php:
--------------------------------------------------------------------------------
1 | createFromGlobals();
39 | }
40 |
41 | /**
42 | * @return array
43 | */
44 | public static function all(): array
45 | {
46 | static $values;
47 |
48 | if ($values === null) {
49 | $values = Enum::values(self::class, 'ARCH_');
50 | }
51 |
52 | /** @psalm-var array $values */
53 | return $values;
54 | }
55 |
56 | public static function isValid(string $value): bool
57 | {
58 | return \in_array($value, self::all(), true);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Environment/Architecture/Factory.php:
--------------------------------------------------------------------------------
1 | >
32 | */
33 | private const UNAME_MAPPINGS = [
34 | Architecture::ARCH_X86_64 => [
35 | 'AMD64',
36 | 'amd64',
37 | 'x86',
38 | 'x64',
39 | 'x86_64',
40 | ],
41 | Architecture::ARCH_ARM_64 => [
42 | 'arm64',
43 | 'aarch64',
44 | ],
45 | ];
46 |
47 | /**
48 | * @return ArchitectureType
49 | */
50 | #[ExpectedValues(valuesFromClass: Architecture::class)]
51 | public function createFromGlobals(): string
52 | {
53 | $uname = \php_uname('m');
54 |
55 | foreach (self::UNAME_MAPPINGS as $result => $available) {
56 | if (\in_array($uname, $available, true)) {
57 | return $result;
58 | }
59 | }
60 |
61 | throw new \OutOfRangeException(\sprintf(self::ERROR_UNKNOWN_ARCH, $uname));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Environment/Enum.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | public static function values(string $class, string $prefix): array
25 | {
26 | $result = [];
27 |
28 | try {
29 | $reflection = new \ReflectionClass($class);
30 | } catch (\ReflectionException $e) {
31 | return [];
32 | }
33 |
34 | /** @psalm-var int|string $value */
35 | foreach ($reflection->getConstants() as $name => $value) {
36 | if (\str_starts_with($name, $prefix)) {
37 | $result[$name] = $value;
38 | }
39 | }
40 |
41 | return $result;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Environment/Environment.php:
--------------------------------------------------------------------------------
1 | createFromGlobals($variables);
54 | }
55 |
56 | public static function isValid(string $value): bool
57 | {
58 | return \in_array($value, self::all(), true);
59 | }
60 |
61 | /**
62 | * @return array
63 | */
64 | public static function all(): array
65 | {
66 | static $values;
67 |
68 | if ($values === null) {
69 | $values = Enum::values(self::class, 'OS_');
70 | }
71 |
72 | /** @psalm-var array $values */
73 | return $values;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Environment/OperatingSystem/Factory.php:
--------------------------------------------------------------------------------
1 | 4,
49 | self::STABILITY_RC => 3,
50 | self::STABILITY_BETA => 2,
51 | self::STABILITY_ALPHA => 1,
52 | self::STABILITY_DEV => 0,
53 | ];
54 |
55 | /**
56 | * @param StabilityType $type
57 | * @return positive-int|0
58 | */
59 | public static function toInt(string $type): int
60 | {
61 | return self::WEIGHT[$type] ?? 0;
62 | }
63 |
64 | /**
65 | * @return array
66 | */
67 | public static function all(): array
68 | {
69 | static $values;
70 |
71 | if ($values === null) {
72 | $values = Enum::values(self::class, 'STABILITY_');
73 | }
74 |
75 | /** @psalm-var array $values */
76 | return $values;
77 | }
78 |
79 | public static function isValid(string $value): bool
80 | {
81 | return \in_array($value, self::all(), true);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/GetBinaryCommand.php:
--------------------------------------------------------------------------------
1 | os = new OperatingSystemOption($this);
53 | $this->arch = new ArchitectureOption($this);
54 | $this->version = new VersionFilterOption($this);
55 | $this->location = new InstallationLocationOption($this);
56 | $this->stability = new StabilityOption($this);
57 | }
58 |
59 | public function getDescription(): string
60 | {
61 | return 'Install or update RoadRunner binary';
62 | }
63 |
64 | /**
65 | *
66 | * @throws \Throwable
67 | */
68 | public function execute(InputInterface $input, OutputInterface $output): int
69 | {
70 | $io = $this->io($input, $output);
71 |
72 | $target = $this->location->get($input, $io);
73 | $repository = $this->getRepository();
74 |
75 | $output->writeln('');
76 | $output->writeln(' Environment:');
77 | $output->writeln(\sprintf(' - Version: %s ', $this->version->get($input, $io)));
78 | $output->writeln(\sprintf(' - Stability: %s ', $this->stability->get($input, $io)));
79 | $output->writeln(\sprintf(' - Operating System: %s ', $this->os->get($input, $io)));
80 | $output->writeln(\sprintf(' - Architecture: %s ', $this->arch->get($input, $io)));
81 | $output->writeln('');
82 |
83 |
84 | // List of all available releases
85 | $releases = $this->version->find($input, $io, $repository);
86 |
87 | /**
88 | * @var AssetInterface $asset
89 | * @var ReleaseInterface $release
90 | */
91 | [$asset, $release] = $this->findAsset($repository, $releases, $input, $io);
92 |
93 | // Installation
94 | $output->writeln(
95 | \sprintf(" - %s ", $release->getRepositoryName()) .
96 | \sprintf(' (%s ):', $release->getVersion()) .
97 | ' Downloading...',
98 | );
99 |
100 | if ($output->isVerbose()) {
101 | $output->writeln(\sprintf(" -- %s ", $asset->getName()));
102 | }
103 |
104 | // Install rr binary
105 | $file = $this->installBinary($target, $release, $asset, $io, $output);
106 |
107 | $this->installConfig($target, $input, $io);
108 |
109 | // Success
110 | if ($file === null) {
111 | $io->warning('RoadRunner has not been installed');
112 |
113 | return 1;
114 | }
115 |
116 | $io->success('Your project is now ready in ' . $file->getPath());
117 |
118 | $io->title('Whats Next?');
119 | $io->listing([
120 | // 1)
121 | 'For more detailed documentation, see the ' .
122 | 'https://roadrunner.dev> ',
123 |
124 | // 2)
125 | 'To run the application, use the following command: ' .
126 | '$ ' . $file->getFilename() . ' serve ',
127 | ]);
128 |
129 | return 0;
130 | }
131 |
132 | protected function configure(): void
133 | {
134 | $this->addOption(
135 | 'plugin',
136 | 'p',
137 | InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
138 | 'Generate configuration with selected plugins.',
139 | );
140 |
141 | $this->addOption(
142 | 'preset',
143 | null,
144 | InputOption::VALUE_OPTIONAL,
145 | 'Generate configuration with plugins in a selected preset.',
146 | );
147 |
148 | $this->addOption(
149 | 'no-config',
150 | null,
151 | InputOption::VALUE_NONE,
152 | 'Do not generate configuration at all.',
153 | );
154 | }
155 |
156 | /**
157 | * @throws \Throwable
158 | */
159 | private function installBinary(
160 | string $target,
161 | ReleaseInterface $release,
162 | AssetInterface $asset,
163 | StyleInterface $io,
164 | OutputInterface $out,
165 | ): ?\SplFileInfo {
166 | $extractor = $this->assetToArchive($asset, $out)
167 | ->extract([
168 | 'rr.exe' => $target . '/rr.exe',
169 | 'rr' => $target . '/rr',
170 | ])
171 | ;
172 |
173 | $file = null;
174 | while ($extractor->valid()) {
175 | $file = $extractor->current();
176 |
177 | if (! $this->checkExisting($file, $io)) {
178 | $extractor->send(false);
179 | continue;
180 | }
181 |
182 | // Success
183 | $path = $file->getRealPath() ?: $file->getPathname();
184 | $message = 'RoadRunner (%s ) has been installed into %s ';
185 | $message = \sprintf($message, $release->getVersion(), $path);
186 | $out->writeln($message);
187 |
188 | $extractor->next();
189 |
190 | if (! $file->isExecutable()) {
191 | @\chmod($file->getRealPath(), 0755);
192 | }
193 | }
194 |
195 | return $file;
196 | }
197 |
198 | private function installConfig(string $to, InputInterface $in, StyleInterface $io): bool
199 | {
200 | $to .= '/.rr.yaml';
201 |
202 | if (\is_file($to) || \is_file(\getcwd() . '/.rr.yaml')) {
203 | return false;
204 | }
205 |
206 | if ($in->getOption('no-config') || ! $io->confirm('Do you want create default ".rr.yaml" configuration file?', true)) {
207 | return false;
208 | }
209 |
210 | $generator = new Generator();
211 | $plugins = $in->getOption('preset') ?
212 | Plugins::fromPreset($in->getOption('preset')) :
213 | Plugins::fromPlugins($in->getOption('plugin'));
214 |
215 | try {
216 | $config = $generator->generate($plugins);
217 | } catch (\Throwable $e) {
218 | $io->error($e->getMessage());
219 | }
220 |
221 | \file_put_contents($to, $config);
222 |
223 | return true;
224 | }
225 |
226 | private function checkExisting(\SplFileInfo $bin, StyleInterface $io): bool
227 | {
228 | if (\is_file($bin->getPathname())) {
229 | $io->warning('RoadRunner binary file already exists!');
230 |
231 | if (! $io->confirm('Do you want overwrite it?', false)) {
232 | $io->note('Skipping RoadRunner installation...');
233 |
234 | return false;
235 | }
236 | }
237 |
238 | return true;
239 | }
240 |
241 | /**
242 | * @return array{0: AssetInterface, 1: ReleaseInterface}
243 | */
244 | private function findAsset(
245 | RepositoryInterface $repo,
246 | ReleasesCollection $releases,
247 | InputInterface $in,
248 | StyleInterface $io,
249 | ): array {
250 | $osOption = $this->os->get($in, $io);
251 | $archOption = $this->arch->get($in, $io);
252 | $stabilityOption = $this->stability->get($in, $io);
253 |
254 | /** @var ReleaseInterface[] $filtered */
255 | $filtered = $releases
256 | ->minimumStability($stabilityOption)
257 | ->withAssets()
258 | ;
259 |
260 | foreach ($filtered as $release) {
261 | $asset = $release->getAssets()
262 | ->onlyRoadrunner()
263 | ->exceptDebPackages()
264 | ->whereArchitecture($archOption)
265 | ->whereOperatingSystem($osOption)
266 | ->first()
267 | ;
268 |
269 | if ($asset === null) {
270 | $io->warning(\vsprintf('%s %s does not contain available assembly (further search in progress)', [
271 | $repo->getName(),
272 | $release->getVersion(),
273 | ]));
274 |
275 | continue;
276 | }
277 |
278 | return [$asset, $release];
279 | }
280 |
281 | $message = \vsprintf(self::ERROR_ENVIRONMENT, [
282 | $this->os->getName(),
283 | $osOption,
284 | $this->arch->getName(),
285 | $archOption,
286 | $this->stability->getName(),
287 | $stabilityOption,
288 | $this->version->choices($releases),
289 | ]);
290 |
291 | throw new \UnexpectedValueException($message);
292 | }
293 |
294 | /**
295 | * @throws \Throwable
296 | */
297 | private function assetToArchive(AssetInterface $asset, OutputInterface $out, ?string $temp = null): ArchiveInterface
298 | {
299 | $factory = new Factory();
300 |
301 | $progress = new ProgressBar($out);
302 | $progress->setFormat(' [%bar%] %percent:3s%% (%size%Kb/%total%Kb)');
303 | $progress->setMessage('0.00', 'size');
304 | $progress->setMessage('?.??', 'total');
305 | $progress->display();
306 |
307 | try {
308 | return $factory->fromAsset($asset, static function (int $size, int $total) use ($progress): void {
309 | if ($progress->getMaxSteps() !== $total) {
310 | $progress->setMaxSteps($total);
311 | }
312 |
313 | if ($progress->getStartTime() === 0) {
314 | $progress->start();
315 | }
316 |
317 | $progress->setMessage(\number_format($size / 1000, 2), 'size');
318 | $progress->setMessage(\number_format($total / 1000, 2), 'total');
319 |
320 | $progress->setProgress($size);
321 | }, $temp);
322 | } finally {
323 | $progress->clear();
324 | }
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/src/MakeConfigCommand.php:
--------------------------------------------------------------------------------
1 | location = new InstallationLocationOption($this);
22 | }
23 |
24 | /**
25 | *
26 | * @throws \Throwable
27 | */
28 | public function execute(InputInterface $input, OutputInterface $output): int
29 | {
30 | $io = $this->io($input, $output);
31 |
32 |
33 | $target = $this->location->get($input, $io) . '/.rr.yaml';
34 |
35 | if (\is_file($target) || \is_file(\getcwd() . '/.rr.yaml')) {
36 | return self::FAILURE;
37 | }
38 |
39 | $generator = new Generator();
40 | $plugins = $input->getOption('preset') ?
41 | Plugins::fromPreset($input->getOption('preset')) :
42 | Plugins::fromPlugins($input->getOption('plugin'));
43 |
44 | try {
45 | $config = $generator->generate($plugins);
46 | \file_put_contents($target, $config);
47 | } catch (\Throwable $e) {
48 | $io->error($e->getMessage());
49 | return self::FAILURE;
50 | }
51 |
52 | return self::SUCCESS;
53 | }
54 |
55 | protected function configure(): void
56 | {
57 | $this->addOption(
58 | 'plugin',
59 | 'p',
60 | InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
61 | 'Generate configuration with selected plugins.',
62 | );
63 |
64 | $this->addOption(
65 | 'preset',
66 | null,
67 | InputOption::VALUE_OPTIONAL,
68 | 'Generate configuration with plugins in a selected preset.',
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Repository/Asset.php:
--------------------------------------------------------------------------------
1 | name = $name;
22 | $this->uri = $uri;
23 | }
24 |
25 | public function getName(): string
26 | {
27 | return $this->name;
28 | }
29 |
30 | public function getUri(): string
31 | {
32 | return $this->uri;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Repository/AssetInterface.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | public function download(?\Closure $progress = null): iterable;
24 | }
25 |
--------------------------------------------------------------------------------
/src/Repository/AssetsCollection.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | final class AssetsCollection extends Collection
18 | {
19 | /**
20 | * @return $this
21 | */
22 | public function onlyRoadrunner(): self
23 | {
24 | return $this->filter(
25 | static fn(AssetInterface $asset): bool =>
26 | \str_starts_with($asset->getName(), 'roadrunner'),
27 | );
28 | }
29 |
30 | /**
31 | * @return $this
32 | */
33 | public function exceptDebPackages(): self
34 | {
35 | return $this->except(
36 | static fn(AssetInterface $asset): bool =>
37 | \str_ends_with(\strtolower($asset->getName()), '.deb'),
38 | );
39 | }
40 |
41 | /**
42 | * @return $this
43 | */
44 | public function whereArchitecture(string $arch): self
45 | {
46 | return $this->filter(
47 | static fn(AssetInterface $asset): bool =>
48 | \str_contains($asset->getName(), '-' . \strtolower($arch) . '.'),
49 | );
50 | }
51 |
52 | /**
53 | * @return $this
54 | */
55 | public function whereOperatingSystem(string $os): self
56 | {
57 | return $this->filter(
58 | static fn(AssetInterface $asset): bool =>
59 | \str_contains($asset->getName(), '-' . \strtolower($os) . '-'),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Repository/Collection.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | abstract class Collection implements \IteratorAggregate, \Countable
23 | {
24 | /**
25 | * @var array
26 | */
27 | protected array $items;
28 |
29 | /**
30 | * @param array $items
31 | */
32 | final public function __construct(array $items)
33 | {
34 | $this->items = $items;
35 | }
36 |
37 | /**
38 | * @param mixed|iterable|\Closure $items
39 | * @return static
40 | */
41 | public static function create($items): self
42 | {
43 | switch (true) {
44 | case $items instanceof static:
45 | return $items;
46 |
47 | case $items instanceof \Traversable:
48 | $items = \iterator_to_array($items);
49 |
50 | // no break
51 | case \is_array($items):
52 | return new static($items);
53 |
54 | case $items instanceof \Closure:
55 | return static::from($items);
56 |
57 | default:
58 | throw new \InvalidArgumentException(
59 | \sprintf('Unsupported iterable type %s', \get_debug_type($items)),
60 | );
61 | }
62 | }
63 |
64 | /**
65 | * @return static
66 | */
67 | public static function from(\Closure $generator): self
68 | {
69 | return static::create($generator());
70 | }
71 |
72 | /**
73 | * @param callable(T): bool $filter
74 | * @return $this
75 | */
76 | public function filter(callable $filter): self
77 | {
78 | return new static(\array_filter($this->items, $filter));
79 | }
80 |
81 | /**
82 | * @param callable(T): mixed $map
83 | * @return $this
84 | */
85 | public function map(callable $map): self
86 | {
87 | return new static(\array_map($map, $this->items));
88 | }
89 |
90 | /**
91 | * @param callable(T): bool $filter
92 | * @return $this
93 | *
94 | * @psalm-suppress MissingClosureParamType
95 | * @psalm-suppress MixedArgument
96 | */
97 | public function except(callable $filter): self
98 | {
99 | $callback = static fn(...$args): bool => ! $filter(...$args);
100 |
101 | return new static(\array_filter($this->items, $callback));
102 | }
103 |
104 | /**
105 | * @param null|callable(T): bool $filter
106 | * @return T|null
107 | */
108 | public function first(?callable $filter = null): ?object
109 | {
110 | $self = $filter === null ? $this : $this->filter($filter);
111 |
112 | return $self->items === [] ? null : \reset($self->items);
113 | }
114 |
115 | /**
116 | * @param callable(): T $otherwise
117 | * @param null|callable(T): bool $filter
118 | * @return T
119 | */
120 | public function firstOr(callable $otherwise, ?callable $filter = null): object
121 | {
122 | return $this->first($filter) ?? $otherwise();
123 | }
124 |
125 | public function getIterator(): \Traversable
126 | {
127 | return new \ArrayIterator($this->items);
128 | }
129 |
130 | public function count(): int
131 | {
132 | return \count($this->items);
133 | }
134 |
135 | /**
136 | * @return $this
137 | */
138 | public function whenEmpty(callable $then): self
139 | {
140 | if ($this->empty()) {
141 | $then();
142 | }
143 |
144 | return $this;
145 | }
146 |
147 | public function empty(): bool
148 | {
149 | return $this->items === [];
150 | }
151 |
152 | /**
153 | * @return array
154 | */
155 | public function toArray(): array
156 | {
157 | return \array_values($this->items);
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/Repository/GitHub/GitHubAsset.php:
--------------------------------------------------------------------------------
1 | client = $client;
31 |
32 | parent::__construct($name, $uri);
33 | }
34 |
35 | /**
36 | * @param GitHubAssetApiResponse $asset
37 | * @return static
38 | *
39 | * @psalm-suppress DocblockTypeContradiction
40 | */
41 | public static function fromApiResponse(HttpClientInterface $client, array $asset): self
42 | {
43 | // Validate name
44 | if (! isset($asset['name']) || ! \is_string($asset['name'])) {
45 | throw new \InvalidArgumentException(
46 | 'Passed array must contain "name" value of type string',
47 | );
48 | }
49 |
50 | // Validate uri
51 | if (! isset($asset['browser_download_url']) || ! \is_string($asset['browser_download_url'])) {
52 | throw new \InvalidArgumentException(
53 | 'Passed array must contain "browser_download_url" key of type string',
54 | );
55 | }
56 |
57 | return new self($client, $asset['name'], $asset['browser_download_url']);
58 | }
59 |
60 | /**
61 | *
62 | * @throws ExceptionInterface
63 | */
64 | public function download(?\Closure $progress = null): \Traversable
65 | {
66 | $response = $this->client->request('GET', $this->getUri(), [
67 | 'on_progress' => $progress,
68 | ]);
69 |
70 | foreach ($this->client->stream($response) as $chunk) {
71 | yield $chunk->getContent();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Repository/GitHub/GitHubRelease.php:
--------------------------------------------------------------------------------
1 |
25 | * }
26 | */
27 | final class GitHubRelease extends Release
28 | {
29 | private HttpClientInterface $client;
30 |
31 | /**
32 | * @param iterable|array $assets
33 | */
34 | public function __construct(
35 | HttpClientInterface $client,
36 | string $name,
37 | string $version,
38 | string $repository,
39 | iterable $assets = [],
40 | ) {
41 | $this->client = $client;
42 |
43 | parent::__construct($name, $version, $repository, $assets);
44 | }
45 |
46 | /**
47 | * @param GitHubReleaseApiResponse $release
48 | * @return static
49 | */
50 | public static function fromApiResponse(GitHubRepository $repository, HttpClientInterface $client, array $release): self
51 | {
52 | if (! isset($release['name'])) {
53 | throw new \InvalidArgumentException(
54 | 'Passed array must contain "name" value of type string',
55 | );
56 | }
57 |
58 | $instantiator = static function () use ($client, $release): \Generator {
59 | foreach ($release['assets'] ?? [] as $item) {
60 | yield GitHubAsset::fromApiResponse($client, $item);
61 | }
62 | };
63 |
64 | $name = self::getTagName($release);
65 | $version = $release['tag_name'] ?? $release['name'];
66 |
67 | return new self($client, $name, $version, $repository->getName(), AssetsCollection::from($instantiator));
68 | }
69 |
70 | public function getConfig(): string
71 | {
72 | $config = \vsprintf('https://raw.githubusercontent.com/%s/%s/.rr.yaml', [
73 | $this->getRepositoryName(),
74 | $this->getVersion(),
75 | ]);
76 |
77 | $response = $this->client->request('GET', $config);
78 |
79 | return $response->getContent();
80 | }
81 |
82 | /**
83 | * Returns pretty-formatted tag (release) name.
84 | *
85 | * Note: The return value is "pretty", but that does not mean that the
86 | * tag physically exists.
87 | *
88 | * @param array { tag_name: string, name: string } $release
89 | */
90 | private static function getTagName(array $release): string
91 | {
92 | $parser = new VersionParser();
93 |
94 | try {
95 | return $parser->normalize($release['tag_name']);
96 | } catch (\Throwable $e) {
97 | try {
98 | return $parser->normalize($release['name']);
99 | } catch (\Throwable $e) {
100 | return 'dev-' . $release['tag_name'];
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Repository/GitHub/GitHubRepository.php:
--------------------------------------------------------------------------------
1 | 'application/vnd.github.v3+json',
40 | ];
41 |
42 | public function __construct(string $owner, string $repository, ?HttpClientInterface $client = null)
43 | {
44 | $this->name = $owner . '/' . $repository;
45 | $this->client = $client ?? HttpClient::create();
46 | }
47 |
48 | public static function create(string $owner, string $name, ?HttpClientInterface $client = null): GitHubRepository
49 | {
50 | return new GitHubRepository($owner, $name, $client);
51 | }
52 |
53 | /**
54 | *
55 | * @throws ExceptionInterface
56 | */
57 | public function getReleases(): ReleasesCollection
58 | {
59 | return ReleasesCollection::from(function () {
60 | $page = 0;
61 |
62 | do {
63 | $response = $this->releasesRequest(++$page);
64 |
65 | /** @psalm-var GitHubReleaseApiResponse $data */
66 | foreach ($response->toArray() as $data) {
67 | yield GitHubRelease::fromApiResponse($this, $this->client, $data);
68 | }
69 | } while ($this->hasNextPage($response));
70 | });
71 | }
72 |
73 | public function getName(): string
74 | {
75 | return $this->name;
76 | }
77 |
78 | /**
79 | * @throws TransportExceptionInterface
80 | * @see HttpClientInterface::request()
81 | */
82 | protected function request(string $method, string $uri, array $options = []): ResponseInterface
83 | {
84 | // Merge headers with defaults
85 | $options['headers'] = \array_merge($this->headers, (array) ($options['headers'] ?? []));
86 |
87 | return $this->client->request($method, $uri, $options);
88 | }
89 |
90 | /**
91 | * @param positive-int $page
92 | * @throws TransportExceptionInterface
93 | */
94 | private function releasesRequest(int $page): ResponseInterface
95 | {
96 | return $this->request('GET', $this->uri(self::URL_RELEASES), [
97 | 'query' => [
98 | 'page' => $page,
99 | ],
100 | ]);
101 | }
102 |
103 | private function uri(string $pattern): string
104 | {
105 | return \sprintf($pattern, $this->getName());
106 | }
107 |
108 | /**
109 | * @throws ExceptionInterface
110 | */
111 | private function hasNextPage(ResponseInterface $response): bool
112 | {
113 | $headers = $response->getHeaders();
114 | $link = $headers['link'] ?? [];
115 |
116 | if (! isset($link[0])) {
117 | return false;
118 | }
119 |
120 | return \str_contains($link[0], 'rel="next"');
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/Repository/Release.php:
--------------------------------------------------------------------------------
1 | version = $version;
33 | $this->repository = $repository;
34 |
35 | $this->name = $this->simplifyReleaseName($name);
36 | $this->assets = AssetsCollection::create($assets);
37 |
38 | $this->stability = $this->parseStability($version);
39 | }
40 |
41 | public function getName(): string
42 | {
43 | return $this->name;
44 | }
45 |
46 | public function getVersion(): string
47 | {
48 | return $this->version;
49 | }
50 |
51 | public function getRepositoryName(): string
52 | {
53 | return $this->repository;
54 | }
55 |
56 | #[ExpectedValues(valuesFromClass: Stability::class)]
57 | public function getStability(): string
58 | {
59 | return $this->stability;
60 | }
61 |
62 | public function getAssets(): AssetsCollection
63 | {
64 | return $this->assets;
65 | }
66 |
67 | public function satisfies(string $constraint): bool
68 | {
69 | return Semver::satisfies($this->getName(), $constraint);
70 | }
71 |
72 | private function parseStability(string $version): string
73 | {
74 | return VersionParser::parseStability($version);
75 | }
76 |
77 | private function simplifyReleaseName(string $name): string
78 | {
79 | $version = (new VersionParser())->normalize($name);
80 |
81 | $parts = \explode('-', $version);
82 | $number = \substr($parts[0], 0, -2);
83 |
84 | return isset($parts[1])
85 | ? $number . '-' . $parts[1]
86 | : $number
87 | ;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Repository/ReleaseInterface.php:
--------------------------------------------------------------------------------
1 |
46 | */
47 | public function getAssets(): AssetsCollection;
48 |
49 | public function satisfies(string $constraint): bool;
50 |
51 | public function getConfig(): string;
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/ReleasesCollection.php:
--------------------------------------------------------------------------------
1 |
18 | * @psalm-import-type StabilityType from Stability
19 | */
20 | final class ReleasesCollection extends Collection
21 | {
22 | /**
23 | * @return $this
24 | */
25 | public function satisfies(string ...$constraints): self
26 | {
27 | $result = $this;
28 |
29 | foreach ($this->constraints($constraints) as $constraint) {
30 | $result = $result->filter(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
31 | }
32 |
33 | return $result;
34 | }
35 |
36 | /**
37 | * @return $this
38 | */
39 | public function notSatisfies(string ...$constraints): self
40 | {
41 | $result = $this;
42 |
43 | foreach ($this->constraints($constraints) as $constraint) {
44 | $result = $result->except(static fn(ReleaseInterface $r): bool => $r->satisfies($constraint));
45 | }
46 |
47 | return $result;
48 | }
49 |
50 | /**
51 | * @return $this
52 | */
53 | public function withAssets(): self
54 | {
55 | return $this->filter(
56 | static fn(ReleaseInterface $r): bool => ! $r->getAssets()
57 | ->empty(),
58 | );
59 | }
60 |
61 | /**
62 | * @return $this
63 | */
64 | public function sortByVersion(): self
65 | {
66 | $result = $this->items;
67 |
68 | $sort = function (ReleaseInterface $a, ReleaseInterface $b): int {
69 | return \version_compare($this->comparisonVersionString($b), $this->comparisonVersionString($a));
70 | };
71 |
72 | \uasort($result, $sort);
73 |
74 | return new self($result);
75 | }
76 |
77 | /**
78 | * @return $this
79 | */
80 | public function stable(): self
81 | {
82 | return $this->stability(Stability::STABILITY_STABLE);
83 | }
84 |
85 | /**
86 | * @param StabilityType $stability
87 | * @return $this
88 | */
89 | public function stability(string $stability): self
90 | {
91 | $filter = static fn(ReleaseInterface $rel): bool => $rel->getStability() === $stability;
92 |
93 | return $this->filter($filter);
94 | }
95 |
96 | /**
97 | * @param StabilityType $stability
98 | * @return $this
99 | */
100 | public function minimumStability(string $stability): self
101 | {
102 | $weight = Stability::toInt($stability);
103 |
104 | return $this->filter(static function (ReleaseInterface $release) use ($weight): bool {
105 | return Stability::toInt($release->getStability()) >= $weight;
106 | });
107 | }
108 |
109 | /**
110 | * @param array $constraints
111 | * @return array
112 | */
113 | private function constraints(array $constraints): array
114 | {
115 | $result = [];
116 |
117 | foreach ($constraints as $constraint) {
118 | foreach (\explode('|', $constraint) as $expression) {
119 | $result[] = $expression;
120 | }
121 | }
122 |
123 | return \array_unique(
124 | \array_filter(
125 | \array_map('\\trim', $result),
126 | ),
127 | );
128 | }
129 |
130 | private function comparisonVersionString(ReleaseInterface $release): string
131 | {
132 | $stability = $release->getStability();
133 | $weight = Stability::toInt($stability);
134 |
135 | return \str_replace('-' . $stability, '.' . $weight . '.', $release->getVersion());
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Repository/RepositoriesCollection.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | private array $repositories;
20 |
21 | /**
22 | * @param array $repositories
23 | */
24 | public function __construct(array $repositories)
25 | {
26 | $this->repositories = $repositories;
27 | }
28 |
29 | public function getName(): string
30 | {
31 | return 'unknown/unknown';
32 | }
33 |
34 | public function getReleases(): ReleasesCollection
35 | {
36 | return ReleasesCollection::from(function () {
37 | foreach ($this->repositories as $repository) {
38 | yield from $repository->getReleases();
39 | }
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Repository/RepositoryInterface.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | public function getReleases(): ReleasesCollection;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Repository/Version1/StaticRepository.php:
--------------------------------------------------------------------------------
1 | }>
27 | */
28 | private const RELEASES_DATA = [
29 | [
30 | 'name' => '1.9.1',
31 | 'version' => 'v1.9.1',
32 | 'assets' => [
33 | [
34 | 'name' => 'roadrunner-1.9.1-darwin-amd64.zip',
35 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.1/roadrunner-1.9.1-darwin-amd64.zip',
36 | ],
37 | [
38 | 'name' => 'roadrunner-1.9.1-freebsd-amd64.zip',
39 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.1/roadrunner-1.9.1-freebsd-amd64.zip',
40 | ],
41 | [
42 | 'name' => 'roadrunner-1.9.1-linux-amd64.tar.gz',
43 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.1/roadrunner-1.9.1-linux-amd64.tar.gz',
44 | ],
45 | [
46 | 'name' => 'roadrunner-1.9.1-unknown-musl-amd64.zip',
47 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.1/roadrunner-1.9.1-unknown-musl-amd64.zip',
48 | ],
49 | [
50 | 'name' => 'roadrunner-1.9.1-windows-amd64.zip',
51 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.1/roadrunner-1.9.1-windows-amd64.zip',
52 | ],
53 | ],
54 | ],
55 | [
56 | 'name' => '1.9.0',
57 | 'version' => 'v1.9.0',
58 | 'assets' => [
59 | [
60 | 'name' => 'roadrunner-1.9.0-darwin-amd64.zip',
61 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.0/roadrunner-1.9.0-darwin-amd64.zip',
62 | ],
63 | [
64 | 'name' => 'roadrunner-1.9.0-freebsd-amd64.zip',
65 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.0/roadrunner-1.9.0-freebsd-amd64.zip',
66 | ],
67 | [
68 | 'name' => 'roadrunner-1.9.0-linux-amd64.tar.gz',
69 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.0/roadrunner-1.9.0-linux-amd64.tar.gz',
70 | ],
71 | [
72 | 'name' => 'roadrunner-1.9.0-unknown-musl-amd64.zip',
73 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.0/roadrunner-1.9.0-unknown-musl-amd64.zip',
74 | ],
75 | [
76 | 'name' => 'roadrunner-1.9.0-windows-amd64.zip',
77 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.9.0/roadrunner-1.9.0-windows-amd64.zip',
78 | ],
79 | ],
80 | ],
81 | [
82 | 'name' => '1.8.4',
83 | 'version' => 'v1.8.4',
84 | 'assets' => [
85 | [
86 | 'name' => 'roadrunner-1.8.4-darwin-amd64.zip',
87 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.4/roadrunner-1.8.4-darwin-amd64.zip',
88 | ],
89 | [
90 | 'name' => 'roadrunner-1.8.4-freebsd-amd64.zip',
91 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.4/roadrunner-1.8.4-freebsd-amd64.zip',
92 | ],
93 | [
94 | 'name' => 'roadrunner-1.8.4-linux-amd64.tar.gz',
95 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.4/roadrunner-1.8.4-linux-amd64.tar.gz',
96 | ],
97 | [
98 | 'name' => 'roadrunner-1.8.4-unknown-musl-amd64.zip',
99 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.4/roadrunner-1.8.4-unknown-musl-amd64.zip',
100 | ],
101 | [
102 | 'name' => 'roadrunner-1.8.4-windows-amd64.zip',
103 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.4/roadrunner-1.8.4-windows-amd64.zip',
104 | ],
105 | ],
106 | ],
107 | [
108 | 'name' => '1.8.3',
109 | 'version' => 'v1.8.3',
110 | 'assets' => [
111 | [
112 | 'name' => 'roadrunner-1.8.3-darwin-amd64.zip',
113 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.3/roadrunner-1.8.3-darwin-amd64.zip',
114 | ],
115 | [
116 | 'name' => 'roadrunner-1.8.3-freebsd-amd64.zip',
117 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.3/roadrunner-1.8.3-freebsd-amd64.zip',
118 | ],
119 | [
120 | 'name' => 'roadrunner-1.8.3-linux-amd64.tar.gz',
121 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.3/roadrunner-1.8.3-linux-amd64.tar.gz',
122 | ],
123 | [
124 | 'name' => 'roadrunner-1.8.3-unknown-musl-amd64.zip',
125 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.3/roadrunner-1.8.3-unknown-musl-amd64.zip',
126 | ],
127 | [
128 | 'name' => 'roadrunner-1.8.3-windows-amd64.zip',
129 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.3/roadrunner-1.8.3-windows-amd64.zip',
130 | ],
131 | ],
132 | ],
133 | [
134 | 'name' => '1.8.2',
135 | 'version' => 'v1.8.2',
136 | 'assets' => [
137 | [
138 | 'name' => 'roadrunner-1.8.2-darwin-amd64.zip',
139 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.2/roadrunner-1.8.2-darwin-amd64.zip',
140 | ],
141 | [
142 | 'name' => 'roadrunner-1.8.2-freebsd-amd64.zip',
143 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.2/roadrunner-1.8.2-freebsd-amd64.zip',
144 | ],
145 | [
146 | 'name' => 'roadrunner-1.8.2-linux-amd64.tar.gz',
147 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.2/roadrunner-1.8.2-linux-amd64.tar.gz',
148 | ],
149 | [
150 | 'name' => 'roadrunner-1.8.2-unknown-musl-amd64.zip',
151 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.2/roadrunner-1.8.2-unknown-musl-amd64.zip',
152 | ],
153 | [
154 | 'name' => 'roadrunner-1.8.2-windows-amd64.zip',
155 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.2/roadrunner-1.8.2-windows-amd64.zip',
156 | ],
157 | ],
158 | ],
159 | [
160 | 'name' => '1.8.1',
161 | 'version' => 'v1.8.1',
162 | 'assets' => [
163 | [
164 | 'name' => 'roadrunner-1.8.1-darwin-amd64.zip',
165 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.1/roadrunner-1.8.1-darwin-amd64.zip',
166 | ],
167 | [
168 | 'name' => 'roadrunner-1.8.1-freebsd-amd64.zip',
169 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.1/roadrunner-1.8.1-freebsd-amd64.zip',
170 | ],
171 | [
172 | 'name' => 'roadrunner-1.8.1-linux-amd64.tar.gz',
173 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.1/roadrunner-1.8.1-linux-amd64.tar.gz',
174 | ],
175 | [
176 | 'name' => 'roadrunner-1.8.1-unknown-musl-amd64.zip',
177 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.1/roadrunner-1.8.1-unknown-musl-amd64.zip',
178 | ],
179 | [
180 | 'name' => 'roadrunner-1.8.1-windows-amd64.zip',
181 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.1/roadrunner-1.8.1-windows-amd64.zip',
182 | ],
183 | ],
184 | ],
185 | [
186 | 'name' => '1.8.0',
187 | 'version' => 'v1.8.0',
188 | 'assets' => [
189 | [
190 | 'name' => 'roadrunner-1.8.0-darwin-amd64.zip',
191 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.0/roadrunner-1.8.0-darwin-amd64.zip',
192 | ],
193 | [
194 | 'name' => 'roadrunner-1.8.0-freebsd-amd64.zip',
195 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.0/roadrunner-1.8.0-freebsd-amd64.zip',
196 | ],
197 | [
198 | 'name' => 'roadrunner-1.8.0-linux-amd64.tar.gz',
199 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.0/roadrunner-1.8.0-linux-amd64.tar.gz',
200 | ],
201 | [
202 | 'name' => 'roadrunner-1.8.0-unknown-musl-amd64.zip',
203 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.0/roadrunner-1.8.0-unknown-musl-amd64.zip',
204 | ],
205 | [
206 | 'name' => 'roadrunner-1.8.0-windows-amd64.zip',
207 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.8.0/roadrunner-1.8.0-windows-amd64.zip',
208 | ],
209 | ],
210 | ],
211 | [
212 | 'name' => '1.7.1',
213 | 'version' => 'v1.7.1',
214 | 'assets' => [
215 | [
216 | 'name' => 'roadrunner-1.7.1-darwin-amd64.zip',
217 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.1/roadrunner-1.7.1-darwin-amd64.zip',
218 | ],
219 | [
220 | 'name' => 'roadrunner-1.7.1-freebsd-amd64.zip',
221 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.1/roadrunner-1.7.1-freebsd-amd64.zip',
222 | ],
223 | [
224 | 'name' => 'roadrunner-1.7.1-linux-amd64.tar.gz',
225 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.1/roadrunner-1.7.1-linux-amd64.tar.gz',
226 | ],
227 | [
228 | 'name' => 'roadrunner-1.7.1-unknown-musl-amd64.zip',
229 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.1/roadrunner-1.7.1-unknown-musl-amd64.zip',
230 | ],
231 | [
232 | 'name' => 'roadrunner-1.7.1-windows-amd64.zip',
233 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.1/roadrunner-1.7.1-windows-amd64.zip',
234 | ],
235 | ],
236 | ],
237 | [
238 | 'name' => '1.7.0',
239 | 'version' => 'v1.7.0',
240 | 'assets' => [
241 | [
242 | 'name' => 'roadrunner-1.7.0-darwin-amd64.zip',
243 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.0/roadrunner-1.7.0-darwin-amd64.zip',
244 | ],
245 | [
246 | 'name' => 'roadrunner-1.7.0-freebsd-amd64.zip',
247 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.0/roadrunner-1.7.0-freebsd-amd64.zip',
248 | ],
249 | [
250 | 'name' => 'roadrunner-1.7.0-linux-amd64.tar.gz',
251 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.0/roadrunner-1.7.0-linux-amd64.tar.gz',
252 | ],
253 | [
254 | 'name' => 'roadrunner-1.7.0-unknown-musl-amd64.zip',
255 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.0/roadrunner-1.7.0-unknown-musl-amd64.zip',
256 | ],
257 | [
258 | 'name' => 'roadrunner-1.7.0-windows-amd64.zip',
259 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.7.0/roadrunner-1.7.0-windows-amd64.zip',
260 | ],
261 | ],
262 | ],
263 | [
264 | 'name' => '1.6.4',
265 | 'version' => 'v1.6.4',
266 | 'assets' => [
267 | [
268 | 'name' => 'roadrunner-1.6.4-darwin-amd64.zip',
269 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.4/roadrunner-1.6.4-darwin-amd64.zip',
270 | ],
271 | [
272 | 'name' => 'roadrunner-1.6.4-freebsd-amd64.zip',
273 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.4/roadrunner-1.6.4-freebsd-amd64.zip',
274 | ],
275 | [
276 | 'name' => 'roadrunner-1.6.4-linux-amd64.tar.gz',
277 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.4/roadrunner-1.6.4-linux-amd64.tar.gz',
278 | ],
279 | [
280 | 'name' => 'roadrunner-1.6.4-unknown-musl-amd64.zip',
281 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.4/roadrunner-1.6.4-unknown-musl-amd64.zip',
282 | ],
283 | [
284 | 'name' => 'roadrunner-1.6.4-windows-amd64.zip',
285 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.4/roadrunner-1.6.4-windows-amd64.zip',
286 | ],
287 | ],
288 | ],
289 | [
290 | 'name' => '1.6.3',
291 | 'version' => 'v1.6.3',
292 | 'assets' => [
293 | [
294 | 'name' => 'roadrunner-1.6.3-darwin-amd64.zip',
295 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.3/roadrunner-1.6.3-darwin-amd64.zip',
296 | ],
297 | [
298 | 'name' => 'roadrunner-1.6.3-freebsd-amd64.zip',
299 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.3/roadrunner-1.6.3-freebsd-amd64.zip',
300 | ],
301 | [
302 | 'name' => 'roadrunner-1.6.3-linux-amd64.tar.gz',
303 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.3/roadrunner-1.6.3-linux-amd64.tar.gz',
304 | ],
305 | [
306 | 'name' => 'roadrunner-1.6.3-unknown-musl-amd64.zip',
307 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.3/roadrunner-1.6.3-unknown-musl-amd64.zip',
308 | ],
309 | [
310 | 'name' => 'roadrunner-1.6.3-windows-amd64.zip',
311 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.3/roadrunner-1.6.3-windows-amd64.zip',
312 | ],
313 | ],
314 | ],
315 | [
316 | 'name' => '1.6.2',
317 | 'version' => 'v1.6.2',
318 | 'assets' => [
319 | [
320 | 'name' => 'roadrunner-1.6.2-darwin-amd64.zip',
321 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.2/roadrunner-1.6.2-darwin-amd64.zip',
322 | ],
323 | [
324 | 'name' => 'roadrunner-1.6.2-freebsd-amd64.zip',
325 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.2/roadrunner-1.6.2-freebsd-amd64.zip',
326 | ],
327 | [
328 | 'name' => 'roadrunner-1.6.2-linux-amd64.tar.gz',
329 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.2/roadrunner-1.6.2-linux-amd64.tar.gz',
330 | ],
331 | [
332 | 'name' => 'roadrunner-1.6.2-unknown-musl-amd64.zip',
333 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.2/roadrunner-1.6.2-unknown-musl-amd64.zip',
334 | ],
335 | [
336 | 'name' => 'roadrunner-1.6.2-windows-amd64.zip',
337 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.2/roadrunner-1.6.2-windows-amd64.zip',
338 | ],
339 | ],
340 | ],
341 | [
342 | 'name' => '1.6.1',
343 | 'version' => 'v1.6.1',
344 | 'assets' => [
345 | [
346 | 'name' => 'roadrunner-1.6.1-darwin-amd64.zip',
347 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.1/roadrunner-1.6.1-darwin-amd64.zip',
348 | ],
349 | [
350 | 'name' => 'roadrunner-1.6.1-freebsd-amd64.zip',
351 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.1/roadrunner-1.6.1-freebsd-amd64.zip',
352 | ],
353 | [
354 | 'name' => 'roadrunner-1.6.1-linux-amd64.tar.gz',
355 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.1/roadrunner-1.6.1-linux-amd64.tar.gz',
356 | ],
357 | [
358 | 'name' => 'roadrunner-1.6.1-windows-amd64.zip',
359 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.1/roadrunner-1.6.1-windows-amd64.zip',
360 | ],
361 | ],
362 | ],
363 | [
364 | 'name' => '1.6.0',
365 | 'version' => 'v1.6.0',
366 | 'assets' => [
367 | [
368 | 'name' => 'roadrunner-1.6.0-darwin-amd64.zip',
369 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.0/roadrunner-1.6.0-darwin-amd64.zip',
370 | ],
371 | [
372 | 'name' => 'roadrunner-1.6.0-freebsd-amd64.zip',
373 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.0/roadrunner-1.6.0-freebsd-amd64.zip',
374 | ],
375 | [
376 | 'name' => 'roadrunner-1.6.0-linux-amd64.tar.gz',
377 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.0/roadrunner-1.6.0-linux-amd64.tar.gz',
378 | ],
379 | [
380 | 'name' => 'roadrunner-1.6.0-windows-amd64.zip',
381 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.6.0/roadrunner-1.6.0-windows-amd64.zip',
382 | ],
383 | ],
384 | ],
385 | [
386 | 'name' => '1.5.3',
387 | 'version' => 'v1.5.3',
388 | 'assets' => [
389 | [
390 | 'name' => 'roadrunner-1.5.3-darwin-amd64.zip',
391 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.3/roadrunner-1.5.3-darwin-amd64.zip',
392 | ],
393 | [
394 | 'name' => 'roadrunner-1.5.3-freebsd-amd64.zip',
395 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.3/roadrunner-1.5.3-freebsd-amd64.zip',
396 | ],
397 | [
398 | 'name' => 'roadrunner-1.5.3-linux-amd64.tar.gz',
399 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.3/roadrunner-1.5.3-linux-amd64.tar.gz',
400 | ],
401 | [
402 | 'name' => 'roadrunner-1.5.3-windows-amd64.zip',
403 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.3/roadrunner-1.5.3-windows-amd64.zip',
404 | ],
405 | ],
406 | ],
407 | [
408 | 'name' => '1.5.2',
409 | 'version' => 'v1.5.2',
410 | 'assets' => [
411 | [
412 | 'name' => 'roadrunner-1.5.2-darwin-amd64.zip',
413 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.2/roadrunner-1.5.2-darwin-amd64.zip',
414 | ],
415 | [
416 | 'name' => 'roadrunner-1.5.2-freebsd-amd64.zip',
417 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.2/roadrunner-1.5.2-freebsd-amd64.zip',
418 | ],
419 | [
420 | 'name' => 'roadrunner-1.5.2-linux-amd64.tar.gz',
421 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.2/roadrunner-1.5.2-linux-amd64.tar.gz',
422 | ],
423 | [
424 | 'name' => 'roadrunner-1.5.2-windows-amd64.zip',
425 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.2/roadrunner-1.5.2-windows-amd64.zip',
426 | ],
427 | ],
428 | ],
429 | [
430 | 'name' => '1.5.1',
431 | 'version' => 'v1.5.1',
432 | 'assets' => [
433 | [
434 | 'name' => 'roadrunner-1.5.1-darwin-amd64.zip',
435 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.1/roadrunner-1.5.1-darwin-amd64.zip',
436 | ],
437 | [
438 | 'name' => 'roadrunner-1.5.1-freebsd-amd64.zip',
439 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.1/roadrunner-1.5.1-freebsd-amd64.zip',
440 | ],
441 | [
442 | 'name' => 'roadrunner-1.5.1-linux-amd64.tar.gz',
443 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.1/roadrunner-1.5.1-linux-amd64.tar.gz',
444 | ],
445 | [
446 | 'name' => 'roadrunner-1.5.1-windows-amd64.zip',
447 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.1/roadrunner-1.5.1-windows-amd64.zip',
448 | ],
449 | ],
450 | ],
451 | [
452 | 'name' => '1.5.0',
453 | 'version' => 'v1.5.0',
454 | 'assets' => [
455 | [
456 | 'name' => 'roadrunner-1.5.0-darwin-amd64.zip',
457 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.0/roadrunner-1.5.0-darwin-amd64.zip',
458 | ],
459 | [
460 | 'name' => 'roadrunner-1.5.0-freebsd-amd64.zip',
461 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.0/roadrunner-1.5.0-freebsd-amd64.zip',
462 | ],
463 | [
464 | 'name' => 'roadrunner-1.5.0-linux-amd64.tar.gz',
465 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.0/roadrunner-1.5.0-linux-amd64.tar.gz',
466 | ],
467 | [
468 | 'name' => 'roadrunner-1.5.0-windows-amd64.zip',
469 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.5.0/roadrunner-1.5.0-windows-amd64.zip',
470 | ],
471 | ],
472 | ],
473 | [
474 | 'name' => '1.4.8',
475 | 'version' => 'v1.4.8',
476 | 'assets' => [
477 | [
478 | 'name' => 'roadrunner-1.4.8-darwin-amd64.zip',
479 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.8/roadrunner-1.4.8-darwin-amd64.zip',
480 | ],
481 | [
482 | 'name' => 'roadrunner-1.4.8-freebsd-amd64.zip',
483 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.8/roadrunner-1.4.8-freebsd-amd64.zip',
484 | ],
485 | [
486 | 'name' => 'roadrunner-1.4.8-linux-amd64.tar.gz',
487 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.8/roadrunner-1.4.8-linux-amd64.tar.gz',
488 | ],
489 | [
490 | 'name' => 'roadrunner-1.4.8-windows-amd64.zip',
491 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.8/roadrunner-1.4.8-windows-amd64.zip',
492 | ],
493 | ],
494 | ],
495 | [
496 | 'name' => '1.4.7',
497 | 'version' => 'v1.4.7',
498 | 'assets' => [
499 | [
500 | 'name' => 'roadrunner-1.4.7-darwin-amd64.zip',
501 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.7/roadrunner-1.4.7-darwin-amd64.zip',
502 | ],
503 | [
504 | 'name' => 'roadrunner-1.4.7-freebsd-amd64.zip',
505 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.7/roadrunner-1.4.7-freebsd-amd64.zip',
506 | ],
507 | [
508 | 'name' => 'roadrunner-1.4.7-linux-amd64.tar.gz',
509 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.7/roadrunner-1.4.7-linux-amd64.tar.gz',
510 | ],
511 | [
512 | 'name' => 'roadrunner-1.4.7-windows-amd64.zip',
513 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.7/roadrunner-1.4.7-windows-amd64.zip',
514 | ],
515 | ],
516 | ],
517 | [
518 | 'name' => '1.4.6',
519 | 'version' => 'v1.4.6',
520 | 'assets' => [
521 | [
522 | 'name' => 'roadrunner-1.4.6-darwin-amd64.zip',
523 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.6/roadrunner-1.4.6-darwin-amd64.zip',
524 | ],
525 | [
526 | 'name' => 'roadrunner-1.4.6-freebsd-amd64.zip',
527 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.6/roadrunner-1.4.6-freebsd-amd64.zip',
528 | ],
529 | [
530 | 'name' => 'roadrunner-1.4.6-linux-amd64.tar.gz',
531 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.6/roadrunner-1.4.6-linux-amd64.tar.gz',
532 | ],
533 | [
534 | 'name' => 'roadrunner-1.4.6-windows-amd64.zip',
535 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.6/roadrunner-1.4.6-windows-amd64.zip',
536 | ],
537 | ],
538 | ],
539 | [
540 | 'name' => '1.4.5',
541 | 'version' => 'v1.4.5',
542 | 'assets' => [
543 | [
544 | 'name' => 'roadrunner-1.4.5-darwin-amd64.zip',
545 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.5/roadrunner-1.4.5-darwin-amd64.zip',
546 | ],
547 | [
548 | 'name' => 'roadrunner-1.4.5-freebsd-amd64.zip',
549 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.5/roadrunner-1.4.5-freebsd-amd64.zip',
550 | ],
551 | [
552 | 'name' => 'roadrunner-1.4.5-linux-amd64.tar.gz',
553 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.5/roadrunner-1.4.5-linux-amd64.tar.gz',
554 | ],
555 | [
556 | 'name' => 'roadrunner-1.4.5-windows-amd64.zip',
557 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.5/roadrunner-1.4.5-windows-amd64.zip',
558 | ],
559 | ],
560 | ],
561 | [
562 | 'name' => '1.4.4',
563 | 'version' => 'v1.4.4',
564 | 'assets' => [
565 | [
566 | 'name' => 'roadrunner-1.4.4-darwin-amd64.zip',
567 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.4/roadrunner-1.4.4-darwin-amd64.zip',
568 | ],
569 | [
570 | 'name' => 'roadrunner-1.4.4-freebsd-amd64.zip',
571 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.4/roadrunner-1.4.4-freebsd-amd64.zip',
572 | ],
573 | [
574 | 'name' => 'roadrunner-1.4.4-linux-amd64.tar.gz',
575 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.4/roadrunner-1.4.4-linux-amd64.tar.gz',
576 | ],
577 | [
578 | 'name' => 'roadrunner-1.4.4-windows-amd64.zip',
579 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.4/roadrunner-1.4.4-windows-amd64.zip',
580 | ],
581 | ],
582 | ],
583 | [
584 | 'name' => '1.4.3',
585 | 'version' => 'v1.4.3',
586 | 'assets' => [
587 | [
588 | 'name' => 'roadrunner-1.4.3-darwin-amd64.zip',
589 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.3/roadrunner-1.4.3-darwin-amd64.zip',
590 | ],
591 | [
592 | 'name' => 'roadrunner-1.4.3-freebsd-amd64.zip',
593 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.3/roadrunner-1.4.3-freebsd-amd64.zip',
594 | ],
595 | [
596 | 'name' => 'roadrunner-1.4.3-linux-amd64.tar.gz',
597 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.3/roadrunner-1.4.3-linux-amd64.tar.gz',
598 | ],
599 | [
600 | 'name' => 'roadrunner-1.4.3-windows-amd64.zip',
601 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.3/roadrunner-1.4.3-windows-amd64.zip',
602 | ],
603 | ],
604 | ],
605 | [
606 | 'name' => '1.4.2',
607 | 'version' => 'v1.4.2',
608 | 'assets' => [
609 | [
610 | 'name' => 'roadrunner-1.4.2-darwin-amd64.zip',
611 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.2/roadrunner-1.4.2-darwin-amd64.zip',
612 | ],
613 | [
614 | 'name' => 'roadrunner-1.4.2-freebsd-amd64.zip',
615 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.2/roadrunner-1.4.2-freebsd-amd64.zip',
616 | ],
617 | [
618 | 'name' => 'roadrunner-1.4.2-linux-amd64.tar.gz',
619 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.2/roadrunner-1.4.2-linux-amd64.tar.gz',
620 | ],
621 | [
622 | 'name' => 'roadrunner-1.4.2-windows-amd64.zip',
623 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.2/roadrunner-1.4.2-windows-amd64.zip',
624 | ],
625 | ],
626 | ],
627 | [
628 | 'name' => '1.4.1',
629 | 'version' => 'v1.4.1',
630 | 'assets' => [
631 | [
632 | 'name' => 'roadrunner-1.4.1-darwin-amd64.zip',
633 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.1/roadrunner-1.4.1-darwin-amd64.zip',
634 | ],
635 | [
636 | 'name' => 'roadrunner-1.4.1-freebsd-amd64.zip',
637 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.1/roadrunner-1.4.1-freebsd-amd64.zip',
638 | ],
639 | [
640 | 'name' => 'roadrunner-1.4.1-linux-amd64.tar.gz',
641 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.1/roadrunner-1.4.1-linux-amd64.tar.gz',
642 | ],
643 | [
644 | 'name' => 'roadrunner-1.4.1-windows-amd64.zip',
645 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.1/roadrunner-1.4.1-windows-amd64.zip',
646 | ],
647 | ],
648 | ],
649 | [
650 | 'name' => '1.4.0',
651 | 'version' => 'v1.4.0',
652 | 'assets' => [
653 | [
654 | 'name' => 'roadrunner-1.4.0-darwin-amd64.zip',
655 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.0/roadrunner-1.4.0-darwin-amd64.zip',
656 | ],
657 | [
658 | 'name' => 'roadrunner-1.4.0-freebsd-amd64.zip',
659 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.0/roadrunner-1.4.0-freebsd-amd64.zip',
660 | ],
661 | [
662 | 'name' => 'roadrunner-1.4.0-linux-amd64.tar.gz',
663 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.0/roadrunner-1.4.0-linux-amd64.tar.gz',
664 | ],
665 | [
666 | 'name' => 'roadrunner-1.4.0-windows-amd64.zip',
667 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.4.0/roadrunner-1.4.0-windows-amd64.zip',
668 | ],
669 | ],
670 | ],
671 | [
672 | 'name' => '1.3.7',
673 | 'version' => 'v1.3.7',
674 | 'assets' => [
675 | [
676 | 'name' => 'roadrunner-1.3.7-darwin-amd64.zip',
677 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.7/roadrunner-1.3.7-darwin-amd64.zip',
678 | ],
679 | [
680 | 'name' => 'roadrunner-1.3.7-freebsd-amd64.zip',
681 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.7/roadrunner-1.3.7-freebsd-amd64.zip',
682 | ],
683 | [
684 | 'name' => 'roadrunner-1.3.7-linux-amd64.tar.gz',
685 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.7/roadrunner-1.3.7-linux-amd64.tar.gz',
686 | ],
687 | [
688 | 'name' => 'roadrunner-1.3.7-windows-amd64.zip',
689 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.7/roadrunner-1.3.7-windows-amd64.zip',
690 | ],
691 | ],
692 | ],
693 | [
694 | 'name' => '1.3.6',
695 | 'version' => 'v1.3.6',
696 | 'assets' => [
697 | [
698 | 'name' => 'roadrunner-1.3.6-darwin-amd64.zip',
699 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.6/roadrunner-1.3.6-darwin-amd64.zip',
700 | ],
701 | [
702 | 'name' => 'roadrunner-1.3.6-freebsd-amd64.zip',
703 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.6/roadrunner-1.3.6-freebsd-amd64.zip',
704 | ],
705 | [
706 | 'name' => 'roadrunner-1.3.6-linux-amd64.tar.gz',
707 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.6/roadrunner-1.3.6-linux-amd64.tar.gz',
708 | ],
709 | [
710 | 'name' => 'roadrunner-1.3.6-windows-amd64.zip',
711 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.6/roadrunner-1.3.6-windows-amd64.zip',
712 | ],
713 | ],
714 | ],
715 | [
716 | 'name' => '1.3.5',
717 | 'version' => 'v1.3.5',
718 | 'assets' => [
719 | [
720 | 'name' => 'roadrunner-1.3.5-darwin-amd64.zip',
721 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.5/roadrunner-1.3.5-darwin-amd64.zip',
722 | ],
723 | [
724 | 'name' => 'roadrunner-1.3.5-freebsd-amd64.zip',
725 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.5/roadrunner-1.3.5-freebsd-amd64.zip',
726 | ],
727 | [
728 | 'name' => 'roadrunner-1.3.5-linux-amd64.tar.gz',
729 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.5/roadrunner-1.3.5-linux-amd64.tar.gz',
730 | ],
731 | [
732 | 'name' => 'roadrunner-1.3.5-windows-amd64.zip',
733 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.5/roadrunner-1.3.5-windows-amd64.zip',
734 | ],
735 | ],
736 | ],
737 | [
738 | 'name' => '1.3.4',
739 | 'version' => 'v1.3.4',
740 | 'assets' => [
741 | [
742 | 'name' => 'roadrunner-1.3.4-darwin-amd64.zip',
743 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.4/roadrunner-1.3.4-darwin-amd64.zip',
744 | ],
745 | [
746 | 'name' => 'roadrunner-1.3.4-freebsd-amd64.zip',
747 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.4/roadrunner-1.3.4-freebsd-amd64.zip',
748 | ],
749 | [
750 | 'name' => 'roadrunner-1.3.4-linux-amd64.tar.gz',
751 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.4/roadrunner-1.3.4-linux-amd64.tar.gz',
752 | ],
753 | [
754 | 'name' => 'roadrunner-1.3.4-windows-amd64.zip',
755 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.4/roadrunner-1.3.4-windows-amd64.zip',
756 | ],
757 | ],
758 | ],
759 | [
760 | 'name' => '1.3.3',
761 | 'version' => 'v1.3.3',
762 | 'assets' => [
763 | [
764 | 'name' => 'roadrunner-1.3.3-darwin-amd64.zip',
765 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.3/roadrunner-1.3.3-darwin-amd64.zip',
766 | ],
767 | [
768 | 'name' => 'roadrunner-1.3.3-freebsd-amd64.zip',
769 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.3/roadrunner-1.3.3-freebsd-amd64.zip',
770 | ],
771 | [
772 | 'name' => 'roadrunner-1.3.3-linux-amd64.tar.gz',
773 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.3/roadrunner-1.3.3-linux-amd64.tar.gz',
774 | ],
775 | [
776 | 'name' => 'roadrunner-1.3.3-windows-amd64.zip',
777 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.3/roadrunner-1.3.3-windows-amd64.zip',
778 | ],
779 | ],
780 | ],
781 | [
782 | 'name' => '1.3.2',
783 | 'version' => 'v1.3.2',
784 | 'assets' => [
785 | [
786 | 'name' => 'roadrunner-1.3.2-darwin-amd64.zip',
787 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.2/roadrunner-1.3.2-darwin-amd64.zip',
788 | ],
789 | [
790 | 'name' => 'roadrunner-1.3.2-freebsd-amd64.zip',
791 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.2/roadrunner-1.3.2-freebsd-amd64.zip',
792 | ],
793 | [
794 | 'name' => 'roadrunner-1.3.2-linux-amd64.tar.gz',
795 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.2/roadrunner-1.3.2-linux-amd64.tar.gz',
796 | ],
797 | [
798 | 'name' => 'roadrunner-1.3.2-windows-amd64.zip',
799 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.2/roadrunner-1.3.2-windows-amd64.zip',
800 | ],
801 | ],
802 | ],
803 | [
804 | 'name' => '1.3.1',
805 | 'version' => 'v1.3.1',
806 | 'assets' => [
807 | [
808 | 'name' => 'roadrunner-1.3.1-darwin-amd64.zip',
809 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.1/roadrunner-1.3.1-darwin-amd64.zip',
810 | ],
811 | [
812 | 'name' => 'roadrunner-1.3.1-freebsd-amd64.zip',
813 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.1/roadrunner-1.3.1-freebsd-amd64.zip',
814 | ],
815 | [
816 | 'name' => 'roadrunner-1.3.1-linux-amd64.tar.gz',
817 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.1/roadrunner-1.3.1-linux-amd64.tar.gz',
818 | ],
819 | [
820 | 'name' => 'roadrunner-1.3.1-windows-amd64.zip',
821 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.1/roadrunner-1.3.1-windows-amd64.zip',
822 | ],
823 | ],
824 | ],
825 | [
826 | 'name' => '1.3.0',
827 | 'version' => 'v1.3.0',
828 | 'assets' => [
829 | [
830 | 'name' => 'roadrunner-1.3.0-darwin-amd64.zip',
831 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.0/roadrunner-1.3.0-darwin-amd64.zip',
832 | ],
833 | [
834 | 'name' => 'roadrunner-1.3.0-freebsd-amd64.zip',
835 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.0/roadrunner-1.3.0-freebsd-amd64.zip',
836 | ],
837 | [
838 | 'name' => 'roadrunner-1.3.0-linux-amd64.tar.gz',
839 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.0/roadrunner-1.3.0-linux-amd64.tar.gz',
840 | ],
841 | [
842 | 'name' => 'roadrunner-1.3.0-windows-amd64.zip',
843 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.3.0/roadrunner-1.3.0-windows-amd64.zip',
844 | ],
845 | ],
846 | ],
847 | [
848 | 'name' => '1.2.8',
849 | 'version' => 'v1.2.8',
850 | 'assets' => [
851 | [
852 | 'name' => 'roadrunner-1.2.8-darwin-amd64.zip',
853 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.8/roadrunner-1.2.8-darwin-amd64.zip',
854 | ],
855 | [
856 | 'name' => 'roadrunner-1.2.8-freebsd-amd64.zip',
857 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.8/roadrunner-1.2.8-freebsd-amd64.zip',
858 | ],
859 | [
860 | 'name' => 'roadrunner-1.2.8-linux-amd64.tar.gz',
861 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.8/roadrunner-1.2.8-linux-amd64.tar.gz',
862 | ],
863 | [
864 | 'name' => 'roadrunner-1.2.8-windows-amd64.zip',
865 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.8/roadrunner-1.2.8-windows-amd64.zip',
866 | ],
867 | ],
868 | ],
869 | [
870 | 'name' => '1.2.7',
871 | 'version' => 'v1.2.7',
872 | 'assets' => [
873 | [
874 | 'name' => 'roadrunner-1.2.7-darwin-amd64.zip',
875 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.7/roadrunner-1.2.7-darwin-amd64.zip',
876 | ],
877 | [
878 | 'name' => 'roadrunner-1.2.7-freebsd-amd64.zip',
879 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.7/roadrunner-1.2.7-freebsd-amd64.zip',
880 | ],
881 | [
882 | 'name' => 'roadrunner-1.2.7-linux-amd64.tar.gz',
883 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.7/roadrunner-1.2.7-linux-amd64.tar.gz',
884 | ],
885 | [
886 | 'name' => 'roadrunner-1.2.7-windows-amd64.zip',
887 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.7/roadrunner-1.2.7-windows-amd64.zip',
888 | ],
889 | ],
890 | ],
891 | [
892 | 'name' => '1.2.6',
893 | 'version' => 'v1.2.6',
894 | 'assets' => [
895 | [
896 | 'name' => 'roadrunner-1.2.6-darwin-amd64.zip',
897 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.6/roadrunner-1.2.6-darwin-amd64.zip',
898 | ],
899 | [
900 | 'name' => 'roadrunner-1.2.6-freebsd-amd64.zip',
901 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.6/roadrunner-1.2.6-freebsd-amd64.zip',
902 | ],
903 | [
904 | 'name' => 'roadrunner-1.2.6-linux-amd64.tar.gz',
905 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.6/roadrunner-1.2.6-linux-amd64.tar.gz',
906 | ],
907 | [
908 | 'name' => 'roadrunner-1.2.6-windows-amd64.zip',
909 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.6/roadrunner-1.2.6-windows-amd64.zip',
910 | ],
911 | ],
912 | ],
913 | [
914 | 'name' => '1.2.5',
915 | 'version' => 'v1.2.5',
916 | 'assets' => [
917 | [
918 | 'name' => 'roadrunner-1.2.5-darwin-amd64.zip',
919 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.5/roadrunner-1.2.5-darwin-amd64.zip',
920 | ],
921 | [
922 | 'name' => 'roadrunner-1.2.5-freebsd-amd64.zip',
923 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.5/roadrunner-1.2.5-freebsd-amd64.zip',
924 | ],
925 | [
926 | 'name' => 'roadrunner-1.2.5-linux-amd64.tar.gz',
927 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.5/roadrunner-1.2.5-linux-amd64.tar.gz',
928 | ],
929 | [
930 | 'name' => 'roadrunner-1.2.5-windows-amd64.zip',
931 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.5/roadrunner-1.2.5-windows-amd64.zip',
932 | ],
933 | ],
934 | ],
935 | [
936 | 'name' => '1.2.4',
937 | 'version' => 'v1.2.4',
938 | 'assets' => [
939 | [
940 | 'name' => 'roadrunner-1.2.4-darwin-amd64.zip',
941 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.4/roadrunner-1.2.4-darwin-amd64.zip',
942 | ],
943 | [
944 | 'name' => 'roadrunner-1.2.4-freebsd-amd64.zip',
945 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.4/roadrunner-1.2.4-freebsd-amd64.zip',
946 | ],
947 | [
948 | 'name' => 'roadrunner-1.2.4-linux-amd64.tar.gz',
949 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.4/roadrunner-1.2.4-linux-amd64.tar.gz',
950 | ],
951 | [
952 | 'name' => 'roadrunner-1.2.4-windows-amd64.zip',
953 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.4/roadrunner-1.2.4-windows-amd64.zip',
954 | ],
955 | ],
956 | ],
957 | [
958 | 'name' => '1.2.3',
959 | 'version' => 'v1.2.3',
960 | 'assets' => [
961 | [
962 | 'name' => 'roadrunner-1.2.3-darwin-amd64.zip',
963 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.3/roadrunner-1.2.3-darwin-amd64.zip',
964 | ],
965 | [
966 | 'name' => 'roadrunner-1.2.3-freebsd-amd64.zip',
967 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.3/roadrunner-1.2.3-freebsd-amd64.zip',
968 | ],
969 | [
970 | 'name' => 'roadrunner-1.2.3-linux-amd64.tar.gz',
971 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.3/roadrunner-1.2.3-linux-amd64.tar.gz',
972 | ],
973 | [
974 | 'name' => 'roadrunner-1.2.3-windows-amd64.zip',
975 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.3/roadrunner-1.2.3-windows-amd64.zip',
976 | ],
977 | ],
978 | ],
979 | [
980 | 'name' => '1.2.2',
981 | 'version' => 'v1.2.2',
982 | 'assets' => [
983 | [
984 | 'name' => 'roadrunner-1.2.2-darwin-amd64.zip',
985 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.2/roadrunner-1.2.2-darwin-amd64.zip',
986 | ],
987 | [
988 | 'name' => 'roadrunner-1.2.2-freebsd-amd64.zip',
989 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.2/roadrunner-1.2.2-freebsd-amd64.zip',
990 | ],
991 | [
992 | 'name' => 'roadrunner-1.2.2-linux-amd64.tar.gz',
993 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.2/roadrunner-1.2.2-linux-amd64.tar.gz',
994 | ],
995 | [
996 | 'name' => 'roadrunner-1.2.2-windows-amd64.zip',
997 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.2/roadrunner-1.2.2-windows-amd64.zip',
998 | ],
999 | ],
1000 | ],
1001 | [
1002 | 'name' => '1.2.1',
1003 | 'version' => 'v1.2.1',
1004 | 'assets' => [
1005 | [
1006 | 'name' => 'roadrunner-1.2.1-darwin-amd64.zip',
1007 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.1/roadrunner-1.2.1-darwin-amd64.zip',
1008 | ],
1009 | [
1010 | 'name' => 'roadrunner-1.2.1-freebsd-amd64.zip',
1011 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.1/roadrunner-1.2.1-freebsd-amd64.zip',
1012 | ],
1013 | [
1014 | 'name' => 'roadrunner-1.2.1-linux-amd64.tar.gz',
1015 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.1/roadrunner-1.2.1-linux-amd64.tar.gz',
1016 | ],
1017 | [
1018 | 'name' => 'roadrunner-1.2.1-windows-amd64.zip',
1019 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.1/roadrunner-1.2.1-windows-amd64.zip',
1020 | ],
1021 | ],
1022 | ],
1023 | [
1024 | 'name' => '1.2.0',
1025 | 'version' => 'v1.2.0',
1026 | 'assets' => [
1027 | [
1028 | 'name' => 'roadrunner-1.2.0-darwin-amd64.zip',
1029 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.0/roadrunner-1.2.0-darwin-amd64.zip',
1030 | ],
1031 | [
1032 | 'name' => 'roadrunner-1.2.0-freebsd-amd64.zip',
1033 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.0/roadrunner-1.2.0-freebsd-amd64.zip',
1034 | ],
1035 | [
1036 | 'name' => 'roadrunner-1.2.0-linux-amd64.tar.gz',
1037 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.0/roadrunner-1.2.0-linux-amd64.tar.gz',
1038 | ],
1039 | [
1040 | 'name' => 'roadrunner-1.2.0-windows-amd64.zip',
1041 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.2.0/roadrunner-1.2.0-windows-amd64.zip',
1042 | ],
1043 | ],
1044 | ],
1045 | [
1046 | 'name' => '1.1.1',
1047 | 'version' => 'v1.1.1',
1048 | 'assets' => [
1049 | [
1050 | 'name' => 'roadrunner-1.1.1-darwin-amd64.zip',
1051 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.1/roadrunner-1.1.1-darwin-amd64.zip',
1052 | ],
1053 | [
1054 | 'name' => 'roadrunner-1.1.1-freebsd-amd64.zip',
1055 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.1/roadrunner-1.1.1-freebsd-amd64.zip',
1056 | ],
1057 | [
1058 | 'name' => 'roadrunner-1.1.1-linux-amd64.tar.gz',
1059 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.1/roadrunner-1.1.1-linux-amd64.tar.gz',
1060 | ],
1061 | [
1062 | 'name' => 'roadrunner-1.1.1-windows-amd64.zip',
1063 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.1/roadrunner-1.1.1-windows-amd64.zip',
1064 | ],
1065 | ],
1066 | ],
1067 | [
1068 | 'name' => '1.1.0',
1069 | 'version' => 'v1.1.0',
1070 | 'assets' => [
1071 | [
1072 | 'name' => 'roadrunner-1.1.0-darwin-amd64.zip',
1073 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.0/roadrunner-1.1.0-darwin-amd64.zip',
1074 | ],
1075 | [
1076 | 'name' => 'roadrunner-1.1.0-freebsd-amd64.zip',
1077 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.0/roadrunner-1.1.0-freebsd-amd64.zip',
1078 | ],
1079 | [
1080 | 'name' => 'roadrunner-1.1.0-linux-amd64.tar.gz',
1081 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.0/roadrunner-1.1.0-linux-amd64.tar.gz',
1082 | ],
1083 | [
1084 | 'name' => 'roadrunner-1.1.0-windows-amd64.zip',
1085 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.1.0/roadrunner-1.1.0-windows-amd64.zip',
1086 | ],
1087 | ],
1088 | ],
1089 | [
1090 | 'name' => '1.0.5',
1091 | 'version' => 'v1.0.5',
1092 | 'assets' => [
1093 | [
1094 | 'name' => 'roadrunner-1.0.5-darwin-amd64.zip',
1095 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.5/roadrunner-1.0.5-darwin-amd64.zip',
1096 | ],
1097 | [
1098 | 'name' => 'roadrunner-1.0.5-freebsd-amd64.zip',
1099 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.5/roadrunner-1.0.5-freebsd-amd64.zip',
1100 | ],
1101 | [
1102 | 'name' => 'roadrunner-1.0.5-linux-amd64.tar.gz',
1103 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.5/roadrunner-1.0.5-linux-amd64.tar.gz',
1104 | ],
1105 | [
1106 | 'name' => 'roadrunner-1.0.5-windows-amd64.zip',
1107 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.5/roadrunner-1.0.5-windows-amd64.zip',
1108 | ],
1109 | ],
1110 | ],
1111 | [
1112 | 'name' => '1.0.4',
1113 | 'version' => 'untagged-f7dbae972208eff54361',
1114 | 'assets' => [
1115 | [
1116 | 'name' => 'roadrunner-1.0.4-darwin-amd64.zip',
1117 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/untagged-f7dbae972208eff54361/roadrunner-1.0.4-darwin-amd64.zip',
1118 | ],
1119 | [
1120 | 'name' => 'roadrunner-1.0.4-freebsd-amd64.zip',
1121 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/untagged-f7dbae972208eff54361/roadrunner-1.0.4-freebsd-amd64.zip',
1122 | ],
1123 | [
1124 | 'name' => 'roadrunner-1.0.4-linux-amd64.tar.gz',
1125 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/untagged-f7dbae972208eff54361/roadrunner-1.0.4-linux-amd64.tar.gz',
1126 | ],
1127 | [
1128 | 'name' => 'roadrunner-1.0.4-windows-amd64.zip',
1129 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/untagged-f7dbae972208eff54361/roadrunner-1.0.4-windows-amd64.zip',
1130 | ],
1131 | ],
1132 | ],
1133 | [
1134 | 'name' => '1.0.3',
1135 | 'version' => 'v1.0.3',
1136 | 'assets' => [
1137 | [
1138 | 'name' => 'roadrunner-1.0.3-darwin-amd64.zip',
1139 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.3/roadrunner-1.0.3-darwin-amd64.zip',
1140 | ],
1141 | [
1142 | 'name' => 'roadrunner-1.0.3-freebsd-amd64.zip',
1143 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.3/roadrunner-1.0.3-freebsd-amd64.zip',
1144 | ],
1145 | [
1146 | 'name' => 'roadrunner-1.0.3-linux-amd64.tar.gz',
1147 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.3/roadrunner-1.0.3-linux-amd64.tar.gz',
1148 | ],
1149 | [
1150 | 'name' => 'roadrunner-1.0.3-windows-amd64.zip',
1151 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.3/roadrunner-1.0.3-windows-amd64.zip',
1152 | ],
1153 | ],
1154 | ],
1155 | [
1156 | 'name' => '1.0.2',
1157 | 'version' => 'v1.0.2',
1158 | 'assets' => [
1159 | [
1160 | 'name' => 'roadrunner-1.0.2-darwin-amd64.zip',
1161 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.2/roadrunner-1.0.2-darwin-amd64.zip',
1162 | ],
1163 | [
1164 | 'name' => 'roadrunner-1.0.2-freebsd-amd64.zip',
1165 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.2/roadrunner-1.0.2-freebsd-amd64.zip',
1166 | ],
1167 | [
1168 | 'name' => 'roadrunner-1.0.2-linux-amd64.tar.gz',
1169 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.2/roadrunner-1.0.2-linux-amd64.tar.gz',
1170 | ],
1171 | [
1172 | 'name' => 'roadrunner-1.0.2-windows-amd64.zip',
1173 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.2/roadrunner-1.0.2-windows-amd64.zip',
1174 | ],
1175 | ],
1176 | ],
1177 | [
1178 | 'name' => '1.0.1',
1179 | 'version' => 'v1.0.1',
1180 | 'assets' => [
1181 | [
1182 | 'name' => 'roadrunner-1.0.1-darwin-amd64.zip',
1183 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.1/roadrunner-1.0.1-darwin-amd64.zip',
1184 | ],
1185 | [
1186 | 'name' => 'roadrunner-1.0.1-freebsd-amd64.zip',
1187 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.1/roadrunner-1.0.1-freebsd-amd64.zip',
1188 | ],
1189 | [
1190 | 'name' => 'roadrunner-1.0.1-linux-amd64.tar.gz',
1191 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.1/roadrunner-1.0.1-linux-amd64.tar.gz',
1192 | ],
1193 | [
1194 | 'name' => 'roadrunner-1.0.1-windows-amd64.zip',
1195 | 'uri' => 'https://github.com/spiral/roadrunner/releases/download/v1.0.1/roadrunner-1.0.1-windows-amd64.zip',
1196 | ],
1197 | ],
1198 | ],
1199 | [
1200 | 'name' => '1.0.0',
1201 | 'version' => 'v1.0.0',
1202 | 'assets' => [],
1203 | ],
1204 | [
1205 | 'name' => '0.9.0',
1206 | 'version' => 'v0.9.0',
1207 | 'assets' => [],
1208 | ],
1209 | ];
1210 |
1211 | /**
1212 | * @var array
1213 | */
1214 | private array $releases = [];
1215 |
1216 | private HttpClientInterface $client;
1217 |
1218 | public function __construct(?HttpClientInterface $client = null)
1219 | {
1220 | $this->client = $client ?? HttpClient::create();
1221 |
1222 | foreach (self::RELEASES_DATA as $release) {
1223 | $this->releases[] = $this->createRelease($release);
1224 | }
1225 | }
1226 |
1227 | public function getName(): string
1228 | {
1229 | return 'spiral/roadrunner';
1230 | }
1231 |
1232 | public function getReleases(): ReleasesCollection
1233 | {
1234 | return new ReleasesCollection($this->releases);
1235 | }
1236 |
1237 | /**
1238 | * @param array{name: string, version: string, assets: array} $release
1239 | */
1240 | private function createRelease(array $release): ReleaseInterface
1241 | {
1242 | $assets = [];
1243 |
1244 | foreach ($release['assets'] as $asset) {
1245 | $assets[] = $this->createAsset($asset);
1246 | }
1247 |
1248 | return new GitHubRelease($this->client, $release['name'], $release['version'], $this->getName(), $assets);
1249 | }
1250 |
1251 | /**
1252 | * @param array{name: string, uri: string} $asset
1253 | */
1254 | private function createAsset(array $asset): AssetInterface
1255 | {
1256 | return new GitHubAsset($this->client, $asset['name'], $asset['uri']);
1257 | }
1258 | }
1259 |
--------------------------------------------------------------------------------
/src/VersionsCommand.php:
--------------------------------------------------------------------------------
1 | os = new OperatingSystemOption($this);
37 | $this->arch = new ArchitectureOption($this);
38 | $this->stability = new StabilityOption($this);
39 |
40 | $this->version = new class($this) extends VersionFilterOption {
41 | protected function default(): string
42 | {
43 | return '*';
44 | }
45 | };
46 | }
47 |
48 | public function getDescription(): string
49 | {
50 | return 'Returns a list of all available RoadRunner versions';
51 | }
52 |
53 | public function execute(InputInterface $input, OutputInterface $output): int
54 | {
55 | $io = $this->io($input, $output);
56 |
57 | $output->writeln('');
58 | $output->writeln(' Environment:');
59 | $output->writeln(\sprintf(' - Version: %s ', $this->version->get($input, $io)));
60 | $output->writeln(\sprintf(' - Stability: %s ', $this->stability->get($input, $io)));
61 | $output->writeln('');
62 |
63 | $rows = [];
64 |
65 | $versions = $this->getRepository()
66 | ->getReleases()
67 | ->sortByVersion()
68 | ->minimumStability($this->stability->get($input, $io))
69 | ->filter(static fn(ReleaseInterface $release): bool => \count($release->getAssets()) > 0)
70 | ->satisfies($this->version->get($input, $io))
71 | ;
72 |
73 | foreach ($versions as $version) {
74 | $rows[] = [
75 | $this->versionToString($version),
76 | $this->stabilityToString($version),
77 | $this->assetsToString($version),
78 | $this->compatibilityToString($version, $input, $io),
79 | ];
80 | }
81 |
82 | $io->table(['Release', 'Stability', 'Binaries', 'Compatibility'], $rows);
83 |
84 | return 0;
85 | }
86 |
87 | private function compatibilityToString(ReleaseInterface $release, InputInterface $input, StyleInterface $io): string
88 | {
89 | $template = ' ✖ > (reason: %s )';
90 |
91 | // Validate version
92 | if (! $release->satisfies(Version::constraint())) {
93 | return \sprintf($template, 'incompatible version');
94 | }
95 |
96 | // Validate assets
97 | $assets = $release->getAssets();
98 |
99 | if ($assets->empty()) {
100 | return \sprintf($template, 'no binaries');
101 | }
102 |
103 | // Validate OS
104 | $assets = $assets->whereOperatingSystem(
105 | $os = $this->os->get($input, $io),
106 | );
107 |
108 | if ($assets->empty()) {
109 | return \sprintf($template, 'no assembly for ' . $os);
110 | }
111 |
112 | // Validate architecture
113 | $assets = $assets->whereArchitecture(
114 | $arch = $this->arch->get($input, $io),
115 | );
116 |
117 | if ($assets->empty()) {
118 | return \sprintf($template, 'no assembly for ' . $arch);
119 | }
120 |
121 | return ' ✓ >';
122 | }
123 |
124 | private function versionToString(ReleaseInterface $release): string
125 | {
126 | return $release->getName();
127 | }
128 |
129 | private function stabilityToString(ReleaseInterface $release): string
130 | {
131 | $stability = $release->getStability();
132 |
133 | switch ($stability) {
134 | case Stability::STABILITY_STABLE:
135 | return " $stability >";
136 |
137 | case Stability::STABILITY_RC:
138 | return " $stability >";
139 |
140 | case Stability::STABILITY_BETA:
141 | return " $stability >";
142 |
143 | case Stability::STABILITY_ALPHA:
144 | return " $stability >";
145 |
146 | default:
147 | return " $stability >";
148 | }
149 | }
150 |
151 | private function assetsToString(ReleaseInterface $release): string
152 | {
153 | $count = $release->getAssets()
154 | ->count()
155 | ;
156 |
157 | if ($count > 0) {
158 | return \sprintf(' ✓ > (%d )', $count);
159 | }
160 |
161 | return ' ✖ >';
162 | }
163 | }
164 |
--------------------------------------------------------------------------------