# Drupal runtime image.
#
# Built from the official Drupal Docker Hub image rather than a custom base.
# This optional build context does not publish an image.
#
# Pinned by digest for reproducible builds. Refresh the tag and digest
# together on a deliberate schedule; do not let them drift apart.
FROM drupal:11-php8.4-apache@sha256:0585cee5fe091eea6b26edf2008dfad1b198693a98fcc3d14123afc7f6cd8ce2

# The Debian archive publishes patched packages well before the
# upstream `drupal:11-php8.4-apache` tag is rebuilt to pick them up, so
# pinning by digest alone (above) does not keep pace with fixed OS-level
# CVEs the way it does with `composer update` for Drupal core below. This
# upgrades every already-patched Debian package at build time instead of
# waiting on an upstream image rebuild; review the resulting image with your
# normal vulnerability-scanning process.
RUN apt-get update \
    && apt-get upgrade -y \
    && rm -rf /var/lib/apt/lists/*

# The base image's Apache listens on the privileged port 80, which
# only root can bind. Moving it to an unprivileged port lets the container
# run entirely as the existing, already correctly permissioned www-data
# user instead of starting as root, per this project's policy against
# running the deployed application as root. The base image's own runtime
# directories (log, lock, PID) and the writable site directory are already
# owned by www-data; only the listening port stood in the way.
RUN sed -i 's/^Listen 80$/Listen 8080/' /etc/apache2/ports.conf \
    && sed -i 's/<VirtualHost \*:80>/<VirtualHost *:8080>/' \
    /etc/apache2/sites-enabled/000-default.conf

# The base image ships PHP without the pcntl and sockets extensions this
# module's queue worker and functional test tooling require. Both build from
# the PHP source already present in the image.
RUN docker-php-source extract \
    && docker-php-ext-install pcntl sockets \
    && docker-php-source delete

# The base image's active MPM (prefork; confirmed with
# `apache2ctl -M`) allows up to 150 worker processes by default, each a full
# PHP process capable of using up to PHP's memory_limit (128M in this image).
# 150 workers could therefore claim up to ~19 GiB, far more than any
# container memory limit this deployment documents. This request-admission
# control already bounds concurrent application-level work well
# below this count; the worker cap is a container-level backstop beneath it,
# not the primary control. Six workers at 128M each bounds Apache's own worst
# case to ~768 MiB against the configured container memory limit; the two
# values must be changed as a pair.
RUN sed -i \
    -e 's/^MaxRequestWorkers.*/MaxRequestWorkers      6/' \
    -e 's/^StartServers.*/StartServers            2/' \
    /etc/apache2/mods-available/mpm_prefork.conf

# The worker-count math above depends on PHP's memory_limit, which
# the base image otherwise leaves at whatever its own upstream default
# happens to be (128M when this comment was written) — an assumption a
# future base image refresh could silently change without notice. Setting it
# explicitly here keeps the worker-count math and the included Kernel regression
# test tied to the same value.
RUN echo 'memory_limit = 128M' > /usr/local/etc/php/conf.d/gcr-memory-limit.ini

WORKDIR /opt/drupal

# The base image's own drupal/core-recommended is pinned to whatever was
# current when it was last built and is not refreshed automatically. Bring it
# to the latest 11.x release before adding this module, so the image never
# ships a Drupal core with known, already-patched advisories by default.
# Keep the Composer constraint open across Drupal 11 patch releases. Reject a
# resolved core below the SA-CORE-2026-014 security floor if metadata is stale.
RUN composer config --no-plugins allow-plugins.symfony/runtime true \
    && composer require --no-update --no-interaction --no-plugins \
    'drupal/core-recommended:^11.4' \
    && composer update drupal/core-recommended drupal/core --with-all-dependencies \
    --no-interaction --no-progress \
    && composer show --format=json drupal/core \
    | php -r '$package = json_decode(stream_get_contents(STDIN), TRUE, 512, JSON_THROW_ON_ERROR); $version = $package["versions"][0] ?? NULL; if (($package["name"] ?? NULL) !== "drupal/core" || !is_string($version) || !version_compare($version, "11.4.10", ">=")) { fwrite(STDERR, "Drupal core is below the reviewed security floor.\n"); exit(1); }'

# Add this module as a Composer package so its own declared dependencies
# (including nikic/php-parser) resolve in the running site. Development-only
# PHPCS/PHPStan tools are not dependencies of the installed module. Discard
# the copied build context after installation.
COPY . /tmp/gpt-code-reviewer-server-source
RUN composer config repositories.gpt-code-reviewer-server \
    '{"type": "path", "url": "/tmp/gpt-code-reviewer-server-source", "options": {"symlink": false}}' \
    && composer require --no-interaction --no-progress drupal/ai_code_review_server:@dev \
    && rm -rf /tmp/gpt-code-reviewer-server-source

# Module enablement and site configuration are deployment-time concerns,
# not baked into this image.

# Run as the base image's existing www-data user rather than root. This must
# stay last: every earlier step (extension compilation, Composer operations)
# needs root, and switching users earlier would make those steps fail.
#
# COMPOSER_HOME defaults to a subdirectory of the running user's home, which
# for www-data is /var/www, a directory www-data cannot write to in this
# base image. Point it at a directory www-data owns so a later `composer
# audit` (or any other Composer command run against the deployed image) does
# not silently fall back to an uncached, warning-producing run every time.
RUN mkdir -p /opt/drupal/.composer-cache \
    && chown www-data:www-data /opt/drupal/.composer-cache
ENV COMPOSER_HOME=/opt/drupal/.composer-cache
EXPOSE 8080
USER www-data
