From 4882bec11846717c2274078ce942d9829fbc3c5b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 21 Aug 2022 13:19:27 +0200 Subject: [PATCH 01/23] Added roadrunner to the project --- .gitignore | 2 + bin/roadrunner-worker.php | 31 +++++++++ composer.json | 4 +- config/autoload/mercure.local.php.dist | 2 +- config/autoload/url-shortener.local.php.dist | 8 ++- config/config.php | 5 +- config/roadrunner/.rr.dev.yml | 41 +++++++++++ config/roadrunner/.rr.yml | 32 +++++++++ data/infra/roadrunner.Dockerfile | 73 ++++++++++++++++++++ docker-compose.override.yml.dist | 6 ++ docker-compose.yml | 28 +++++++- 11 files changed, 224 insertions(+), 8 deletions(-) create mode 100644 bin/roadrunner-worker.php create mode 100644 config/roadrunner/.rr.dev.yml create mode 100644 config/roadrunner/.rr.yml create mode 100644 data/infra/roadrunner.Dockerfile diff --git a/.gitignore b/.gitignore index 933c25ee..4154e11b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea +bin/.rr.* +bin/rr build !docker/build composer.lock diff --git a/bin/roadrunner-worker.php b/bin/roadrunner-worker.php new file mode 100644 index 00000000..a8d3506a --- /dev/null +++ b/bin/roadrunner-worker.php @@ -0,0 +1,31 @@ +get(Application::class); + $worker = new PSR7Worker( + Worker::create(), + $container->get(ServerRequestFactoryInterface::class), + $container->get(StreamFactoryInterface::class), + $container->get(UploadedFileFactoryInterface::class), + ); + + while ($req = $worker->waitRequest()) { + try { + $worker->respond($app->handle($req)); + } catch (Throwable $throwable) { + $worker->getWorker()->error((string) $throwable); + } + } +})(); diff --git a/composer.json b/composer.json index 168418a5..f9d3e27a 100644 --- a/composer.json +++ b/composer.json @@ -44,11 +44,13 @@ "pugx/shortid-php": "^1.0", "ramsey/uuid": "^4.3", "shlinkio/shlink-common": "^5.0", - "shlinkio/shlink-config": "^2.0", + "shlinkio/shlink-config": "dev-main#24ccd64 as 2.1", "shlinkio/shlink-event-dispatcher": "^2.5", "shlinkio/shlink-importer": "^4.0", "shlinkio/shlink-installer": "^8.1", "shlinkio/shlink-ip-geolocation": "^3.0", + "spiral/roadrunner": "^2.11", + "spiral/roadrunner-jobs": "^2.3", "symfony/console": "^6.1", "symfony/filesystem": "^6.1", "symfony/lock": "^6.1", diff --git a/config/autoload/mercure.local.php.dist b/config/autoload/mercure.local.php.dist index b10ad86e..e818404b 100644 --- a/config/autoload/mercure.local.php.dist +++ b/config/autoload/mercure.local.php.dist @@ -7,7 +7,7 @@ return [ 'mercure' => [ 'public_hub_url' => 'http://localhost:8001', 'internal_hub_url' => 'http://shlink_mercure_proxy', - 'jwt_secret' => 'mercure_jwt_key', + 'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error', ], ]; diff --git a/config/autoload/url-shortener.local.php.dist b/config/autoload/url-shortener.local.php.dist index 0069ffa9..1fffcd0d 100644 --- a/config/autoload/url-shortener.local.php.dist +++ b/config/autoload/url-shortener.local.php.dist @@ -2,14 +2,18 @@ declare(strict_types=1); -$isSwoole = extension_loaded('openswoole'); +use function Shlinkio\Shlink\Config\swooleIsInstalled; return [ 'url_shortener' => [ 'domain' => [ 'schema' => 'http', - 'hostname' => sprintf('localhost:%s', $isSwoole ? '8080' : '8000'), + 'hostname' => sprintf('localhost:%s', match (true) { + swooleIsInstalled() => '8080', // Swoole + PHP_SAPI === 'cli' => '8800', // Roadrunner + default => '8000', // FPM + }), ], 'auto_resolve_titles' => true, // 'multi_segment_slugs_enabled' => true, diff --git a/config/config.php b/config/config.php index 6c38707d..2763d23d 100644 --- a/config/config.php +++ b/config/config.php @@ -13,10 +13,11 @@ use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider; use function class_exists; use function Shlinkio\Shlink\Config\env; +use function Shlinkio\Shlink\Config\swooleIsInstalled; use const PHP_SAPI; -$isCli = PHP_SAPI === 'cli'; +$enableSwoole = PHP_SAPI === 'cli' && swooleIsInstalled(); $isTestEnv = env('APP_ENV') === 'test'; return (new ConfigAggregator\ConfigAggregator([ @@ -26,7 +27,7 @@ return (new ConfigAggregator\ConfigAggregator([ Mezzio\ConfigProvider::class, Mezzio\Router\ConfigProvider::class, Mezzio\Router\FastRouteRouter\ConfigProvider::class, - $isCli && class_exists(Swoole\ConfigProvider::class) + $enableSwoole && class_exists(Swoole\ConfigProvider::class) ? Swoole\ConfigProvider::class : new ConfigAggregator\ArrayProvider([]), ProblemDetails\ConfigProvider::class, diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml new file mode 100644 index 00000000..af667e0a --- /dev/null +++ b/config/roadrunner/.rr.dev.yml @@ -0,0 +1,41 @@ +version: '2.7' + +server: + command: 'php ../../bin/roadrunner-worker.php' + +http: + address: '0.0.0.0:8080' + static: + dir: ../../public + forbid: + - .php + - .htaccess + pool: + num_workers: 1 + supervisor: + max_worker_memory: 100 + +jobs: + pool: + num_workers: 2 + max_worker_memory: 100 + consume: { } + +logs: + mode: development + channels: + http: + level: debug # Log all http requests, set to info to disable + server: + level: debug # Everything written to worker stderr is logged + metrics: + level: debug + +reload: + enabled: true + interval: 1s + patterns: ['.php', '.yml', '.yaml'] + services: + http: + dirs: ['.'] + recursive: true diff --git a/config/roadrunner/.rr.yml b/config/roadrunner/.rr.yml new file mode 100644 index 00000000..ebfe9673 --- /dev/null +++ b/config/roadrunner/.rr.yml @@ -0,0 +1,32 @@ +version: '2.7' + +server: + command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php' + +http: + address: '0.0.0.0:8080' + static: + dir: ../../public + forbid: + - .php + - .htaccess + pool: + num_workers: 16 # TODO Make configurable + supervisor: + max_worker_memory: 100 + +jobs: + pool: + num_workers: 16 # TODO Make configurable + max_worker_memory: 100 + consume: { } + +logs: + mode: production + channels: + http: + level: info # Log all http requests, set to info to disable + server: + level: debug # Everything written to worker stderr is logged + metrics: + level: error diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile new file mode 100644 index 00000000..7a68a7ae --- /dev/null +++ b/data/infra/roadrunner.Dockerfile @@ -0,0 +1,73 @@ +FROM php:8.1.9-alpine3.16 +MAINTAINER Alejandro Celaya + +ENV APCU_VERSION 5.1.21 +ENV PDO_SQLSRV_VERSION 5.10.1 +ENV MS_ODBC_SQL_VERSION 17.5.2.2 + +RUN apk update + +# Install common php extensions +RUN docker-php-ext-install pdo_mysql +RUN docker-php-ext-install calendar + +RUN apk add --no-cache oniguruma-dev +RUN docker-php-ext-install mbstring + +RUN apk add --no-cache sqlite-libs +RUN apk add --no-cache sqlite-dev +RUN docker-php-ext-install pdo_sqlite + +RUN apk add --no-cache icu-dev +RUN docker-php-ext-install intl + +RUN apk add --no-cache libzip-dev zlib-dev +RUN docker-php-ext-install zip + +RUN apk add --no-cache libpng-dev +RUN docker-php-ext-install gd + +RUN apk add --no-cache postgresql-dev +RUN docker-php-ext-install pdo_pgsql + +RUN docker-php-ext-install sockets +RUN docker-php-ext-install bcmath + +# Install APCu extension +ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz +RUN mkdir -p /usr/src/php/ext/apcu \ + && tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1 \ + && docker-php-ext-configure apcu \ + && docker-php-ext-install apcu \ + && rm /tmp/apcu.tar.gz \ + && rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \ + && echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini + +# Install pcov and sqlsrv driver +RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ + apk add --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ + apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \ + pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \ + docker-php-ext-enable pdo_sqlsrv pcov && \ + apk del .phpize-deps && \ + rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk + +# Install composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + +# Make home directory writable by anyone +RUN chmod 777 /home + +VOLUME /home/shlink +WORKDIR /home/shlink + +# Expose roadrunner port +EXPOSE 8080 + +CMD \ + # Install dependencies if the vendor dir does not exist + if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ + # Download roadrunner binary + if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --location bin/ && chmod +x bin/rr ; fi && \ + # This forces the app to be started every second until the exit code is 0 + until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done diff --git a/docker-compose.override.yml.dist b/docker-compose.override.yml.dist index 990d1b5d..1c5409c6 100644 --- a/docker-compose.override.yml.dist +++ b/docker-compose.override.yml.dist @@ -13,6 +13,12 @@ services: - /etc/passwd:/etc/passwd:ro - /etc/group:/etc/group:ro + shlink_roadrunner: + user: 1000:1000 + volumes: + - /etc/passwd:/etc/passwd:ro + - /etc/group:/etc/group:ro + shlink_db_mysql: user: 1000:1000 volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 739c0079..8293ab03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -73,6 +73,30 @@ services: extra_hosts: - 'host.docker.internal:host-gateway' + shlink_roadrunner: + container_name: shlink_roadrunner + build: + context: . + dockerfile: ./data/infra/roadrunner.Dockerfile + ports: + - "8800:8080" + volumes: + - ./:/home/shlink + - ./data/infra/php.ini:/usr/local/etc/php/php.ini + links: + - shlink_db_mysql + - shlink_db_postgres + - shlink_db_maria + - shlink_db_ms + - shlink_redis + - shlink_mercure + - shlink_mercure_proxy + - shlink_rabbitmq + environment: + LC_ALL: C + extra_hosts: + - 'host.docker.internal:host-gateway' + shlink_db_mysql: container_name: shlink_db_mysql image: mysql:5.7 @@ -144,8 +168,8 @@ services: - "3080:80" environment: SERVER_NAME: ":80" - MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key - MERCURE_SUBSCRIBER_JWT_KEY: mercure_jwt_key + MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error + MERCURE_SUBSCRIBER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000" shlink_rabbitmq: From e9ec32b3c3ba2e33c21d8c4e823323b6b74a196d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 26 Aug 2022 14:59:27 +0200 Subject: [PATCH 02/23] Added support to dispatch async event listeners as RoadRunner jobs --- .dockerignore | 1 + bin/roadrunner-worker.php | 32 ++++++++++++------- composer.json | 2 +- config/roadrunner/.rr.dev.yml | 18 ++++++++--- config/roadrunner/.rr.yml | 12 ++++++- .../Event/AbstractVisitEvent.php | 8 ++++- .../EventDispatcher/Event/ShortUrlCreated.php | 8 ++++- 7 files changed, 61 insertions(+), 20 deletions(-) diff --git a/.dockerignore b/.dockerignore index 870f3610..ac530ed3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,4 +22,5 @@ infection* **/test* build* **/.* +!config/roadrunner/.rr.yml bin/helper diff --git a/bin/roadrunner-worker.php b/bin/roadrunner-worker.php index a8d3506a..7d65f250 100644 --- a/bin/roadrunner-worker.php +++ b/bin/roadrunner-worker.php @@ -7,25 +7,33 @@ use Psr\Container\ContainerInterface; use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileFactoryInterface; +use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener; use Spiral\RoadRunner\Http\PSR7Worker; use Spiral\RoadRunner\Worker; (static function (): void { + $rrMode = getenv('RR_MODE'); /** @var ContainerInterface $container */ $container = include __DIR__ . '/../config/container.php'; - $app = $container->get(Application::class); - $worker = new PSR7Worker( - Worker::create(), - $container->get(ServerRequestFactoryInterface::class), - $container->get(StreamFactoryInterface::class), - $container->get(UploadedFileFactoryInterface::class), - ); - while ($req = $worker->waitRequest()) { - try { - $worker->respond($app->handle($req)); - } catch (Throwable $throwable) { - $worker->getWorker()->error((string) $throwable); + if ($rrMode === 'http') { + // This was spin-up as a web worker + $app = $container->get(Application::class); + $worker = new PSR7Worker( + Worker::create(), + $container->get(ServerRequestFactoryInterface::class), + $container->get(StreamFactoryInterface::class), + $container->get(UploadedFileFactoryInterface::class), + ); + + while ($req = $worker->waitRequest()) { + try { + $worker->respond($app->handle($req)); + } catch (Throwable $e) { + $worker->getWorker()->error((string) $e); + } } + } else { + $container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks(); } })(); diff --git a/composer.json b/composer.json index f9d3e27a..e549a8f6 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "ramsey/uuid": "^4.3", "shlinkio/shlink-common": "^5.0", "shlinkio/shlink-config": "dev-main#24ccd64 as 2.1", - "shlinkio/shlink-event-dispatcher": "^2.5", + "shlinkio/shlink-event-dispatcher": "dev-feature/roadrunner-support", "shlinkio/shlink-importer": "^4.0", "shlinkio/shlink-installer": "^8.1", "shlinkio/shlink-ip-geolocation": "^3.0", diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index af667e0a..389ed003 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -1,5 +1,8 @@ version: '2.7' +rpc: + listen: tcp://127.0.0.1:6001 + server: command: 'php ../../bin/roadrunner-worker.php' @@ -11,7 +14,7 @@ http: - .php - .htaccess pool: - num_workers: 1 + num_workers: 3 supervisor: max_worker_memory: 100 @@ -19,15 +22,22 @@ jobs: pool: num_workers: 2 max_worker_memory: 100 - consume: { } + timeout: 300 + consume: ['shlink'] + pipelines: + shlink: + driver: memory + config: + priority: 10 + prefetch: 10 logs: mode: development channels: http: - level: debug # Log all http requests, set to info to disable + level: debug server: - level: debug # Everything written to worker stderr is logged + level: debug metrics: level: debug diff --git a/config/roadrunner/.rr.yml b/config/roadrunner/.rr.yml index ebfe9673..032989b9 100644 --- a/config/roadrunner/.rr.yml +++ b/config/roadrunner/.rr.yml @@ -1,5 +1,8 @@ version: '2.7' +rpc: + listen: tcp://127.0.0.1:6001 + server: command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php' @@ -16,10 +19,17 @@ http: max_worker_memory: 100 jobs: + timeout: 300 pool: num_workers: 16 # TODO Make configurable max_worker_memory: 100 - consume: { } + consume: ['shlink'] + pipelines: + shlink: + driver: memory + config: + priority: 10 + prefetch: 10 logs: mode: production diff --git a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php index 6fadaa5d..0d41b7d1 100644 --- a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php +++ b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php @@ -5,8 +5,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\EventDispatcher\Event; use JsonSerializable; +use Shlinkio\Shlink\EventDispatcher\Util\JsonUnserializable; -abstract class AbstractVisitEvent implements JsonSerializable +abstract class AbstractVisitEvent implements JsonSerializable, JsonUnserializable { public function __construct(public readonly string $visitId) { @@ -16,4 +17,9 @@ abstract class AbstractVisitEvent implements JsonSerializable { return ['visitId' => $this->visitId]; } + + public static function fromPayload(array $payload): self + { + return new static($payload['visitId'] ?? ''); + } } diff --git a/module/Core/src/EventDispatcher/Event/ShortUrlCreated.php b/module/Core/src/EventDispatcher/Event/ShortUrlCreated.php index 9786808f..b6ab1a0c 100644 --- a/module/Core/src/EventDispatcher/Event/ShortUrlCreated.php +++ b/module/Core/src/EventDispatcher/Event/ShortUrlCreated.php @@ -5,8 +5,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\EventDispatcher\Event; use JsonSerializable; +use Shlinkio\Shlink\EventDispatcher\Util\JsonUnserializable; -final class ShortUrlCreated implements JsonSerializable +final class ShortUrlCreated implements JsonSerializable, JsonUnserializable { public function __construct(public readonly string $shortUrlId) { @@ -18,4 +19,9 @@ final class ShortUrlCreated implements JsonSerializable 'shortUrlId' => $this->shortUrlId, ]; } + + public static function fromPayload(array $payload): self + { + return new self($payload['shortUrlId'] ?? ''); + } } From 846802c00332661d5752c185c8a1ea311acb9201 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 26 Aug 2022 17:58:25 +0200 Subject: [PATCH 03/23] Slight improvements on RoadRunner config --- config/autoload/url-shortener.local.php.dist | 3 ++- config/roadrunner/.rr.dev.yml | 6 ++++-- .../EventDispatcher/Event/AbstractVisitEvent.php | 2 +- .../Core/src/EventDispatcher/Event/UrlVisited.php | 14 ++++++++++++-- module/Core/src/EventDispatcher/LocateVisit.php | 2 +- module/Core/src/Visit/VisitsTracker.php | 2 +- .../Core/test/EventDispatcher/LocateVisitTest.php | 2 +- .../DropDefaultDomainFromRequestMiddleware.php | 4 ++-- 8 files changed, 24 insertions(+), 11 deletions(-) diff --git a/config/autoload/url-shortener.local.php.dist b/config/autoload/url-shortener.local.php.dist index 1fffcd0d..1ed5a47e 100644 --- a/config/autoload/url-shortener.local.php.dist +++ b/config/autoload/url-shortener.local.php.dist @@ -2,6 +2,7 @@ declare(strict_types=1); +use function Shlinkio\Shlink\Config\env; use function Shlinkio\Shlink\Config\swooleIsInstalled; return [ @@ -10,8 +11,8 @@ return [ 'domain' => [ 'schema' => 'http', 'hostname' => sprintf('localhost:%s', match (true) { + PHP_SAPI === 'cli' && env('RR_MODE') !== null => '8800', // Roadrunner swooleIsInstalled() => '8080', // Swoole - PHP_SAPI === 'cli' => '8800', // Roadrunner default => '8000', // FPM }), ], diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index 389ed003..4136f195 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -42,10 +42,12 @@ logs: level: debug reload: - enabled: true interval: 1s patterns: ['.php', '.yml', '.yaml'] services: http: - dirs: ['.'] + dirs: ['../..'] + recursive: true + jobs: + dirs: ['../..'] recursive: true diff --git a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php index 0d41b7d1..907b3d9c 100644 --- a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php +++ b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php @@ -9,7 +9,7 @@ use Shlinkio\Shlink\EventDispatcher\Util\JsonUnserializable; abstract class AbstractVisitEvent implements JsonSerializable, JsonUnserializable { - public function __construct(public readonly string $visitId) + final public function __construct(public readonly string $visitId) { } diff --git a/module/Core/src/EventDispatcher/Event/UrlVisited.php b/module/Core/src/EventDispatcher/Event/UrlVisited.php index 02452a3e..c57d59d6 100644 --- a/module/Core/src/EventDispatcher/Event/UrlVisited.php +++ b/module/Core/src/EventDispatcher/Event/UrlVisited.php @@ -6,8 +6,18 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\Event; final class UrlVisited extends AbstractVisitEvent { - public function __construct(string $visitId, public readonly ?string $originalIpAddress = null) + private ?string $originalIpAddress = null; + + public static function withOriginalIpAddress(string $visitId, ?string $originalIpAddress): self { - parent::__construct($visitId); + $instance = new self($visitId); + $instance->originalIpAddress = $originalIpAddress; + + return $instance; + } + + public function originalIpAddress(): ?string + { + return $this->originalIpAddress; } } diff --git a/module/Core/src/EventDispatcher/LocateVisit.php b/module/Core/src/EventDispatcher/LocateVisit.php index 197ce9a0..8708fb8a 100644 --- a/module/Core/src/EventDispatcher/LocateVisit.php +++ b/module/Core/src/EventDispatcher/LocateVisit.php @@ -41,7 +41,7 @@ class LocateVisit return; } - $this->locateVisit($visitId, $shortUrlVisited->originalIpAddress, $visit); + $this->locateVisit($visitId, $shortUrlVisited->originalIpAddress(), $visit); $this->eventDispatcher->dispatch(new VisitLocated($visitId)); } diff --git a/module/Core/src/Visit/VisitsTracker.php b/module/Core/src/Visit/VisitsTracker.php index 3aef46df..585a0f86 100644 --- a/module/Core/src/Visit/VisitsTracker.php +++ b/module/Core/src/Visit/VisitsTracker.php @@ -72,6 +72,6 @@ class VisitsTracker implements VisitsTrackerInterface $this->em->persist($visit); $this->em->flush(); - $this->eventDispatcher->dispatch(new UrlVisited($visit->getId(), $visitor->remoteAddress)); + $this->eventDispatcher->dispatch(UrlVisited::withOriginalIpAddress($visit->getId(), $visitor->remoteAddress)); } } diff --git a/module/Core/test/EventDispatcher/LocateVisitTest.php b/module/Core/test/EventDispatcher/LocateVisitTest.php index 406e8146..09a8086d 100644 --- a/module/Core/test/EventDispatcher/LocateVisitTest.php +++ b/module/Core/test/EventDispatcher/LocateVisitTest.php @@ -193,7 +193,7 @@ class LocateVisitTest extends TestCase { $ipAddr = $originalIpAddress ?? $visit->getRemoteAddr(); $location = new Location('', '', '', '', 0.0, 0.0, ''); - $event = new UrlVisited('123', $originalIpAddress); + $event = UrlVisited::withOriginalIpAddress('123', $originalIpAddress); $findVisit = $this->em->find(Visit::class, '123')->willReturn($visit); $flush = $this->em->flush()->will(function (): void { diff --git a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php index 8eb98153..73a6ac69 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -11,14 +11,14 @@ use Psr\Http\Server\RequestHandlerInterface; class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface { - public function __construct(private string $defaultDomain) + public function __construct(private readonly string $defaultDomain) { } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { /** @var array $body */ - $body = $request->getParsedBody(); + $body = $request->getParsedBody() ?? []; $request = $request->withQueryParams($this->sanitizeDomainFromPayload($request->getQueryParams())) ->withParsedBody($this->sanitizeDomainFromPayload($body)); From 86159c5d8651c57978f939da98cf1e289aa33ee0 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 26 Aug 2022 19:17:10 +0200 Subject: [PATCH 04/23] Updated to latest event dispatcher lib --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1fe3b89c..bb99634e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A PHP-based self-hosted URL shortener that can be used to serve shortened URLs u - [Full documentation](#full-documentation) - [Docker image](#docker-image) -- [Self hosted](#self-hosted) +- [Self-hosted](#self-hosted) - [Download](#download) - [Configure](#configure) - [Using shlink](#using-shlink) diff --git a/composer.json b/composer.json index e549a8f6..0380442f 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "ramsey/uuid": "^4.3", "shlinkio/shlink-common": "^5.0", "shlinkio/shlink-config": "dev-main#24ccd64 as 2.1", - "shlinkio/shlink-event-dispatcher": "dev-feature/roadrunner-support", + "shlinkio/shlink-event-dispatcher": "dev-main#7bf5b58 as 2.6", "shlinkio/shlink-importer": "^4.0", "shlinkio/shlink-installer": "^8.1", "shlinkio/shlink-ip-geolocation": "^3.0", From c5b6d203f53698437623c47d262ac95e5ee43e3b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 08:01:57 +0200 Subject: [PATCH 05/23] Simplified RoadRunner worker, and fixed RoadRunner reloading config --- bin/roadrunner-worker.php | 15 ++++----------- composer.json | 2 +- config/autoload/dependencies.global.php | 19 +++++++++++++++++++ config/roadrunner/.rr.dev.yml | 4 ++-- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/bin/roadrunner-worker.php b/bin/roadrunner-worker.php index 7d65f250..c4a89a85 100644 --- a/bin/roadrunner-worker.php +++ b/bin/roadrunner-worker.php @@ -4,27 +4,20 @@ declare(strict_types=1); use Mezzio\Application; use Psr\Container\ContainerInterface; -use Psr\Http\Message\ServerRequestFactoryInterface; -use Psr\Http\Message\StreamFactoryInterface; -use Psr\Http\Message\UploadedFileFactoryInterface; use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener; use Spiral\RoadRunner\Http\PSR7Worker; -use Spiral\RoadRunner\Worker; + +use function Shlinkio\Shlink\Config\env; (static function (): void { - $rrMode = getenv('RR_MODE'); /** @var ContainerInterface $container */ $container = include __DIR__ . '/../config/container.php'; + $rrMode = env('RR_MODE'); if ($rrMode === 'http') { // This was spin-up as a web worker $app = $container->get(Application::class); - $worker = new PSR7Worker( - Worker::create(), - $container->get(ServerRequestFactoryInterface::class), - $container->get(StreamFactoryInterface::class), - $container->get(UploadedFileFactoryInterface::class), - ); + $worker = $container->get(PSR7Worker::class); while ($req = $worker->waitRequest()) { try { diff --git a/composer.json b/composer.json index 0380442f..0028e27f 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "ramsey/uuid": "^4.3", "shlinkio/shlink-common": "^5.0", "shlinkio/shlink-config": "dev-main#24ccd64 as 2.1", - "shlinkio/shlink-event-dispatcher": "dev-main#7bf5b58 as 2.6", + "shlinkio/shlink-event-dispatcher": "dev-main#48c0137 as 2.6", "shlinkio/shlink-importer": "^4.0", "shlinkio/shlink-installer": "^8.1", "shlinkio/shlink-ip-geolocation": "^3.0", diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index dbc553f1..657caffb 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -3,12 +3,22 @@ declare(strict_types=1); use GuzzleHttp\Client; +use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Mezzio\Container; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\ServerRequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\UploadedFileFactoryInterface; +use Spiral\RoadRunner\Http\PSR7Worker; +use Spiral\RoadRunner\WorkerInterface; return [ 'dependencies' => [ + 'factories' => [ + PSR7Worker::class => ConfigAbstractFactory::class, + ], + 'delegators' => [ Mezzio\Application::class => [ Container\ApplicationConfigInjectionDelegator::class, @@ -26,4 +36,13 @@ return [ ], ], + ConfigAbstractFactory::class => [ + PSR7Worker::class => [ + WorkerInterface::class, + ServerRequestFactoryInterface::class, + StreamFactoryInterface::class, + UploadedFileFactoryInterface::class, + ], + ], + ]; diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index 4136f195..23e9679f 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -46,8 +46,8 @@ reload: patterns: ['.php', '.yml', '.yaml'] services: http: - dirs: ['../..'] + dirs: ['../../bin', '../../config', '../../data/migrations', '../../docker', '../../module', '../../vendor'] recursive: true jobs: - dirs: ['../..'] + dirs: ['../../bin', '../../config', '../../data/migrations', '../../docker', '../../module', '../../vendor'] recursive: true From ca515998e4dbacadf5cc81745a738cae188517de Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:09:14 +0200 Subject: [PATCH 06/23] Added support to run API tests on roadrunner --- .gitignore | 1 + bin/test/run-api-tests.sh | 22 +++++++++++++++++----- composer.json | 1 + config/roadrunner/.rr.dev.yml | 2 +- config/test/bootstrap_api_tests.php | 6 +++--- config/test/constants.php | 4 ++-- config/test/test_config.global.php | 10 +++++----- indocker | 9 +++++++-- 8 files changed, 37 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 4154e11b..daea5f2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea bin/.rr.* bin/rr +config/roadrunner/.pid build !docker/build composer.lock diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index 3f6e27e6..1f61b681 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -2,24 +2,36 @@ export APP_ENV=test export DB_DRIVER=postgres export TEST_ENV=api +export TEST_RUNTIME=${TEST_RUNTIME:-"openswoole"} export GENERATE_COVERAGE=${GENERATE_COVERAGE:-"no"} # Reset logs +OUTPUT_LOGS=data/log/api-tests/output.log rm -rf data/log/api-tests mkdir data/log/api-tests -touch data/log/api-tests/output.log +touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution -vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -f echo 'Starting server...' -vendor/bin/laminas mezzio:swoole:start -d -sleep 2 +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:start -d +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ + -o=http.address=0.0.0.0:9999 \ + -o=logs.encoding=json \ + -o=logs.channels.http.encoding=json \ + -o=logs.channels.server.encoding=json \ + -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & +sleep 2 # Let's give the server a couple of seconds to start vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $* testsExitCode=$? -vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 # Exit this script with the same code as the tests. If tests failed, this script has to fail exit $testsExitCode diff --git a/composer.json b/composer.json index 0028e27f..54d180e3 100644 --- a/composer.json +++ b/composer.json @@ -122,6 +122,7 @@ "test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite", "test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite", "test:api": "bin/test/run-api-tests.sh", + "test:api:rr": "TEST_RUNTIME=rr bin/test/run-api-tests.sh", "test:api:ci": "GENERATE_COVERAGE=yes composer test:api", "test:api:pretty": "GENERATE_COVERAGE=pretty composer test:api", "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml", diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index 23e9679f..7f06fc9f 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -43,7 +43,7 @@ logs: reload: interval: 1s - patterns: ['.php', '.yml', '.yaml'] + patterns: ['.php'] services: http: dirs: ['../../bin', '../../config', '../../data/migrations', '../../docker', '../../module', '../../vendor'] diff --git a/config/test/bootstrap_api_tests.php b/config/test/bootstrap_api_tests.php index 52c9d4fb..bc119284 100644 --- a/config/test/bootstrap_api_tests.php +++ b/config/test/bootstrap_api_tests.php @@ -10,8 +10,8 @@ use Psr\Container\ContainerInterface; use function register_shutdown_function; use function sprintf; -use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST; -use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT; +use const ShlinkioTest\Shlink\API_TESTS_HOST; +use const ShlinkioTest\Shlink\API_TESTS_PORT; /** @var ContainerInterface $container */ $container = require __DIR__ . '/../container.php'; @@ -24,7 +24,7 @@ $httpClient = $container->get('shlink_test_api_client'); register_shutdown_function(function () use ($httpClient): void { $httpClient->request( 'GET', - sprintf('http://%s:%s/api-tests/stop-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT), + sprintf('http://%s:%s/api-tests/stop-coverage', API_TESTS_HOST, API_TESTS_PORT), ); }); diff --git a/config/test/constants.php b/config/test/constants.php index a2c880fc..c767abc9 100644 --- a/config/test/constants.php +++ b/config/test/constants.php @@ -4,5 +4,5 @@ declare(strict_types=1); namespace ShlinkioTest\Shlink; -const SWOOLE_TESTING_HOST = '127.0.0.1'; -const SWOOLE_TESTING_PORT = 9999; +const API_TESTS_HOST = '127.0.0.1'; +const API_TESTS_PORT = 9999; diff --git a/config/test/test_config.global.php b/config/test/test_config.global.php index 8998dd22..9b338d7a 100644 --- a/config/test/test_config.global.php +++ b/config/test/test_config.global.php @@ -34,8 +34,8 @@ use function Shlinkio\Shlink\Config\env; use function sprintf; use function sys_get_temp_dir; -use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST; -use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT; +use const ShlinkioTest\Shlink\API_TESTS_HOST; +use const ShlinkioTest\Shlink\API_TESTS_PORT; $isApiTest = env('TEST_ENV') === 'api'; $isCliTest = env('TEST_ENV') === 'cli'; @@ -136,8 +136,8 @@ return [ 'mezzio-swoole' => [ 'enable_coroutine' => false, 'swoole-http-server' => [ - 'host' => SWOOLE_TESTING_HOST, - 'port' => SWOOLE_TESTING_PORT, + 'host' => API_TESTS_HOST, + 'port' => API_TESTS_PORT, 'process-name' => 'shlink_test', 'options' => [ 'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid', @@ -188,7 +188,7 @@ return [ 'dependencies' => [ 'services' => [ 'shlink_test_api_client' => new Client([ - 'base_uri' => sprintf('http://%s:%s/', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT), + 'base_uri' => sprintf('http://%s:%s/', API_TESTS_HOST, API_TESTS_PORT), 'http_errors' => false, ]), ], diff --git a/indocker b/indocker index 789386ac..3dde53c4 100755 --- a/indocker +++ b/indocker @@ -1,8 +1,13 @@ #!/usr/bin/env bash # Run docker containers if they are not up yet -if ! [[ $(docker ps | grep shlink_swoole) ]]; then +if ! [[ $(docker ps | grep shlink) ]]; then docker-compose up -d fi -docker exec -it shlink_swoole /bin/sh -c "$*" +if [[ "$*" == *"test:api:rr"* ]]; then + # API tests should be run inside the RoadRunner container when the test runtime is RoadRunner + docker exec -it shlink_roadrunner /bin/sh -c "$*" +else + docker exec -it shlink_swoole /bin/sh -c "$*" +fi From 679bb8d357cfa99af2f743c08c6faa0eb8a45e27 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:15:58 +0200 Subject: [PATCH 07/23] Added API tests over roadrunner on CI --- .github/actions/ci-setup/action.yml | 2 +- .github/workflows/ci.yml | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/actions/ci-setup/action.yml b/.github/actions/ci-setup/action.yml index eb7c8979..78cbdf1c 100644 --- a/.github/actions/ci-setup/action.yml +++ b/.github/actions/ci-setup/action.yml @@ -11,7 +11,7 @@ inputs: required: true php-extensions: description: 'The PHP extensions to install' - required: true + required: false default: '' extensions-cache-key: description: 'The key used to cache PHP extensions. If empty value is provided, extension caching is disabled' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57985e8a..1fcebea2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,11 +29,27 @@ jobs: with: test-group: unit - api-tests: + api-tests-openswoole: uses: './.github/workflows/ci-tests.yml' with: test-group: api + api-tests-roadrunner: + runs-on: ubuntu-22.04 + strategy: + matrix: + php-version: [ '8.1' ] + steps: + - uses: actions/checkout@v3 + - run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ inputs.php-version }} + tools: composer + - run: composer install --no-interaction --prefer-dist + - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr + - run: composer test:api:rr + cli-tests: uses: './.github/workflows/ci-tests.yml' with: From d54a2bde0fd3f11ef5a5b9890526a4ce3e0b2935 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:18:46 +0200 Subject: [PATCH 08/23] Fixed reference to unknown job in CI workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fcebea2..804514db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,7 @@ jobs: api-mutation-tests: needs: - - api-tests + - api-tests-openswoole uses: './.github/workflows/ci-mutation-tests.yml' with: test-group: api From b7f7288a4b3a8fe3bff26753e941652469c8b0d5 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:19:55 +0200 Subject: [PATCH 09/23] Fixed reference to unknown job in CI workflow --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 804514db..60d3da2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,12 +29,12 @@ jobs: with: test-group: unit - api-tests-openswoole: + openswoole-api-tests: uses: './.github/workflows/ci-tests.yml' with: test-group: api - api-tests-roadrunner: + roadrunner-api-tests: runs-on: ubuntu-22.04 strategy: matrix: @@ -96,7 +96,7 @@ jobs: api-mutation-tests: needs: - - api-tests-openswoole + - openswoole-api-tests uses: './.github/workflows/ci-mutation-tests.yml' with: test-group: api @@ -111,7 +111,7 @@ jobs: upload-coverage: needs: - unit-tests - - api-tests + - openswoole-api-tests - cli-tests - sqlite-db-tests runs-on: ubuntu-22.04 From aca5804f989c938f625218bb486d9341530b5c49 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:21:17 +0200 Subject: [PATCH 10/23] Fixed usage of inputs instead of matrix on CI workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60d3da2d..709c9834 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: - run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres - uses: shivammathur/setup-php@v2 with: - php-version: ${{ inputs.php-version }} + php-version: ${{ matrix.php-version }} tools: composer - run: composer install --no-interaction --prefer-dist - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr From df70810aa64f2efed18e63c4f43158b3878750de Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:23:45 +0200 Subject: [PATCH 11/23] Ensured tests are run in bash in CI --- .github/workflows/ci-tests.yml | 1 + .github/workflows/ci.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index b7f7af98..3f3915c3 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -28,6 +28,7 @@ jobs: php-extensions: openswoole-4.11.1 extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }} - run: composer test:${{ inputs.test-group }}:ci + shell: bash - uses: actions/upload-artifact@v3 if: ${{ matrix.php-version == '8.1' }} with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 709c9834..857dbf59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: - run: composer install --no-interaction --prefer-dist - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr - run: composer test:api:rr + shell: bash cli-tests: uses: './.github/workflows/ci-tests.yml' From bfbeb7b1fb26515c88152a1a3b41d17b6e9395de Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:36:37 +0200 Subject: [PATCH 12/23] Improved run-api-tests.sh script to make it compatible with sh --- .github/workflows/ci-tests.yml | 1 - .github/workflows/ci.yml | 1 - bin/test/run-api-tests.sh | 35 +++++++++++++++++++++------------- config/config.php | 3 ++- indocker | 7 +------ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 3f3915c3..b7f7af98 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -28,7 +28,6 @@ jobs: php-extensions: openswoole-4.11.1 extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }} - run: composer test:${{ inputs.test-group }}:ci - shell: bash - uses: actions/upload-artifact@v3 if: ${{ matrix.php-version == '8.1' }} with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 857dbf59..709c9834 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,6 @@ jobs: - run: composer install --no-interaction --prefer-dist - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr - run: composer test:api:rr - shell: bash cli-tests: uses: './.github/workflows/ci-tests.yml' diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index 1f61b681..24652cb7 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -12,26 +12,35 @@ mkdir data/log/api-tests touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -f +if test "$TEST_RUNTIME" == 'openswoole'; then + vendor/bin/laminas mezzio:swoole:stop +elif test "$TEST_RUNTIME" == 'rr'; then + bin/rr stop -f +fi echo 'Starting server...' -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:start -d -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ - -o=http.address=0.0.0.0:9999 \ - -o=logs.encoding=json \ - -o=logs.channels.http.encoding=json \ - -o=logs.channels.server.encoding=json \ - -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ - -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ - -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & +if test "$TEST_RUNTIME" == 'openswoole'; then + vendor/bin/laminas mezzio:swoole:start -d +elif test "$TEST_RUNTIME" == 'rr'; then + bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ + -o=http.address=0.0.0.0:9999 \ + -o=logs.encoding=json \ + -o=logs.channels.http.encoding=json \ + -o=logs.channels.server.encoding=json \ + -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & +fi sleep 2 # Let's give the server a couple of seconds to start vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $* testsExitCode=$? -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 +if test "$TEST_RUNTIME" == 'openswoole'; then + vendor/bin/laminas mezzio:swoole:stop +elif test "$TEST_RUNTIME" == 'rr'; then + bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 +fi # Exit this script with the same code as the tests. If tests failed, this script has to fail exit $testsExitCode diff --git a/config/config.php b/config/config.php index 2763d23d..cacf9bcd 100644 --- a/config/config.php +++ b/config/config.php @@ -17,8 +17,9 @@ use function Shlinkio\Shlink\Config\swooleIsInstalled; use const PHP_SAPI; -$enableSwoole = PHP_SAPI === 'cli' && swooleIsInstalled(); $isTestEnv = env('APP_ENV') === 'test'; +$isRR = env('RR_MODE') !== null; +$enableSwoole = PHP_SAPI === 'cli' && ! $isRR && swooleIsInstalled(); return (new ConfigAggregator\ConfigAggregator([ ! $isTestEnv diff --git a/indocker b/indocker index 3dde53c4..03061e2f 100755 --- a/indocker +++ b/indocker @@ -5,9 +5,4 @@ if ! [[ $(docker ps | grep shlink) ]]; then docker-compose up -d fi -if [[ "$*" == *"test:api:rr"* ]]; then - # API tests should be run inside the RoadRunner container when the test runtime is RoadRunner - docker exec -it shlink_roadrunner /bin/sh -c "$*" -else - docker exec -it shlink_swoole /bin/sh -c "$*" -fi +docker exec -it shlink_swoole /bin/sh -c "$*" From 8260a0843ba9346b0efb7fd96e42178780e7f863 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:43:20 +0200 Subject: [PATCH 13/23] Undone changes for sh on API tests --- .github/workflows/ci-tests.yml | 1 + .github/workflows/ci.yml | 1 + bin/test/run-api-tests.sh | 38 +++++++++++++--------------------- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index b7f7af98..3f3915c3 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -28,6 +28,7 @@ jobs: php-extensions: openswoole-4.11.1 extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }} - run: composer test:${{ inputs.test-group }}:ci + shell: bash - uses: actions/upload-artifact@v3 if: ${{ matrix.php-version == '8.1' }} with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 709c9834..857dbf59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: - run: composer install --no-interaction --prefer-dist - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr - run: composer test:api:rr + shell: bash cli-tests: uses: './.github/workflows/ci-tests.yml' diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index 24652cb7..c115feac 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -1,8 +1,7 @@ -#!/usr/bin/env sh export APP_ENV=test -export DB_DRIVER=postgres export TEST_ENV=api export TEST_RUNTIME=${TEST_RUNTIME:-"openswoole"} +export DB_DRIVER=${DB_DRIVER:-"postgres"} export GENERATE_COVERAGE=${GENERATE_COVERAGE:-"no"} # Reset logs @@ -12,35 +11,26 @@ mkdir data/log/api-tests touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution -if test "$TEST_RUNTIME" == 'openswoole'; then - vendor/bin/laminas mezzio:swoole:stop -elif test "$TEST_RUNTIME" == 'rr'; then - bin/rr stop -f -fi +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -f echo 'Starting server...' -if test "$TEST_RUNTIME" == 'openswoole'; then - vendor/bin/laminas mezzio:swoole:start -d -elif test "$TEST_RUNTIME" == 'rr'; then - bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ - -o=http.address=0.0.0.0:9999 \ - -o=logs.encoding=json \ - -o=logs.channels.http.encoding=json \ - -o=logs.channels.server.encoding=json \ - -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ - -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ - -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & -fi +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:start -d +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ + -o=http.address=0.0.0.0:9999 \ + -o=logs.encoding=json \ + -o=logs.channels.http.encoding=json \ + -o=logs.channels.server.encoding=json \ + -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ + -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & sleep 2 # Let's give the server a couple of seconds to start vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $* testsExitCode=$? -if test "$TEST_RUNTIME" == 'openswoole'; then - vendor/bin/laminas mezzio:swoole:stop -elif test "$TEST_RUNTIME" == 'rr'; then - bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 -fi +[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop +[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 # Exit this script with the same code as the tests. If tests failed, this script has to fail exit $testsExitCode From f71c95b74a747a863209c796a4143e38ae0fb7f4 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:49:32 +0200 Subject: [PATCH 14/23] Another attempt to make API tests script sh compatible --- bin/test/run-api-tests.sh | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index c115feac..1cbf948a 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -1,8 +1,10 @@ +#!/usr/bin/env sh + export APP_ENV=test export TEST_ENV=api -export TEST_RUNTIME=${TEST_RUNTIME:-"openswoole"} -export DB_DRIVER=${DB_DRIVER:-"postgres"} -export GENERATE_COVERAGE=${GENERATE_COVERAGE:-"no"} +export TEST_RUNTIME="${TEST_RUNTIME:-"openswoole"}" +export DB_DRIVER="${DB_DRIVER:-"postgres"}" +export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" # Reset logs OUTPUT_LOGS=data/log/api-tests/output.log @@ -11,12 +13,12 @@ mkdir data/log/api-tests touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -f +[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop +[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f echo 'Starting server...' -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:start -d -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ +[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:start -d +[ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \ -o=http.address=0.0.0.0:9999 \ -o=logs.encoding=json \ -o=logs.channels.http.encoding=json \ @@ -29,8 +31,8 @@ sleep 2 # Let's give the server a couple of seconds to start vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $* testsExitCode=$? -[[ $TEST_RUNTIME == 'openswoole' ]] && vendor/bin/laminas mezzio:swoole:stop -[[ $TEST_RUNTIME == 'rr' ]] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 +[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop +[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999 # Exit this script with the same code as the tests. If tests failed, this script has to fail exit $testsExitCode From 529ddacafe71524163343764fcf52088595647f8 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 09:54:15 +0200 Subject: [PATCH 15/23] Removed usage of bash again from tests in CI, as it does nothing really --- .github/workflows/ci-tests.yml | 1 - .github/workflows/ci.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 3f3915c3..b7f7af98 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -28,7 +28,6 @@ jobs: php-extensions: openswoole-4.11.1 extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }} - run: composer test:${{ inputs.test-group }}:ci - shell: bash - uses: actions/upload-artifact@v3 if: ${{ matrix.php-version == '8.1' }} with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 857dbf59..709c9834 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,6 @@ jobs: - run: composer install --no-interaction --prefer-dist - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr - run: composer test:api:rr - shell: bash cli-tests: uses: './.github/workflows/ci-tests.yml' From a739eb6d60146d83bcec3bf1609597441ed6647e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 13:01:38 +0200 Subject: [PATCH 16/23] Added support to build the docker image with RoadRunner instead of openswoole --- .dockerignore | 2 +- Dockerfile | 22 +++++++++++++++----- composer.json | 2 +- config/autoload/url-shortener.local.php.dist | 10 ++++----- config/config.php | 7 ++++--- config/roadrunner/.rr.dev.yml | 4 ++-- docker/config/shlink_in_docker.local.php | 3 +++ docker/docker-entrypoint.sh | 10 ++++++--- 8 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.dockerignore b/.dockerignore index ac530ed3..beca6373 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ +bin/rr config/autoload/*local* data/infra data/cache/* @@ -23,4 +24,3 @@ infection* build* **/.* !config/roadrunner/.rr.yml -bin/helper diff --git a/Dockerfile b/Dockerfile index 2944db45..bc0f4fe2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,8 @@ FROM php:8.1.9-alpine3.16 as base ARG SHLINK_VERSION=latest ENV SHLINK_VERSION ${SHLINK_VERSION} +ARG SHLINK_RUNTIME=openswoole +ENV SHLINK_RUNTIME ${SHLINK_RUNTIME} ENV OPENSWOOLE_VERSION 4.11.1 ENV PDO_SQLSRV_VERSION 5.10.1 ENV MS_ODBC_SQL_VERSION 17.5.2.2 @@ -22,8 +24,10 @@ RUN \ # Install openswoole and sqlsrv driver for x86_64 builds RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \ - pecl install openswoole-${OPENSWOOLE_VERSION} && \ - docker-php-ext-enable openswoole && \ + if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \ + pecl install openswoole-${OPENSWOOLE_VERSION} && \ + docker-php-ext-enable openswoole ; \ + fi; \ if [ $(uname -m) == "x86_64" ]; then \ wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --no-cache --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ @@ -38,7 +42,12 @@ FROM base as builder COPY . . COPY --from=composer:2 /usr/bin/composer ./composer.phar RUN apk add --no-cache git && \ - php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \ + php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \ + if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \ + php composer.phar remove spiral/roadrunner spiral/roadrunner-jobs --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interactionc ; \ + elif [ $SHLINK_RUNTIME == 'rr' ]; then \ + php composer.phar remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interaction ; \ + fi; \ php composer.phar clear-cache && \ rm -r docker composer.* && \ sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php @@ -49,9 +58,12 @@ FROM base LABEL maintainer="Alejandro Celaya " COPY --from=builder /etc/shlink . -RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink +RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink && \ + if [ "$SHLINK_RUNTIME" == 'rr' ]; then \ + php ./vendor/bin/rr get --location bin/ && chmod +x bin/rr ; \ + fi; -# Expose default openswoole port +# Expose default port EXPOSE 8080 # Copy config specific for the image diff --git a/composer.json b/composer.json index 54d180e3..d8b2874f 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "pugx/shortid-php": "^1.0", "ramsey/uuid": "^4.3", "shlinkio/shlink-common": "^5.0", - "shlinkio/shlink-config": "dev-main#24ccd64 as 2.1", + "shlinkio/shlink-config": "dev-main#33004e6 as 2.1", "shlinkio/shlink-event-dispatcher": "dev-main#48c0137 as 2.6", "shlinkio/shlink-importer": "^4.0", "shlinkio/shlink-installer": "^8.1", diff --git a/config/autoload/url-shortener.local.php.dist b/config/autoload/url-shortener.local.php.dist index 1ed5a47e..f49570e1 100644 --- a/config/autoload/url-shortener.local.php.dist +++ b/config/autoload/url-shortener.local.php.dist @@ -2,8 +2,8 @@ declare(strict_types=1); -use function Shlinkio\Shlink\Config\env; -use function Shlinkio\Shlink\Config\swooleIsInstalled; +use function Shlinkio\Shlink\Config\runningInOpenswoole; +use function Shlinkio\Shlink\Config\runningInRoadRunner; return [ @@ -11,9 +11,9 @@ return [ 'domain' => [ 'schema' => 'http', 'hostname' => sprintf('localhost:%s', match (true) { - PHP_SAPI === 'cli' && env('RR_MODE') !== null => '8800', // Roadrunner - swooleIsInstalled() => '8080', // Swoole - default => '8000', // FPM + runningInRoadRunner() => '8800', + runningInOpenswoole() => '8080', + default => '8000', }), ], 'auto_resolve_titles' => true, diff --git a/config/config.php b/config/config.php index cacf9bcd..1c4fa40b 100644 --- a/config/config.php +++ b/config/config.php @@ -13,13 +13,14 @@ use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider; use function class_exists; use function Shlinkio\Shlink\Config\env; -use function Shlinkio\Shlink\Config\swooleIsInstalled; +use function Shlinkio\Shlink\Config\openswooleIsInstalled; +use function Shlinkio\Shlink\Config\runningInRoadRunner; use const PHP_SAPI; $isTestEnv = env('APP_ENV') === 'test'; -$isRR = env('RR_MODE') !== null; -$enableSwoole = PHP_SAPI === 'cli' && ! $isRR && swooleIsInstalled(); +// TODO Could check for actual openswoole with runningInOpenswoole()?? +$enableSwoole = PHP_SAPI === 'cli' && ! runningInRoadRunner() && openswooleIsInstalled(); return (new ConfigAggregator\ConfigAggregator([ ! $isTestEnv diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index 7f06fc9f..df6e2993 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -46,8 +46,8 @@ reload: patterns: ['.php'] services: http: - dirs: ['../../bin', '../../config', '../../data/migrations', '../../docker', '../../module', '../../vendor'] + dirs: ['../../bin', '../../config', '../../data/migrations', '../../module', '../../vendor'] recursive: true jobs: - dirs: ['../../bin', '../../config', '../../data/migrations', '../../docker', '../../module', '../../vendor'] + dirs: ['../../bin', '../../config', '../../data/migrations', '../../module', '../../vendor'] recursive: true diff --git a/docker/config/shlink_in_docker.local.php b/docker/config/shlink_in_docker.local.php index 4fba24b6..9dc99351 100644 --- a/docker/config/shlink_in_docker.local.php +++ b/docker/config/shlink_in_docker.local.php @@ -6,11 +6,14 @@ namespace Shlinkio\Shlink; use Shlinkio\Shlink\Common\Logger\LoggerType; +use function Shlinkio\Shlink\Config\runningInRoadRunner; + return [ 'logger' => [ 'Shlink' => [ 'type' => LoggerType::STREAM->value, + 'destination' => runningInRoadRunner() ? 'php://stderr' : 'php://stdout', ], ], diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index f1c4c495..c191afe7 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -31,6 +31,10 @@ if [ $ENABLE_PERIODIC_VISIT_LOCATE ]; then /usr/sbin/crond & fi -# When restarting the container, openswoole might think it is already in execution -# This forces the app to be started every second until the exit code is 0 -until php vendor/bin/laminas mezzio:swoole:start; do sleep 1 ; done +if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then + # When restarting the container, openswoole might think it is already in execution + # This forces the app to be started every second until the exit code is 0 + until php vendor/bin/laminas mezzio:swoole:start; do sleep 1 ; done +elif [ "$SHLINK_RUNTIME" == 'rr' ]; then + ./bin/rr serve -c config/roadrunner/.rr.yml +fi From 2099ea16ec853a99c7cdc1239d3d99ec5e5a9c3f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 13:14:27 +0200 Subject: [PATCH 17/23] Added stage to build docker images for roadrunner --- .github/workflows/docker-image-build.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image-build.yml b/.github/workflows/docker-image-build.yml index cbd8b213..11396f3f 100644 --- a/.github/workflows/docker-image-build.yml +++ b/.github/workflows/docker-image-build.yml @@ -8,9 +8,19 @@ on: - 'v*' jobs: - build: + build-openswool: uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main secrets: inherit with: image-name: shlinkio/shlink version-arg-name: SHLINK_VERSION + + build-roadrunner: + uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main + secrets: inherit + with: + image-name: shlinkio/shlink + version-arg-name: SHLINK_VERSION + tags-suffix: roadrunner + extra-build-args: | + SHLINK_RUNTIME=rr From 885273911154444fc55988cb50b6865157dda99d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 17:17:37 +0200 Subject: [PATCH 18/23] Ensured some dynamic RR config for prod env, based on env vars --- .github/workflows/ci.yml | 2 +- Dockerfile | 2 +- config/roadrunner/.rr.dev.yml | 13 ++++--------- config/roadrunner/.rr.yml | 17 ++++++----------- data/infra/roadrunner.Dockerfile | 2 +- docker/docker-entrypoint.sh | 5 +++++ 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 709c9834..354c4752 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: php-version: ${{ matrix.php-version }} tools: composer - run: composer install --no-interaction --prefer-dist - - run: ./vendor/bin/rr get --location bin/ && chmod +x bin/rr + - run: ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr - run: composer test:api:rr cli-tests: diff --git a/Dockerfile b/Dockerfile index bc0f4fe2..2835d75f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,7 +60,7 @@ LABEL maintainer="Alejandro Celaya " COPY --from=builder /etc/shlink . RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink && \ if [ "$SHLINK_RUNTIME" == 'rr' ]; then \ - php ./vendor/bin/rr get --location bin/ && chmod +x bin/rr ; \ + php ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr ; \ fi; # Expose default port diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index df6e2993..a12f2475 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -9,19 +9,14 @@ server: http: address: '0.0.0.0:8080' static: - dir: ../../public - forbid: - - .php - - .htaccess + dir: '../../public' + forbid: ['.php', '.htaccess'] pool: - num_workers: 3 - supervisor: - max_worker_memory: 100 + num_workers: 16 jobs: pool: - num_workers: 2 - max_worker_memory: 100 + num_workers: 16 timeout: 300 consume: ['shlink'] pipelines: diff --git a/config/roadrunner/.rr.yml b/config/roadrunner/.rr.yml index 032989b9..f7409c48 100644 --- a/config/roadrunner/.rr.yml +++ b/config/roadrunner/.rr.yml @@ -7,22 +7,17 @@ server: command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php' http: - address: '0.0.0.0:8080' + address: '0.0.0.0:${PORT}' static: - dir: ../../public - forbid: - - .php - - .htaccess + dir: '../../public' + forbid: ['.php', '.htaccess'] pool: - num_workers: 16 # TODO Make configurable - supervisor: - max_worker_memory: 100 + num_workers: ${WEB_WORKER_NUM} jobs: - timeout: 300 + timeout: 300 # 5 minutes pool: - num_workers: 16 # TODO Make configurable - max_worker_memory: 100 + num_workers: ${TASK_WORKER_NUM} consume: ['shlink'] pipelines: shlink: diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 7a68a7ae..8520b92d 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -68,6 +68,6 @@ CMD \ # Install dependencies if the vendor dir does not exist if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ # Download roadrunner binary - if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --location bin/ && chmod +x bin/rr ; fi && \ + if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr ; fi && \ # This forces the app to be started every second until the exit code is 0 until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index c191afe7..a1cc03be 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -31,6 +31,11 @@ if [ $ENABLE_PERIODIC_VISIT_LOCATE ]; then /usr/sbin/crond & fi +# RoadRunner config needs these to have been set, so falling back to default values if not set yet +export PORT="${PORT:-"8765"}" +export WEB_WORKER_NUM="${WEB_WORKER_NUM:-"16"}" +export TASK_WORKER_NUM="${TASK_WORKER_NUM:-"16"}" + if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then # When restarting the container, openswoole might think it is already in execution # This forces the app to be started every second until the exit code is 0 From a4f979be080d563419e9df378babf5af2fdd961f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 17:27:54 +0200 Subject: [PATCH 19/23] Enabled support for static files from public dir via RoadRunner --- config/config.php | 7 ++----- config/roadrunner/.rr.dev.yml | 1 + config/roadrunner/.rr.yml | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/config/config.php b/config/config.php index 1c4fa40b..088cad0a 100644 --- a/config/config.php +++ b/config/config.php @@ -13,14 +13,11 @@ use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider; use function class_exists; use function Shlinkio\Shlink\Config\env; -use function Shlinkio\Shlink\Config\openswooleIsInstalled; +use function Shlinkio\Shlink\Config\runningInOpenswoole; use function Shlinkio\Shlink\Config\runningInRoadRunner; -use const PHP_SAPI; - $isTestEnv = env('APP_ENV') === 'test'; -// TODO Could check for actual openswoole with runningInOpenswoole()?? -$enableSwoole = PHP_SAPI === 'cli' && ! runningInRoadRunner() && openswooleIsInstalled(); +$enableSwoole = ! runningInRoadRunner() && runningInOpenswoole(); return (new ConfigAggregator\ConfigAggregator([ ! $isTestEnv diff --git a/config/roadrunner/.rr.dev.yml b/config/roadrunner/.rr.dev.yml index a12f2475..7adf4520 100644 --- a/config/roadrunner/.rr.dev.yml +++ b/config/roadrunner/.rr.dev.yml @@ -8,6 +8,7 @@ server: http: address: '0.0.0.0:8080' + middleware: ['static'] static: dir: '../../public' forbid: ['.php', '.htaccess'] diff --git a/config/roadrunner/.rr.yml b/config/roadrunner/.rr.yml index f7409c48..40c93b2b 100644 --- a/config/roadrunner/.rr.yml +++ b/config/roadrunner/.rr.yml @@ -8,6 +8,7 @@ server: http: address: '0.0.0.0:${PORT}' + middleware: ['static'] static: dir: '../../public' forbid: ['.php', '.htaccess'] From 7b637d6a616d13a305369a3d2c95afa1506a21d5 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 17:48:59 +0200 Subject: [PATCH 20/23] Ensured RoadRunner deps are removed when building openswoole dist file --- build.sh | 6 +++++- config/roadrunner/.rr.yml | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index e274210a..219ccd2d 100755 --- a/build.sh +++ b/build.sh @@ -24,18 +24,22 @@ rsync -av * "${builtContent}" \ --exclude=*docker* \ --exclude=Dockerfile \ --include=.htaccess \ + --include=config/roadrunner/.rr.yml \ --exclude-from=./.dockerignore cd "${builtContent}" # Install dependencies echo "Installing dependencies with $composerBin..." -composerFlags="--optimize-autoloader --no-progress --no-interaction" +composerFlags="--optimize-autoloader --no-progress --no-interaction --ignore-platform-reqs" ${composerBin} self-update ${composerBin} install --no-dev --prefer-dist $composerFlags if [[ $noSwoole ]]; then # If generating a dist not for openswoole, uninstall mezzio-swoole ${composerBin} remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev $composerFlags +else + # If generating a dist for openswoole, uninstall RoadRunner + ${composerBin} remove spiral/roadrunner spiral/roadrunner-jobs --with-all-dependencies --update-no-dev $composerFlags fi # Delete development files diff --git a/config/roadrunner/.rr.yml b/config/roadrunner/.rr.yml index 40c93b2b..d44801ee 100644 --- a/config/roadrunner/.rr.yml +++ b/config/roadrunner/.rr.yml @@ -34,5 +34,3 @@ logs: level: info # Log all http requests, set to info to disable server: level: debug # Everything written to worker stderr is logged - metrics: - level: error From 7daa60263097de1db4310b91ccfb42a27ed4f0ef Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 17:51:14 +0200 Subject: [PATCH 21/23] Removed accidental flag in build script --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 219ccd2d..d9cda64d 100755 --- a/build.sh +++ b/build.sh @@ -30,7 +30,7 @@ cd "${builtContent}" # Install dependencies echo "Installing dependencies with $composerBin..." -composerFlags="--optimize-autoloader --no-progress --no-interaction --ignore-platform-reqs" +composerFlags="--optimize-autoloader --no-progress --no-interaction" ${composerBin} self-update ${composerBin} install --no-dev --prefer-dist $composerFlags From 2cf21ab3bd5cd828d197568a951184ec31d7a676 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 19:38:05 +0200 Subject: [PATCH 22/23] Fixed openswoole E2E tests --- .github/workflows/ci.yml | 10 +++++----- config/config.php | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 354c4752..f9d3660e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,11 @@ jobs: with: test-group: unit + cli-tests: + uses: './.github/workflows/ci-tests.yml' + with: + test-group: cli + openswoole-api-tests: uses: './.github/workflows/ci-tests.yml' with: @@ -50,11 +55,6 @@ jobs: - run: ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr - run: composer test:api:rr - cli-tests: - uses: './.github/workflows/ci-tests.yml' - with: - test-group: cli - sqlite-db-tests: uses: './.github/workflows/ci-db-tests.yml' with: diff --git a/config/config.php b/config/config.php index 088cad0a..4f4a1359 100644 --- a/config/config.php +++ b/config/config.php @@ -13,11 +13,12 @@ use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider; use function class_exists; use function Shlinkio\Shlink\Config\env; -use function Shlinkio\Shlink\Config\runningInOpenswoole; use function Shlinkio\Shlink\Config\runningInRoadRunner; +use const PHP_SAPI; + $isTestEnv = env('APP_ENV') === 'test'; -$enableSwoole = ! runningInRoadRunner() && runningInOpenswoole(); +$enableSwoole = PHP_SAPI === 'cli' && ! runningInRoadRunner(); return (new ConfigAggregator\ConfigAggregator([ ! $isTestEnv From f97effcfe080471614400a440c8457b50697858d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Aug 2022 19:40:52 +0200 Subject: [PATCH 23/23] Fixed rr E2E tests --- config/config.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/config.php b/config/config.php index 4f4a1359..15a45348 100644 --- a/config/config.php +++ b/config/config.php @@ -13,12 +13,13 @@ use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider; use function class_exists; use function Shlinkio\Shlink\Config\env; +use function Shlinkio\Shlink\Config\openswooleIsInstalled; use function Shlinkio\Shlink\Config\runningInRoadRunner; use const PHP_SAPI; $isTestEnv = env('APP_ENV') === 'test'; -$enableSwoole = PHP_SAPI === 'cli' && ! runningInRoadRunner(); +$enableSwoole = PHP_SAPI === 'cli' && openswooleIsInstalled() && ! runningInRoadRunner(); return (new ConfigAggregator\ConfigAggregator([ ! $isTestEnv