From bbe439ab41cd9de80ade4811ef568b66a1a0703b Mon Sep 17 00:00:00 2001 From: Marius Klocke Date: Sun, 11 Feb 2024 13:13:15 +0100 Subject: [PATCH] Build an optional roadrunner flavoured docker image --- bin/rr-server.php | 12 +-- build.sh | 112 ++++++++++++++++++++-- docker/php/fpm/Dockerfile | 70 ++++++++++++++ docker/php/{ => fpm}/docker.conf | 0 docker/php/fpm/entrypoint.sh | 13 +++ docker/php/{ => fpm}/healthcheck.sh | 0 docker/php/{ => roadrunner}/Dockerfile | 6 +- docker/php/{ => roadrunner}/entrypoint.sh | 0 8 files changed, 198 insertions(+), 15 deletions(-) create mode 100644 docker/php/fpm/Dockerfile rename docker/php/{ => fpm}/docker.conf (100%) create mode 100644 docker/php/fpm/entrypoint.sh rename docker/php/{ => fpm}/healthcheck.sh (100%) rename docker/php/{ => roadrunner}/Dockerfile (95%) rename docker/php/{ => roadrunner}/entrypoint.sh (100%) diff --git a/bin/rr-server.php b/bin/rr-server.php index dbce62c2..091828a5 100644 --- a/bin/rr-server.php +++ b/bin/rr-server.php @@ -8,24 +8,24 @@ $app = new Application(); $worker = Worker::create(); -$factory = new Psr17Factory(); -$psr7 = new PSR7Worker($worker, $factory, $factory, $factory); +$psrFactory = new Psr17Factory(); +$psrWorker = new PSR7Worker($worker, $psrFactory, $psrFactory, $psrFactory); while (true) { try { - $request = $psr7->waitRequest(); + $request = $psrWorker->waitRequest(); if ($request === null) { break; } } catch (\Throwable $e) { - $psr7->respond($factory->createResponse(400)); + $psrWorker->respond($psrFactory->createResponse(400)); continue; } try { - $psr7->respond($app->handle($request)); + $psrWorker->respond($app->handle($request)); } catch (\Throwable $e) { - $psr7->respond($factory->createResponse(500)); + $psrWorker->respond($psrFactory->createResponse(500)); $worker->error((string)$e); } } diff --git a/build.sh b/build.sh index f867139a..92e649b9 100755 --- a/build.sh +++ b/build.sh @@ -10,7 +10,8 @@ if [[ -z "${REDIS_VERSION}" ]]; then REDIS_VERSION="6" fi -PHP_IMAGE="php:${PHP_VERSION}-cli-alpine" +MARIADB_IMAGE="mariadb:${MARIADB_VERSION}" +REDIS_IMAGE="redis:${REDIS_VERSION}-alpine" if [[ $GITHUB_REF == *"refs/tags"* ]]; then TAG=${GITHUB_REF##refs/tags/} @@ -20,15 +21,114 @@ fi TARGET_IMAGE="mklocke/liga-manager-api:${TAG}" +cleanup() { + echo "Removing containers ..." + docker rm -f php mariadb redis > /dev/null + echo "Removing network ..." + docker network rm build > /dev/null + echo "Cleanup completed" +} + +# Make sure we clean up containers and networks in case of error +trap cleanup EXIT + # Enable strict error handling set -e -echo "Pulling image ${PHP_IMAGE} ..." -docker pull --quiet ${PHP_IMAGE} +echo "Creating network ..." +docker network create build + +echo "Starting MariaDB container from ${MARIADB_IMAGE} ..." +docker run -d --name=mariadb --network=build --pull=always \ + -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \ + -e MYSQL_DATABASE=test \ + -e MYSQL_USER=test \ + -e MYSQL_PASSWORD=test \ + ${MARIADB_IMAGE} + +echo "Starting Redis container from ${REDIS_IMAGE} ..." +docker run -d --name=redis --network=build --pull=always ${REDIS_IMAGE} + echo "Building image ${TARGET_IMAGE} ..." DOCKER_BUILDKIT=1 docker build \ - -f docker/php/Dockerfile \ + -f docker/php/fpm/Dockerfile \ -t ${TARGET_IMAGE} \ - --build-arg PHP_IMAGE=$PHP_IMAGE \ + --build-arg PHP_VERSION=$PHP_VERSION \ + --build-arg APP_VERSION=$TAG \ + --cache-from ${TARGET_IMAGE} . \ + --pull + +echo "Building image ${TARGET_IMAGE}-rr ..." +DOCKER_BUILDKIT=1 docker build \ + -f docker/php/roadrunner/Dockerfile \ + -t "${TARGET_IMAGE}-rr" \ + --build-arg PHP_VERSION=$PHP_VERSION \ --build-arg APP_VERSION=$TAG \ - --cache-from ${TARGET_IMAGE} . + --cache-from "${TARGET_IMAGE}-rr" . \ + --pull + +echo "Starting container from image ${TARGET_IMAGE} ..." +docker run -d --name=php --network=build \ + -e ADMIN_EMAIL=admin@example.com \ + -e ADMIN_PASSWORD=123456 \ + -e LOG_LEVEL=warning \ + -e REDIS_HOST=redis \ + -e JWT_SECRET=a194be3811fc \ + -e MYSQL_HOST=mariadb \ + -e MYSQL_DATABASE=test \ + -e MYSQL_USER=test \ + -e MYSQL_PASSWORD=test \ + -v "$PWD/.git:/var/www/api/.git" \ + -v "$PWD/tests:/var/www/api/tests" \ + ${TARGET_IMAGE} + +attempt=0 +while [ $attempt -le 10 ]; do + attempt=$(( $attempt + 1 )) + echo "Waiting for PHP container to be become healthy ... Attempt $attempt" + if docker exec -t php docker-php-fpm-healthcheck > /dev/null ; then + echo "PHP container is healthy" + break + fi + sleep 2 +done + +echo "Install dev dependencies ..." +docker exec -t php composer install --no-cache --no-progress + +echo "Running deptrac ..." +docker exec -t php deptrac --no-progress --config-file=config/deptrac.yaml + +echo "Testing gdpr-dump config ..." +docker exec -t php gdpr-dump config/gdpr-dump.yml > /dev/null + +echo "Running phpunit tests ..." +docker exec -t php phpunit -c config/phpunit.xml --display-deprecations + +echo "Enabling xdebug ..." +docker exec -t -u root php docker-php-ext-enable xdebug + +echo "Running phpunit tests with coverage ..." +docker exec -t php phpunit -c config/phpunit.xml --coverage-clover clover.xml --display-deprecations + +if [[ -n "${UPLOAD_COVERAGE}" ]]; then + echo "Installing git ..." + docker exec -t -u root php apk add git + + echo "Applying fix for git's dubious ownership issue ..." + docker exec -t php git config --global --add safe.directory /var/www/api + + echo "Uploading coverage report to coveralls.io ..." + docker exec -t -e COVERALLS_RUN_LOCALLY -e COVERALLS_REPO_TOKEN php php-coveralls -v -x clover.xml -o coveralls.json +fi + +if [[ -n "${PUBLISH_IMAGE}" ]]; then + echo "Logging in to docker hub ..." + echo "$DOCKER_TOKEN" | docker login -u "$DOCKER_USER" --password-stdin + + echo "Pushing images to docker hub ..." + docker push --quiet ${TARGET_IMAGE} + docker push --quiet "${TARGET_IMAGE}-rr" +fi + +echo "Build completed" diff --git a/docker/php/fpm/Dockerfile b/docker/php/fpm/Dockerfile new file mode 100644 index 00000000..d67cdbf1 --- /dev/null +++ b/docker/php/fpm/Dockerfile @@ -0,0 +1,70 @@ +ARG PHP_VERSION +ARG APP_VERSION +FROM php:${PHP_VERSION}-fpm-alpine + +# Install packages & PHP extensions +RUN set -xe \ + && apk update \ + && apk upgrade \ + && apk add --no-cache fcgi gmp gnupg ncurses \ + && apk add --no-cache --virtual .build-deps ${PHPIZE_DEPS} gmp-dev linux-headers \ + && docker-php-ext-install bcmath gmp pcntl pdo_mysql opcache sockets > /tmp/ext-install.log 2>&1 || (cat /tmp/ext-install.log; exit 1) \ + && pecl channel-update pecl.php.net \ + && pecl install apcu > /dev/null \ + && pecl install redis > /dev/null \ + && pecl install xdebug > /dev/null \ + && docker-php-ext-enable apcu redis \ + && pecl clear-cache \ + && apk del .build-deps \ + && rm -rf /tmp/* /var/cache/* + +# Install PHAR tools +COPY docker/php/install-phar.sh /usr/local/bin/install-phar.sh +RUN chmod +x /usr/local/bin/install-phar.sh \ + && install-phar.sh /usr/local/bin/composer https://github.com/composer/composer/releases/download/2.6.6/composer.phar 72600201c73c7c4b218f1c0511b36d8537963e36aafa244757f52309f885b314 \ + && install-phar.sh /usr/local/bin/gdpr-dump https://github.com/Smile-SA/gdpr-dump/releases/download/4.0.2/gdpr-dump.phar 50266d719d3967d5e029102c351aa3796ebfbe5ff78b050468522a44c86cbfc6 \ + && rm /usr/local/bin/install-phar.sh + +# Configure PHP +COPY docker/php/fpm/docker.conf /usr/local/etc/php-fpm.d/docker.conf +COPY docker/php/php.ini /usr/local/etc/php/php.ini +COPY docker/php/fpm/healthcheck.sh /usr/local/bin/docker-php-fpm-healthcheck +RUN chmod +x /usr/local/bin/docker-php-fpm-healthcheck + +# Add entrypoint +COPY docker/php/fpm/entrypoint.sh /usr/local/bin/docker-php-entrypoint +RUN chmod +x /usr/local/bin/docker-php-entrypoint + +# Prepare logos directory +ENV APP_LOGOS_PATH="/var/www/logos" +RUN mkdir ${APP_LOGOS_PATH} + +# Prepare application directory +ENV APP_HOME="/var/www/api" +RUN mkdir ${APP_HOME} +WORKDIR ${APP_HOME} + +# Install dependencies first +ENV COMPOSER_ALLOW_SUPERUSER="1" +COPY composer.lock composer.json ./ +RUN composer install --optimize-autoloader --no-cache --no-dev --no-progress + +# Install own application sources +COPY templates templates/ +COPY src src/ +COPY public public/ +COPY migrations migrations/ +COPY config config/ +COPY bin bin/ + +# Apply version branding +RUN sed -i "s/VERSION = 'development';/VERSION = '$APP_VERSION';/" \ + src/Infrastructure/API/Application.php \ + src/Infrastructure/CLI/Application.php + +# Configure ownership and permissions +RUN chown www-data:www-data ${APP_LOGOS_PATH} && chmod +x bin/* + +ENV PATH="${PATH}:${APP_HOME}/bin:${APP_HOME}/vendor/bin" +HEALTHCHECK --interval=15s --timeout=1s CMD docker-php-fpm-healthcheck || exit 1 +CMD lima migrations:migrate -n && php-fpm diff --git a/docker/php/docker.conf b/docker/php/fpm/docker.conf similarity index 100% rename from docker/php/docker.conf rename to docker/php/fpm/docker.conf diff --git a/docker/php/fpm/entrypoint.sh b/docker/php/fpm/entrypoint.sh new file mode 100644 index 00000000..6845a1e6 --- /dev/null +++ b/docker/php/fpm/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -e + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- php-fpm "$@" +fi + +if [ -e /var/www/logos ]; then + chown www-data:www-data /var/www/logos +fi + +exec "$@" diff --git a/docker/php/healthcheck.sh b/docker/php/fpm/healthcheck.sh similarity index 100% rename from docker/php/healthcheck.sh rename to docker/php/fpm/healthcheck.sh diff --git a/docker/php/Dockerfile b/docker/php/roadrunner/Dockerfile similarity index 95% rename from docker/php/Dockerfile rename to docker/php/roadrunner/Dockerfile index 9061e60e..6e1bf78a 100644 --- a/docker/php/Dockerfile +++ b/docker/php/roadrunner/Dockerfile @@ -1,7 +1,7 @@ -ARG PHP_IMAGE +ARG PHP_VERSION ARG APP_VERSION FROM ghcr.io/roadrunner-server/roadrunner:2023.3.10 AS roadrunner -FROM ${PHP_IMAGE} +FROM php:${PHP_VERSION}-cli-alpine COPY --from=roadrunner /usr/bin/rr /usr/local/bin/rr @@ -32,7 +32,7 @@ RUN chmod +x /usr/local/bin/install-phar.sh \ COPY docker/php/php.ini /usr/local/etc/php/php.ini # Add entrypoint -COPY docker/php/entrypoint.sh /usr/local/bin/docker-php-entrypoint +COPY docker/php/roadrunner/entrypoint.sh /usr/local/bin/docker-php-entrypoint RUN chmod +x /usr/local/bin/docker-php-entrypoint # Configure RoadRunner diff --git a/docker/php/entrypoint.sh b/docker/php/roadrunner/entrypoint.sh similarity index 100% rename from docker/php/entrypoint.sh rename to docker/php/roadrunner/entrypoint.sh