- name: Deploy Learning Platform with Ansible hosts: all become: yes gather_facts: yes vars: ansible_ssh_common_args: '-o StrictHostKeyChecking=no' tasks: - name: Set destination directory ansible.builtin.set_fact: dest_dir: "/opt/learning-platform" - name: Ensure dependencies are installed ansible.builtin.package: name: - docker-ce state: present ignore_errors: yes register: docker_valid - name: Include geerlingguy.docker role conditionally include_role: name: geerlingguy.docker when: docker_valid is failed - name: Ensure target directory exists ansible.builtin.file: path: "{{ dest_dir }}" state: directory owner: root group: root mode: "0755" - name: Copy compose.yml to destination ansible.builtin.copy: src: compose.yml dest: "{{ dest_dir }}/compose.yml" - name: Copy pb_migrations to destination ansible.builtin.copy: src: ../../../pb_migrations dest: "{{ dest_dir }}/" mode: "0755" - name: Copy pb_hooks to destination ansible.builtin.copy: src: ../../../pb_hooks dest: "{{ dest_dir }}/" mode: "0755" - name: Create .env file for secrets ansible.builtin.copy: dest: "{{ dest_dir }}/.env" content: | ANTHROPIC_API_KEY={{ anthropic_api_key | default('') }} ENTRA_TENANT_ID={{ entra_tenant_id | default('') }} ENTRA_CLIENT_ID={{ entra_client_id | default('') }} ENTRA_CLIENT_SECRET={{ entra_client_secret | default('') }} ENTRA_ADMIN_EMAILS={{ entra_admin_emails | default('') }} mode: '0600' - name: Pull latest image community.docker.docker_compose_v2: project_src: "{{ dest_dir }}" state: present files: compose.yml pull: always - name: Start services with Docker Compose community.docker.docker_compose_v2: project_src: "{{ dest_dir }}" state: present files: compose.yml recreate: always # A failed migration aborts `pocketbase serve` and the container crash-loops # while `docker compose up` still reports success (issue #18). Gate the # deploy on PocketBase actually serving its health endpoint. - name: Wait for PocketBase to become healthy ansible.builtin.command: docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/health register: pb_health until: pb_health.rc == 0 retries: 18 delay: 5 changed_when: false ignore_errors: yes - name: Collect PocketBase logs for diagnosis ansible.builtin.command: docker logs --tail 80 pocketbase-learning register: pb_logs when: pb_health is failed changed_when: false failed_when: false - name: Abort deploy — PocketBase is not healthy ansible.builtin.fail: msg: | PocketBase did not become healthy after the deploy (health endpoint unreachable). Recent container logs: {{ pb_logs.stdout | default('') }} {{ pb_logs.stderr | default('') }} when: pb_health is failed # The frontend container carries the SPA + Caddy. An invalid Caddyfile # crash-loops it at startup while the PocketBase gate stays green — that # outage (issue #20) was invisible to CI because the test job swaps in # Caddyfile.test. Gate on the real container actually serving. - name: Wait for the frontend (Caddy) to become healthy ansible.builtin.command: docker exec learning-platform wget -q --spider http://127.0.0.1:80/ register: fe_health until: fe_health.rc == 0 retries: 18 delay: 5 changed_when: false ignore_errors: yes - name: Collect frontend logs for diagnosis ansible.builtin.command: docker logs --tail 80 learning-platform register: fe_logs when: fe_health is failed changed_when: false failed_when: false - name: Abort deploy — frontend is not healthy ansible.builtin.fail: msg: | The frontend (Caddy) container did not become healthy after the deploy. Recent container logs: {{ fe_logs.stdout | default('') }} {{ fe_logs.stderr | default('') }} when: fe_health is failed # Observability behind the auth perimeter: surface the end-to-end state # in the CI log on every deploy. - name: Post-deploy smoke report ansible.builtin.shell: | cd /opt/learning-platform echo '--- docker compose ps ---' docker compose ps echo '--- frontend -> pocketbase proxy health ---' docker exec learning-platform wget -q -O - http://127.0.0.1:80/api/health || echo 'PROXY HEALTH FAILED' echo '--- team_members auth methods (OIDC provider present?) ---' docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/collections/team_members/auth-methods || echo 'AUTH-METHODS FAILED' echo '--- pocketbase logs (tail 30) ---' docker compose logs --tail=30 pocketbase-learning register: smoke_report changed_when: false failed_when: false - name: Show smoke report ansible.builtin.debug: msg: "{{ smoke_report.stdout_lines }}"