--- - name: créer le dossier de l'application ansible.builtin.file: path: "{{ portail_dossier_app }}" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: "0755" # storage/ et bootstrap/cache/ sont de l'état et du cache runtime, jamais du # code : on ne les synchronise jamais depuis le Mac (un rsync --delete les # effacerait sinon, y compris storage/app/ où atterriront plus tard les # travaux d'élèves). On crée leur structure une fois, puis on laisse # www-data (PHP-FPM) les posséder en continu. - name: créer la structure storage/ et bootstrap/cache/ (une seule fois) ansible.builtin.file: path: "{{ portail_dossier_app }}/{{ item }}" state: directory owner: www-data group: www-data mode: "0775" loop: - storage/app/public - storage/framework/cache/data - storage/framework/sessions - storage/framework/views - storage/logs - bootstrap/cache - name: synchroniser le code du portail (sans vendor/node_modules/.env/storage) become: false ansible.posix.synchronize: src: "{{ playbook_dir }}/../portail/" dest: "{{ portail_dossier_app }}/" delete: true rsync_opts: - "--exclude=/vendor" - "--exclude=/node_modules" - "--exclude=/.env" - "--exclude=/database/database.sqlite" - "--exclude=/storage" - "--exclude=/bootstrap/cache" - "--exclude=/public/build" - "--exclude=/public/hot" - "--exclude=/.git" register: portail_code_synchronise notify: redémarrer horizon - name: installer les dépendances PHP (composer) ansible.builtin.command: cmd: composer install --no-dev --optimize-autoloader --no-interaction args: chdir: "{{ portail_dossier_app }}" environment: COMPOSER_ALLOW_SUPERUSER: "1" register: portail_composer_install_app changed_when: "'Nothing to install' not in portail_composer_install_app.stdout" - name: vérifier si .env existe déjà ansible.builtin.stat: path: "{{ portail_dossier_app }}/.env" register: portail_env_stat - name: générer une clé d'application (une seule fois) ansible.builtin.command: cmd: php artisan key:generate --show args: chdir: "{{ portail_dossier_app }}" register: portail_app_key_generee when: not portail_env_stat.stat.exists changed_when: true - name: déployer .env de production (une seule fois, jamais réécrit ensuite) ansible.builtin.template: src: env.production.j2 dest: "{{ portail_dossier_app }}/.env" owner: "{{ ansible_user }}" group: www-data mode: "0640" vars: portail_app_key: "{{ portail_app_key_generee.stdout }}" portail_app_url: "http://{{ ansible_host }}" when: not portail_env_stat.stat.exists # Variables ajoutées après le premier déploiement (étapes 9 et 10) : mises à # jour ligne par ligne, pour ne jamais retoucher le reste d'un .env déjà en # place (APP_KEY en particulier, qui ne doit jamais changer sans le vouloir). # # Authentification par mot de passe, pas par application credential : Heat # refuse de créer un trust Keystone à partir d'un jeton d'application # credential (« Delegated tokens cannot manage trusts », constaté le # 2026-10-08) — voir portail/config/openstack.php et docs/journal.md. - name: lire les identifiants OpenStack du portail (étape 6) ansible.builtin.slurp: src: /etc/kolla/clouds-paillasse.yaml register: portail_clouds_brut - name: extraire le nom d'utilisateur et le mot de passe ansible.builtin.set_fact: portail_os_username: "{{ portail_clouds_contenu | regex_search('username:\\s*\"?([^\"\\n]+)', '\\1') | first }}" portail_os_password: "{{ portail_clouds_contenu | regex_search('password:\\s*\"?([^\"\\n]+)', '\\1') | first }}" portail_os_project_name: "{{ portail_clouds_contenu | regex_search('project_name:\\s*\"?([^\"\\n]+)', '\\1') | first }}" vars: portail_clouds_contenu: "{{ portail_clouds_brut.content | b64decode }}" no_log: true - name: ajouter/mettre à jour les variables OpenStack dans .env ansible.builtin.lineinfile: path: "{{ portail_dossier_app }}/.env" regexp: "^{{ item.cle }}=" line: "{{ item.cle }}={{ item.valeur }}" loop: - { cle: "OS_AUTH_URL", valeur: "http://10.10.0.1:5000" } - { cle: "OS_NOVA_URL", valeur: "http://10.10.0.1:8774/v2.1" } - { cle: "OS_HEAT_URL", valeur: "http://10.10.0.1:8004/v1" } - { cle: "OS_USERNAME", valeur: "{{ portail_os_username }}" } - { cle: "OS_PASSWORD", valeur: "{{ portail_os_password }}" } - { cle: "OS_PROJECT_NAME", valeur: "{{ portail_os_project_name }}" } - { cle: "OS_APPLICATION_CREDENTIAL_ID", valeur: "" } - { cle: "OS_APPLICATION_CREDENTIAL_SECRET", valeur: "" } - { cle: "REDIS_PREFIX", valeur: "" } - { cle: "LABS_DOSSIER", valeur: "/opt/labs" } # > timeout des superviseurs Horizon (300 s, config/horizon.php) - { cle: "REDIS_QUEUE_RETRY_AFTER", valeur: "330" } no_log: true register: portail_env_mis_a_jour # Horizon charge la configuration une seule fois au démarrage : sans # redémarrage, il garderait l'ancien mot de passe après une rotation. notify: redémarrer horizon - name: installer les dépendances JS (npm) ansible.builtin.command: cmd: npm ci args: chdir: "{{ portail_dossier_app }}" register: portail_npm_ci changed_when: true - name: compiler les assets (vite build) ansible.builtin.command: cmd: npm run build args: chdir: "{{ portail_dossier_app }}" register: portail_npm_build changed_when: true - name: appliquer les migrations ansible.builtin.command: cmd: php artisan migrate --force args: chdir: "{{ portail_dossier_app }}" register: portail_migrate changed_when: "'Nothing to migrate' not in portail_migrate.stdout" - name: mettre en cache la configuration et les routes ansible.builtin.shell: cmd: php artisan config:cache && php artisan route:cache && php artisan view:cache args: chdir: "{{ portail_dossier_app }}" changed_when: true