From 9f398ee012a7b2c6da3112e067944e37b39f7a9b Mon Sep 17 00:00:00 2001
From: Oliver Davies <oliver@oliverdavies.uk>
Date: Sat, 21 Jan 2023 15:50:45 +0000
Subject: [PATCH] Update Docker configuration

---
 .env.example                                  |  4 ++
 .gitignore                                    |  3 +
 Dockerfile                                    | 64 ++++++++---------
 build.yaml                                    | 52 ++++++++++++++
 docker-compose.yaml                           | 70 +++++++++++++++----
 phpcs.xml.dist                                |  7 ++
 phpstan.neon.dist                             |  5 ++
 phpunit.xml.dist                              | 20 ++++++
 run                                           |  4 +-
 .../nginx/root/etc/nginx/conf.d/default.conf  | 20 ++++++
 .../root/usr/local/bin/docker-entrypoint-php  |  5 ++
 .../web/root/etc/nginx/conf.d/default.conf    | 20 ++++++
 12 files changed, 224 insertions(+), 50 deletions(-)
 create mode 100644 .env.example
 create mode 100644 build.yaml
 create mode 100644 phpcs.xml.dist
 create mode 100644 phpstan.neon.dist
 create mode 100644 phpunit.xml.dist
 create mode 100644 tools/docker/images/nginx/root/etc/nginx/conf.d/default.conf
 create mode 100755 tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php
 create mode 100644 tools/docker/images/web/root/etc/nginx/conf.d/default.conf

diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..01b5668
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,4 @@
+export COMPOSE_PROJECT_NAME=docker-example-drupal
+export COMPOSE_PROFILES=web,php,database
+
+export DOCKER_WEB_VOLUME=.:/app
diff --git a/.gitignore b/.gitignore
index 94e2ede..2df2c1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 /.editorconfig
+/.env
 /.gitattributes
 /vendor/
 /web/.csslintrc
@@ -13,6 +14,7 @@
 /web/example.gitignore
 /web/index.php
 /web/modules/README.txt
