203 lines
7.9 KiB
Docker
203 lines
7.9 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# =============================================================================
|
|
# Stage 1 — PHP dependencies (no dev, production autoloader)
|
|
# =============================================================================
|
|
FROM composer:2 AS vendor
|
|
|
|
WORKDIR /app
|
|
|
|
# --no-scripts: artisan isn't available yet, package discovery would fail.
|
|
# Autoload is optimized in the runtime stage once the full source is present.
|
|
COPY composer.json composer.lock ./
|
|
RUN composer install \
|
|
--no-interaction \
|
|
--no-dev \
|
|
--prefer-dist \
|
|
--no-progress \
|
|
--no-scripts \
|
|
--ignore-platform-reqs
|
|
|
|
# =============================================================================
|
|
# Stage 2 — PHP dependencies WITH dev (solo per la suite di test)
|
|
#
|
|
# Stesso composer.lock dello stage sopra, senza --no-dev: e' l'unico posto in
|
|
# cui phpunit esiste. L'immagine di runtime non lo contiene di proposito, e non
|
|
# deve iniziare a contenerlo — questo stage finisce solo nell'immagine `test`.
|
|
# =============================================================================
|
|
FROM composer:2 AS vendor-dev
|
|
|
|
WORKDIR /app
|
|
|
|
COPY composer.json composer.lock ./
|
|
RUN composer install \
|
|
--no-interaction \
|
|
--prefer-dist \
|
|
--no-progress \
|
|
--no-scripts \
|
|
--ignore-platform-reqs
|
|
|
|
# =============================================================================
|
|
# Stage 3 — Frontend assets (Vite + Tailwind)
|
|
# =============================================================================
|
|
FROM node:20-alpine AS assets
|
|
|
|
WORKDIR /app
|
|
|
|
COPY package.json package-lock.json* ./
|
|
RUN if [ -f package-lock.json ]; then \
|
|
npm ci --no-audit --no-fund; \
|
|
else \
|
|
npm install --no-audit --no-fund; \
|
|
fi
|
|
|
|
# Tailwind 4 scans blade/php sources for class names, so they must be present.
|
|
COPY vite.config.js ./
|
|
COPY resources ./resources
|
|
COPY app ./app
|
|
COPY config ./config
|
|
RUN npm run build
|
|
|
|
# =============================================================================
|
|
# Stage 4 — Base di sistema (PHP + estensioni + nginx/supervisor)
|
|
#
|
|
# Condivisa da `runtime` e da `test`, cosi' la suite gira sulle stesse
|
|
# estensioni e sullo stesso php.ini della produzione: un test che passa qui
|
|
# non puo' passare grazie a un ambiente diverso da quello vero.
|
|
# =============================================================================
|
|
FROM php:8.3-fpm AS base
|
|
|
|
# nginx + supervisor run in-image alongside php-fpm: a separate nginx container
|
|
# would need its own copy of public/ plus the storage volume just to serve
|
|
# static files, which is more moving parts than this app warrants.
|
|
# poppler-utils provides pdftoppm/pdfinfo used by TerritorioThumbnailService.
|
|
# Node is deliberately absent here: assets are built in the `assets` stage.
|
|
# The -dev packages are kept rather than purged. Their runtime counterparts
|
|
# are named per Debian release (libicu72 vs libicu76, libzip4 vs libzip5), so
|
|
# purging means pinning package names that break whenever the php base image
|
|
# moves to a new Debian. A few tens of MB is worth not having that breakage.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
curl \
|
|
netcat-openbsd \
|
|
procps \
|
|
nginx \
|
|
supervisor \
|
|
poppler-utils \
|
|
libpng-dev \
|
|
libjpeg62-turbo-dev \
|
|
libfreetype6-dev \
|
|
libonig-dev \
|
|
libxml2-dev \
|
|
libzip-dev \
|
|
libicu-dev \
|
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
|
&& docker-php-ext-install -j"$(nproc)" \
|
|
pdo_mysql \
|
|
mbstring \
|
|
exif \
|
|
pcntl \
|
|
bcmath \
|
|
gd \
|
|
intl \
|
|
zip \
|
|
opcache \
|
|
&& pecl install redis \
|
|
&& docker-php-ext-enable redis \
|
|
&& apt-get clean \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Kept only so `composer dump-autoload` can run below; harmless at runtime.
|
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
|
|
|
COPY docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini
|
|
COPY docker/php/www.conf /usr/local/etc/php-fpm.d/www.conf
|
|
COPY docker/nginx/default.conf /etc/nginx/sites-available/default
|
|
COPY docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
|
|
|
WORKDIR /var/www/html
|
|
|
|
# =============================================================================
|
|
# Stage 5 — Immagine di test
|
|
#
|
|
# Uguale al runtime tranne per il vendor, che qui include phpunit. Non monta
|
|
# nessun volume: la suite gira su SQLite in memoria e cache `array`, quindi non
|
|
# ha bisogno ne' di MariaDB ne' di Redis (vedi phpunit.xml).
|
|
#
|
|
# Si usa dal compose:
|
|
# docker compose --profile test run --rm test
|
|
# docker compose --profile test run --rm test --filter BackupArchiveTest
|
|
# =============================================================================
|
|
FROM base AS test
|
|
|
|
COPY --chown=www-data:www-data . .
|
|
COPY --from=vendor-dev --chown=www-data:www-data /app/vendor ./vendor
|
|
|
|
RUN mkdir -p \
|
|
storage/framework/cache/data \
|
|
storage/framework/sessions \
|
|
storage/framework/views \
|
|
storage/logs \
|
|
storage/app/public \
|
|
bootstrap/cache \
|
|
&& chown -R www-data:www-data storage bootstrap/cache \
|
|
&& chmod -R 775 storage bootstrap/cache
|
|
|
|
# --optimize non e' un vezzo: senza classmap ogni risoluzione di classe diventa
|
|
# un file_exists sul sorgente, che nel container di test arriva da un bind mount
|
|
# (lento su Docker Desktop, lentissimo se il progetto sta in una cartella
|
|
# sincronizzata). Misurato: 74s contro 13s sulla stessa suite. Le classi
|
|
# aggiunte dopo il build non finiscono nella mappa ma restano raggiungibili dal
|
|
# PSR-4, quindi un test nuovo non obbliga a ricostruire l'immagine.
|
|
RUN composer dump-autoload --optimize --no-interaction \
|
|
&& php artisan package:discover --ansi
|
|
|
|
# Sostituisce ENTRYPOINT/CMD di php:8.3-fpm: questo container non serve
|
|
# richieste, esegue la suite e esce con il codice di phpunit. Gli argomenti
|
|
# passati a `docker compose run test` arrivano direttamente a phpunit.
|
|
ENTRYPOINT ["php", "vendor/bin/phpunit"]
|
|
|
|
# =============================================================================
|
|
# Stage 6 — Runtime
|
|
#
|
|
# DEVE RESTARE L'ULTIMO STAGE DEL FILE. `docker build` senza --target costruisce
|
|
# l'ultimo, ed e' cosi' che lo invocano sia docker-compose.yml (i tre servizi
|
|
# non dichiarano `target`) sia Dokploy. Uno stage aggiunto qui sotto diventa in
|
|
# silenzio l'immagine di produzione.
|
|
# =============================================================================
|
|
FROM base AS runtime
|
|
|
|
# Everything the app needs is baked into the image. Runtime containers do NOT
|
|
# bind-mount the source, so there is no composer/npm work at boot.
|
|
COPY --chown=www-data:www-data . .
|
|
COPY --from=vendor --chown=www-data:www-data /app/vendor ./vendor
|
|
COPY --from=assets --chown=www-data:www-data /app/public/build ./public/build
|
|
|
|
# Writable paths. storage/ is replaced by a named volume at runtime; this is
|
|
# the skeleton the entrypoint repopulates when that volume starts out empty.
|
|
RUN mkdir -p \
|
|
storage/framework/cache/data \
|
|
storage/framework/sessions \
|
|
storage/framework/views \
|
|
storage/logs \
|
|
storage/app/public \
|
|
bootstrap/cache \
|
|
&& chown -R www-data:www-data storage bootstrap/cache \
|
|
&& chmod -R 775 storage bootstrap/cache
|
|
|
|
RUN composer dump-autoload --optimize --no-dev --no-interaction \
|
|
&& php artisan package:discover --ansi
|
|
|
|
COPY docker/php/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
COPY docker/php/queue-entrypoint.sh /usr/local/bin/queue-entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/queue-entrypoint.sh
|
|
|
|
EXPOSE 80
|
|
|
|
# /up is Laravel's built-in health endpoint (registered in bootstrap/app.php),
|
|
# so this exercises the full nginx -> php-fpm -> framework path, not just a port.
|
|
HEALTHCHECK --interval=10s --timeout=5s --start-period=120s --retries=12 \
|
|
CMD curl -fsS http://127.0.0.1/up >/dev/null || exit 1
|
|
|
|
ENTRYPOINT ["entrypoint.sh"]
|
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|