diff --git a/config/packages/webpack_encore.yaml b/config/packages/webpack_encore.yaml index 4c009ee..cc50b57 100644 --- a/config/packages/webpack_encore.yaml +++ b/config/packages/webpack_encore.yaml @@ -34,11 +34,11 @@ framework: assets: json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' -#when@prod: -# webpack_encore: -# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) -# # Available in version 1.2 -# cache: true +when@prod: + webpack_encore: + # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) + # Available in version 1.2 + cache: true #when@test: # webpack_encore: diff --git a/config/routes.yaml b/config/routes.yaml index ceb0599..365bdb7 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,9 +1,3 @@ -controllers: - resource: - path: ../src/Controller/ - namespace: App\Controller - type: attribute - vue_app: path: /{req} controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController @@ -11,4 +5,10 @@ vue_app: template: 'vue/index.html.twig' req: '' requirements: - req: ".*" + req: "^(?!api/|legacy).*" + +controllers: + resource: + path: ../src/Controller/ + namespace: App\Controller + type: attribute diff --git a/deploy.php b/deploy.php index cec7df2..cbd6aa3 100644 --- a/deploy.php +++ b/deploy.php @@ -9,9 +9,9 @@ set('repository', "https://{$giteaToken}@git.homelab.nestor-server.fr/colgora/Ma set('keep_releases', 3); set('composer_options', '--no-dev --optimize-autoloader --no-interaction --prefer-dist --ignore-platform-reqs --no-scripts'); -// Copier vendor/ et node_modules/ depuis la release précédente (hard links, quasi instantané) -// Composer et npm ne mettent à jour que ce qui a changé → déploiements beaucoup plus rapides -set('copy_dirs', ['vendor', 'node_modules']); +// Copier vendor/ depuis la release précédente (hard links, quasi instantané) +// node_modules est géré par le shared mount /srv/mangarr/shared/node_modules +set('copy_dirs', ['vendor']); // Pas de shared_files ni shared_dirs : tout est géré par les volumes Docker set('shared_files', []); @@ -31,27 +31,96 @@ task('deploy:prepare_dirs', function () { // composer install via container éphémère (pas de PHP sur l'hôte requis) // --user assure que vendor/ appartient au user deploy et non root +// Skip si composer.lock inchangé et vendor/ déjà populé (hard-linké depuis la release précédente) task('deploy:vendors', function () { + $releaseDir = get('release_path'); + $previousDir = get('previous_release'); + + if ($previousDir !== null) { + $lockUnchanged = test("diff -q $previousDir/composer.lock $releaseDir/composer.lock > /dev/null 2>&1"); + $vendorPopulated = test("[ -d $releaseDir/vendor/composer ]"); + + if ($lockUnchanged && $vendorPopulated) { + writeln('deploy:vendors skipped — composer.lock unchanged'); + return; + } + } + run('docker run --rm --user $(id -u):$(id -g) -v {{release_path}}:/app -w /app composer:2 install {{composer_options}}'); }); // Build assets via container node éphémère -// Le cache webpack (node_modules/.cache) est persisté dans shared/webpack_cache entre les releases -// → 1er build lent, suivants compilent uniquement les modules modifiés +// 3 couches d'optimisation : +// 1. Skip total si aucun fichier front-end n'a changé (hard-link public/build/) +// 2. Skip npm install si package-lock.json inchangé (node_modules partagé persistant) +// 3. Cache npm et webpack persistants entre les releases desc('Build Webpack Encore assets'); task('webpack_encore:build', function () { - run('mkdir -p /srv/mangarr/shared/webpack_cache'); - run('docker run --rm --user $(id -u):$(id -g) -e npm_config_cache=/tmp/npm-cache -e PUPPETEER_SKIP_DOWNLOAD=1 -v {{release_path}}:/app -v /srv/mangarr/shared/webpack_cache:/app/node_modules/.cache -w /app node:22-alpine sh -c "npm install && npm run build"'); + $sharedDir = '/srv/mangarr/shared'; + $sharedWebpackCache = "$sharedDir/webpack_cache"; + $sharedNodeModules = "$sharedDir/node_modules"; + $sharedNpmCache = "$sharedDir/npm_cache"; + + run("mkdir -p $sharedWebpackCache $sharedNodeModules $sharedNpmCache"); + + $releaseDir = get('release_path'); + $previousDir = get('previous_release'); // null au 1er déploiement + + // --- COUCHE 1 : skip total si aucun fichier front-end n'a changé --- + if ($previousDir !== null) { + $watchList = ['assets', 'templates', 'package.json', 'package-lock.json', + 'webpack.config.js', 'postcss.config.js', 'tailwind.config.js']; + + $diffChecks = implode(' && ', array_map( + fn($p) => "diff -rq --no-dereference $previousDir/$p $releaseDir/$p > /dev/null 2>&1", + $watchList + )); + + $hasPreviousBuild = test("[ -d $previousDir/public/build ] && [ -f $previousDir/public/build/manifest.json ]"); + + if ($hasPreviousBuild && test("($diffChecks)")) { + run("cp -al $previousDir/public/build $releaseDir/public/build"); + writeln('webpack_encore:build skipped — no front-end files changed'); + return; + } + } + + // --- COUCHE 2 : skip npm install si package-lock.json inchangé --- + $needsNpmInstall = true; + if ($previousDir !== null) { + $lockUnchanged = test("diff -q $previousDir/package-lock.json $releaseDir/package-lock.json > /dev/null 2>&1"); + $nmPopulated = test("[ -d $sharedNodeModules/.bin ]"); + if ($lockUnchanged && $nmPopulated) { + $needsNpmInstall = false; + } + } + + // --- COUCHE 3 : build docker avec caches persistants --- + $installCmd = $needsNpmInstall + ? 'npm install --prefer-offline && npm run build' + : 'npm run build'; + + run("docker run --rm \ + --user \$(id -u):\$(id -g) \ + -v $releaseDir:/app \ + -v $sharedNodeModules:/app/node_modules \ + -v $sharedWebpackCache:/app/node_modules/.cache \ + -v $sharedNpmCache:/npm_cache \ + -e npm_config_cache=/npm_cache \ + -e PUPPETEER_SKIP_DOWNLOAD=1 \ + -w /app \ + node:22-alpine \ + sh -c '$installCmd'"); }); // Restart Docker containers (entrypoint gère les migrations automatiquement) -// cache:clear avant restart : le nouveau code est déjà visible via bind mount, -// mais var/ est un volume Docker persistant qui garde l'ancien cache Symfony. +// Le cache:clear est fait APRÈS le restart : Docker résout le bind mount au démarrage +// du container, pas dynamiquement. Avant restart, docker exec voit encore l'ancienne release. desc('Restart Docker containers'); task('docker:restart', function () { - run('docker exec mangarr php bin/console cache:clear --env=prod'); run('docker restart mangarr-worker-commands mangarr-worker-events mangarr-worker-scheduler'); run('docker restart mangarr'); + run('docker exec mangarr php bin/console cache:clear --env=prod'); }); // Pas de PHP sur l'hôte : désactiver les tâches Symfony qui en ont besoin