+/web/modules/contrib/
 /web/profiles/README.txt
 /web/robots.txt
 /web/sites/*/files/
@@ -27,5 +29,6 @@
 /web/sites/example.sites.php
 /web/sites/simpletest/
 /web/themes/README.txt
+/web/themes/contrib/
 /web/update.php
 /web/web.config
diff --git a/Dockerfile b/Dockerfile
index 98c7b78..fb10f5d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,52 +1,50 @@
-ARG PHP_VERSION=8.1
+FROM php:8.1-fpm-bullseye AS base
 
-FROM php:${PHP_VERSION}-fpm-bullseye AS base
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+RUN which composer && composer -V
 
-###
+WORKDIR /app
+
+ENV PATH="${PATH}:/app/vendor/bin"
+
+COPY composer.* ./
+
+################################################################################
 
 FROM base AS build
 
-ENV PATH=${PATH}:/var/www/html/vendor/bin
+RUN apt-get update -yqq \
+  && apt-get install -yqq --no-install-recommends \
+    git libpng-dev libzip-dev mariadb-client unzip
 
-COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+RUN docker-php-ext-install gd pdo_mysql zip
 
-WORKDIR /var/www/html
 
-RUN useradd --create-home app \
-  && chown app:app -R /var/www/html \
-  && apt-get update -yqq \
-  && apt-get install -yqq \
-    git \
-    libpng-dev \
-    mariadb-client \
-    unzip \
-  && rm -fr /var/lib/apt/lists/* \
-  && pecl install xdebug \
-  && docker-php-ext-install \
-    gd \
-    opcache \
-    pdo_mysql \
-  && docker-php-ext-enable \
-    xdebug
+RUN composer validate
+RUN composer install
 
 COPY tools/docker/images/php/root /
 
-COPY --chown=app:app composer.* ./
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint-php"]
+CMD ["php-fpm"]
 
-USER app
-RUN composer install --no-dev --prefer-dist --optimize-autoloader
+################################################################################
 
-COPY --chown=app:app . .
+FROM base AS test
 
-###
+COPY . .
 
-FROM build AS test
+RUN parallel-lint src --no-progress \
+  && phpcs -vv \
+  && phpstan \
+  && phpunit --testdox
 
-# RUN composer install \
-#   && phpunit
+################################################################################
 
-###
+FROM nginx:1 as web
 
-FROM build AS production
+EXPOSE 8080
 
-RUN composer install --no-dev --prefer-dist --optimize-autoloader
+WORKDIR /app
+
+COPY tools/docker/images/web/root /
diff --git a/build.yaml b/build.yaml
new file mode 100644
index 0000000..51b80d6
--- /dev/null
+++ b/build.yaml
@@ -0,0 +1,52 @@
+name: docker-example-drupal
+language: php
+type: drupal-project
+
+web:
+  type: nginx
+
+database:
+  type: mariadb
+  version: 10
+
+php:
+  version: 8.1-fpm-bullseye
+  phpcs:
+    standard: Drupal,DrupalPractice
+  phpstan:
+    level: max
+
+docker-compose:
+  services:
+    database: ~
+    php:
+      build:
+        target: build
+
+dockerfile:
+  stages:
+    build:
+      extends: base
+      packages:
+        - git
+        - libpng-dev
+        - libzip-dev
+        - mariadb-client
+        - unzip
+      extensions:
+        install:
+          - gd
+          - pdo_mysql
+          - zip
+      commands:
+        - composer validate
+        - composer install
+
+    test:
+      extends: base
+      commands:
+        - parallel-lint src --no-progress
+        - phpcs -vv
+        - phpstan
+        - phpunit --testdox
+
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 960ae7e..4c51799 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,30 +1,70 @@
+x-app: &default-app
+  volumes:
+    - "${DOCKER_WEB_VOLUME:-./web:/app/web}"
+  env_file:
+    - .env
+  restart: "${DOCKER_RESTART_POLICY:-unless-stopped}"
+  networks:
+    - default
+  deploy:
+    resources:
+      limits:
+        cpus: "${DOCKER_MYSQL_CPUS:-0}"
+        memory: "${DOCKER_MYSQL_MEMORY:-0}"
+  labels:
+    - "traefik.enabled=false"
+  tty: true
+
 services:
   web:
-    image: caddy
-    volumes:
-      - ./Caddyfile:/etc/caddy/Caddyfile
-      - .:/var/www/html
-    ports:
-      - "${DOCKER_WEB_PORT:-127.0.0.1:80}:80"
-    environment:
-      SERVER_NAME: "${SERVER_NAME:-:80}"
-
-  php-fpm:
-    image: "ghcr.io/opdavies/docker-drupal-example-php-fpm:${DOCKER_TAG:-latest}"
+    <<: *default-app
     build:
       context: .
-    volumes:
-      - .:/var/www/html
+      target: web
+    depends_on:
+      - php
+    networks:
+      - default
+      - web
+    labels:
+      - "traefik.docker.network=traefik_proxy"
+      - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(
+          `${COMPOSE_PROJECT_NAME}.localhost`,
+          )"
+    profiles: [web]
 
-  mysql:
+  php:
+    <<: *default-app
+    build:
+      context: .
+      target: build
+    depends_on:
+      - database
+    profiles: [php]
+
+  database:
     image: mariadb:10
+    deploy:
+      resources:
+        limits:
+          cpus: "${DOCKER_MYSQL_CPUS:-0}"
+          memory: "${DOCKER_MYSQL_MEMORY:-0}"
     volumes:
       - db-data:/var/lib/mysql
+    env_file:
+      - .env
+    labels:
+      - "traefik.enabled=false"
     environment:
+      MYSQL_RANDOM_ROOT_PASSWORD: true
       MYSQL_DATABASE: drupal
-      MYSQL_RANDOM_ROOT_PASSWORD: 'true'
       MYSQL_PASSWORD: drupal
       MYSQL_USER: drupal
+    profiles: [database]
 
 volumes:
   db-data: {}
+
+networks:
+  web:
+    name: traefik_proxy
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..4fcef96
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<ruleset name="docker-example-drupal coding standards">
+   <description>PHPCS configuration file for docker-example-drupal.</description>
+   <file>src</file>
+   <arg value="np"/>
+   <rule ref="Drupal,DrupalPractice"/>
+</ruleset>
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 0000000..76bd437
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,5 @@
+parameters:
+  level: max
+  paths:
+    - src
+    
\ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..76faa41
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
+         bootstrap="vendor/autoload.php"
+         cacheResultFile=".phpunit.cache/test-results"
+         executionOrder="depends,defects"
+         forceCoversAnnotation="false"
+         beStrictAboutCoversAnnotation="true"
+         beStrictAboutOutputDuringTests="false"
+         beStrictAboutTodoAnnotatedTests="true"
+         convertDeprecationsToExceptions="true"
+         failOnRisky="true"
+         failOnWarning="true"
+         verbose="true">
+    <testsuites>
+        <testsuite name="docker-example-drupal">
+            <directory>tests</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>
diff --git a/run b/run
index 1f2c7f8..5650997 100755
--- a/run
+++ b/run
@@ -3,7 +3,7 @@
 set -e
 
 function cmd {
-  _dc php-fpm "${@}"
+  _dc php "${@}"
 }
 
 function composer {
@@ -23,7 +23,7 @@ function help {
 }
 
 function _dc {
-  docker-compose run --rm "${@}"
+  docker compose run --rm "${@}"
 }
 
 eval "${@:-help}"
diff --git a/tools/docker/images/nginx/root/etc/nginx/conf.d/default.conf b/tools/docker/images/nginx/root/etc/nginx/conf.d/default.conf
new file mode 100644
index 0000000..b032e23
--- /dev/null
+++ b/tools/docker/images/nginx/root/etc/nginx/conf.d/default.conf
@@ -0,0 +1,20 @@
+server {
+  server_name _;
+
+  root /app/web;
+
+  location / {
+    try_files $uri /index.php?$query_string;
+  }
+
+  location ~ \.php(/|$) {
+    fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
+    try_files $fastcgi_script_name =404;
+    include fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+    fastcgi_param PATH_INFO $fastcgi_path_info;
+    fastcgi_param QUERY_STRING $query_string;
+    fastcgi_intercept_errors on;
+    fastcgi_pass php:9000;
+  }
+}
diff --git a/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php b/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php
new file mode 100755
index 0000000..564778b
--- /dev/null
+++ b/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+[[ -f composer.json && ! -d vendor ]] && composer install
+
+eval "$@"
diff --git a/tools/docker/images/web/root/etc/nginx/conf.d/default.conf b/tools/docker/images/web/root/etc/nginx/conf.d/default.conf
new file mode 100644
index 0000000..f941f8c
--- /dev/null
+++ b/tools/docker/images/web/root/etc/nginx/conf.d/default.conf
@@ -0,0 +1,20 @@
+server {
+  server_name _;
+
+  root /app/docroot;
+
+  location / {
+    try_files $uri /index.php?$query_string;
+  }
+
+  location ~ \.php(/|$) {
+    fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
+    try_files $fastcgi_script_name =404;
+    include fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+    fastcgi_param PATH_INFO $fastcgi_path_info;
+    fastcgi_param QUERY_STRING $query_string;
+    fastcgi_intercept_errors on;
+    fastcgi_pass php:9000;
+  }
+}