diff --git a/.gitignore b/.gitignore
index 73dfcb2..48c4173 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/.direnv
/dist/*
!/dist/.keep
+.ignored/
###> symfony/framework-bundle ###
/.env.local
diff --git a/run b/run
index d79eeef..b393d8e 100755
--- a/run
+++ b/run
@@ -28,7 +28,7 @@ function build {
# Generate the phar file.
box compile --config box.json.dist
- rm -f .env.local.php
+ rm -f .env.local .env.local.php
tree dist/
@@ -37,6 +37,7 @@ function build {
function ci:test {
nix develop --command composer install
+ nix develop --command ./run test:snapshots
nix develop --command phpunit
}
@@ -49,10 +50,61 @@ function help {
printf "\nExtended help:\n Each task has comments for general usage\n"
}
-
function test {
phpunit "${@}"
}
+# Create a new snapshot for a configuration based on generated files.
+function test:create-snapshot {
+ set -o nounset
+
+ config="${1}"
+
+ config_file="tests/snapshots/configs/${config}.yaml"
+ output_path="tests/snapshots/output/${config}"
+
+ cat "${config_file}"
+
+ rm -fr "${output_path}"
+
+ ./bin/build-configs app:generate --config-file "${config_file}" --output-dir "${output_path}"
+
+ git status "${output_path}"
+}
+
+# Generate a file and ensure it matches the expected version.
+function test:snapshots {
+ rm -rf .ignored/snapshots
+ mkdir -p .ignored/snapshots
+
+ local configs=(
+ # TODO: add more configurations for different types and configurations.
+ drupal
+ )
+
+ for config in "${configs[@]}"; do
+ config_file="tests/snapshots/configs/${config}.yaml"
+ input_path="tests/snapshots/output/${config}"
+ output_path=".ignored/snapshots/output/${config}"
+
+ cat "${config_file}"
+
+ ./bin/build-configs app:generate --config-file "${config_file}" --output-dir "${output_path}"
+
+ find "${input_path}" -type f -print0 | while IFS= read -r -d '' original_file; do
+ generated_file="$output_path/${original_file#"${input_path}"/}"
+
+ if cmp -s "${original_file}" "${generated_file}"; then
+ echo "Files match: ${original_file}"
+ else
+ # TODO: show the diff for all failed files. This will stop after the first failure.
+ echo "Files do not match: ${original_file}"
+ diff "${original_file}" "${generated_file}"
+ exit 1
+ fi
+ done
+ done
+}
+
TIMEFORMAT=$'\nTask completed in %3lR'
time "${@:-help}"
diff --git a/tests/snapshots/configs/drupal.yaml b/tests/snapshots/configs/drupal.yaml
new file mode 100644
index 0000000..a24b5cc
--- /dev/null
+++ b/tests/snapshots/configs/drupal.yaml
@@ -0,0 +1,43 @@
+name: drupal
+language: php
+type: drupal
+
+web:
+ type: nginx
+
+database:
+ type: mariadb
+ version: 10
+
+php:
+ version: 8.1-fpm-bullseye
+ phpcs:
+ paths:
+ - web/modules/custom
+ standards:
+ - Drupal
+ - DrupalPractice
+ phpstan:
+ level: max
+ paths:
+ - web/modules/custom
+
+drupal:
+ docroot: web
+
+docker-compose:
+ services:
+ - database
+ - php
+ - web
+
+dockerfile:
+ stages:
+ build:
+ commands:
+ - composer validate
+ - composer install
+
+experimental:
+ createGitHubActionsConfiguration: true
+ runGitHooksBeforePush: true
diff --git a/tests/snapshots/output/drupal/.dockerignore b/tests/snapshots/output/drupal/.dockerignore
new file mode 100644
index 0000000..61d63c0
--- /dev/null
+++ b/tests/snapshots/output/drupal/.dockerignore
@@ -0,0 +1,2 @@
+/README.md
+/.github/
\ No newline at end of file
diff --git a/tests/snapshots/output/drupal/.env.example b/tests/snapshots/output/drupal/.env.example
new file mode 100644
index 0000000..ed69226
--- /dev/null
+++ b/tests/snapshots/output/drupal/.env.example
@@ -0,0 +1,12 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+export DOCKER_UID=1000
+
+export COMPOSE_PROJECT_NAME=drupal
+export COMPOSE_PROFILES=web,php,database
+
+export DOCKER_WEB_VOLUME=.:/app
+
+export MYSQL_DATABASE=app
+export MYSQL_PASSWORD=app
+export MYSQL_USER=app
diff --git a/tests/snapshots/output/drupal/.githooks/pre-push b/tests/snapshots/output/drupal/.githooks/pre-push
new file mode 100755
index 0000000..54ac104
--- /dev/null
+++ b/tests/snapshots/output/drupal/.githooks/pre-push
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+set -o errexit
+
+export TTY="-T"
+
+./run test:commit
diff --git a/tests/snapshots/output/drupal/.githooks/prepare-commit-msg b/tests/snapshots/output/drupal/.githooks/prepare-commit-msg
new file mode 100755
index 0000000..6ff8ea0
--- /dev/null
+++ b/tests/snapshots/output/drupal/.githooks/prepare-commit-msg
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+# Load the issue ID from an `.issue-id` file within the project and replace the
+# `ISSUE_ID` placeholder within a Git commit message.
+#
+# For example, running `echo "OD-123" > .issue-id` will add `Refs: OD-123` to
+# the commit message.
+#
+# This also works with multiple issue IDs in the same string, e.g.
+# "OD-123 OD-456", or IDs on multiple lines.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+PROJECT_DIR=$(git rev-parse --show-toplevel)
+ISSUE_FILE="$PROJECT_DIR/.issue-id"
+
+if [ -f "${ISSUE_FILE}" ]; then
+ ISSUE_IDS=$(cat "${ISSUE_FILE}" | tr '\n' ',' | tr ' ' ',' | sed 's/,$//' | sed 's/,/, /g')
+
+ if [ -n "${ISSUE_IDS}" ]; then
+ sed -i.bak "s/# Refs:/Refs: $ISSUE_IDS/" "$1"
+ fi
+fi
diff --git a/tests/snapshots/output/drupal/.github/workflows/ci.yml b/tests/snapshots/output/drupal/.github/workflows/ci.yml
new file mode 100644
index 0000000..868fe2e
--- /dev/null
+++ b/tests/snapshots/output/drupal/.github/workflows/ci.yml
@@ -0,0 +1,25 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ workflow_dispatch:
+
+env:
+ COMPOSE_DOCKER_CLI_BUILD: 1
+ DOCKER_BUILDKIT: 1
+ DOCKER_UID: 1001
+
+jobs:
+ build_and_test:
+ name: Build and test
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
+
+ - name: Build and test
+ run: |
+ ./run ci:test
diff --git a/tests/snapshots/output/drupal/.gitignore b/tests/snapshots/output/drupal/.gitignore
new file mode 100644
index 0000000..f1df0c3
--- /dev/null
+++ b/tests/snapshots/output/drupal/.gitignore
@@ -0,0 +1,42 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+.editorconfig
+.env
+.gitattributes
+vendor/
+web/.csslintrc
+web/.eslintignore
+web/.eslintrc.json
+web/.ht.router.php
+web/.htaccess
+web/INSTALL.txt
+web/README.md
+web/autoload.php
+web/core/
+web/example.gitignore
+web/index.php
+web/modules/README.txt
+web/modules/contrib/
+web/profiles/README.txt
+web/robots.txt
+web/sites/*/files/
+web/sites/*/private/
+web/sites/*/services*.yml
+web/sites/*/settings*.php
+web/sites/README.txt
+web/sites/default/default.services.yml
+web/sites/default/default.settings.php
+web/sites/development.services.yml
+web/sites/example.settings.local.php
+web/sites/example.sites.php
+web/sites/simpletest/
+web/themes/README.txt
+web/themes/contrib/
+web/update.php
+web/web.config
+
+# Docker.
+.env
+docker-compose.override.yaml
+
+
diff --git a/tests/snapshots/output/drupal/.hadolint.yaml b/tests/snapshots/output/drupal/.hadolint.yaml
new file mode 100644
index 0000000..d87bf88
--- /dev/null
+++ b/tests/snapshots/output/drupal/.hadolint.yaml
@@ -0,0 +1,2 @@
+ignore:
+ - DL3059
diff --git a/tests/snapshots/output/drupal/Dockerfile b/tests/snapshots/output/drupal/Dockerfile
new file mode 100644
index 0000000..7164bf6
--- /dev/null
+++ b/tests/snapshots/output/drupal/Dockerfile
@@ -0,0 +1,64 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+FROM php:8.1-fpm-bullseye AS base
+
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+RUN which composer && composer -V
+
+ARG DOCKER_UID=1000
+ENV DOCKER_UID="${DOCKER_UID}"
+
+WORKDIR /app
+
+RUN adduser --disabled-password --uid "${DOCKER_UID}" app \
+ && chown app:app -R /app
+
+USER app
+
+ENV PATH="${PATH}:/app/bin:/app/vendor/bin"
+
+COPY --chown=app:app composer.* ./
+
+################################################################################
+
+FROM base AS build
+
+USER root
+
+
+RUN apt-get update -yqq \
+ && apt-get install -yqq --no-install-recommends \
+ git libpng-dev libjpeg-dev libzip-dev mariadb-client unzip \
+ && rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man \
+ && apt-get clean
+
+RUN docker-php-ext-configure gd --with-jpeg
+
+RUN docker-php-ext-install gd pdo_mysql zip
+
+COPY --chown=app:app phpunit.xml* ./
+
+
+
+USER app
+
+RUN composer validate
+RUN composer install
+
+COPY --chown=app:app tools/docker/images/php/root /
+
+ENTRYPOINT ["/usr/local/bin/docker-entrypoint-php"]
+CMD ["php-fpm"]
+
+
+
+
+################################################################################
+
+FROM nginx:1 as web
+
+EXPOSE 8080
+
+WORKDIR /app
+
+COPY tools/docker/images/web/root /
diff --git a/tests/snapshots/output/drupal/docker-compose.yaml b/tests/snapshots/output/drupal/docker-compose.yaml
new file mode 100644
index 0000000..fa1f56c
--- /dev/null
+++ b/tests/snapshots/output/drupal/docker-compose.yaml
@@ -0,0 +1,77 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+x-proxy: &default-proxy
+ networks:
+ - default
+ - web
+ labels:
+ - "traefik.docker.network=traefik_proxy"
+ - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(
+ `${COMPOSE_PROJECT_NAME}.localhost`,
+ )"
+
+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:
+ <<: [*default-proxy, *default-app]
+ build:
+ context: .
+ target: web
+ depends_on:
+ - php
+ profiles: [web]
+
+ php:
+ <<: [*default-app]
+ build:
+ context: .
+ target: build
+ args:
+ - "DOCKER_UID=${DOCKER_UID:-1000}"
+ volumes:
+ - .:/app
+ 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
+ profiles: [database]
+
+volumes:
+ db-data: {}
+
+networks:
+ web:
+ external: true
+ name: traefik_proxy
diff --git a/tests/snapshots/output/drupal/phpcs.xml.dist b/tests/snapshots/output/drupal/phpcs.xml.dist
new file mode 100644
index 0000000..f9e7395
--- /dev/null
+++ b/tests/snapshots/output/drupal/phpcs.xml.dist
@@ -0,0 +1,32 @@
+
+
+
+
+ PHPCS configuration file for drupal.
+
+ web/modules/custom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/snapshots/output/drupal/phpstan.neon.dist b/tests/snapshots/output/drupal/phpstan.neon.dist
new file mode 100644
index 0000000..a5a696c
--- /dev/null
+++ b/tests/snapshots/output/drupal/phpstan.neon.dist
@@ -0,0 +1,9 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+parameters:
+ level: max
+ excludePaths:
+ - *Test.php
+ - *TestBase.php
+ paths:
+ - web/modules/custom
diff --git a/tests/snapshots/output/drupal/phpunit.xml.dist b/tests/snapshots/output/drupal/phpunit.xml.dist
new file mode 100644
index 0000000..179546d
--- /dev/null
+++ b/tests/snapshots/output/drupal/phpunit.xml.dist
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ./web/modules/custom/**/tests/**/Functional
+
+
+ ./web/modules/custom/**/tests/**/Kernel
+
+
+ ./web/modules/custom/**/tests/**/Unit
+
+
+
diff --git a/tests/snapshots/output/drupal/run b/tests/snapshots/output/drupal/run
new file mode 100755
index 0000000..c70906a
--- /dev/null
+++ b/tests/snapshots/output/drupal/run
@@ -0,0 +1,129 @@
+#!/usr/bin/env bash
+
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+set -o errexit
+set -o pipefail
+
+# If we're running in CI we need to disable TTY allocation for docker compose
+# commands that enable it by default, such as exec and run.
+TTY="${TTY:-}"
+if [[ ! -t 1 ]]; then
+ TTY="-T"
+fi
+
+# Run automated tests as part of the Continuous Integration (CI) pipeline.
+function ci:test {
+ lint:dockerfile
+
+ docker compose version
+
+ docker network create traefik_proxy
+
+ cp --no-clobber .env.example .env
+
+ docker compose build --progress plain
+
+ docker compose up --detach
+ docker compose logs
+
+ composer install --quiet --no-progress
+
+ test --testdox
+
+ quality
+}
+
+# Run a command within the php container.
+function cmd {
+ docker compose exec php "${@}"
+}
+
+function coding-standards {
+ cmd phpcs "${@}"
+}
+
+function composer {
+ _exec php composer "${@}"
+}
+
+function drush {
+ _exec php drush "${@}"
+}
+
+function git-hooks:off {
+ git config --unset core.hooksPath
+}
+
+function git-hooks:on {
+ git config core.hooksPath .githooks
+}
+
+# Display a list of all available commands.
+function help {
+ printf "%s [args]\n\nTasks:\n" "${0}"
+
+ compgen -A function | grep -v "^_" | cat -n
+
+ printf "\nExtended help:\n Each task has comments for general usage\n"
+}
+
+function lint:dockerfile {
+ docker container run --rm -i \
+ hadolint/hadolint hadolint --ignore DL3008 --ignore DL3059 -t style "${@}" - < Dockerfile
+}
+
+function quality {
+ coding-standards
+ static-analysis
+}
+
+function start {
+ cp -v --no-clobber .env.example .env
+
+ docker compose up -d
+}
+
+function static-analysis {
+ cmd phpstan --memory-limit=-1 --no-progress "${@}"
+}
+
+function stop {
+ docker compose down
+}
+
+function test {
+ _exec php phpunit --colors=always "${@}"
+}
+
+function test:commit {
+ test --testdox --testsuite functional
+ test --testdox --testsuite kernel
+ test --testdox --testsuite unit
+
+ quality
+}
+
+function _exec {
+ docker compose exec ${TTY} "${@}"
+}
+
+function _run {
+ local service="${1}"
+ local command="${2}"
+
+ docker compose run \
+ --entrypoint "${command}" \
+ --no-deps \
+ --rm \
+ ${TTY} \
+ "${service}" "${@}"
+}
+
+# Include any local tasks.
+[[ -e run.local ]] && source run.local
+
+TIMEFORMAT=$'\nTask completed in %3lR'
+time "${@:-help}"
+
+# vim: ft=bash
diff --git a/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php b/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php
new file mode 100755
index 0000000..dbd9900
--- /dev/null
+++ b/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+[[ -f composer.json && ! -d vendor ]] && composer install
+
+eval "$@"
diff --git a/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/etc/php/php.ini b/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/etc/php/php.ini
new file mode 100644
index 0000000..aa883a1
--- /dev/null
+++ b/tests/snapshots/output/drupal/tools/docker/images/php/root/usr/local/etc/php/php.ini
@@ -0,0 +1,4 @@
+# Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.
+
+max_vars_input = 1000
+memory_limit = 128M