--- - name: Create Multica install directory ansible.builtin.file: path: "{{ multica_install_dir }}" state: directory mode: "0755" - name: Clone / update Multica repository ansible.builtin.git: repo: "{{ multica_repo_url }}" dest: "{{ multica_install_dir }}" version: "{{ multica_version }}" update: "{{ multica_git_update }}" force: false register: _multica_git # Optional: dump Postgres before touching a stack that is already running - name: Back up Postgres before changes when: - multica_backup_enabled | bool - _multica_compose_stat.stat.exists - (_multica_ps.stdout | default('') | length) > 0 block: - name: Create backup directory ansible.builtin.file: path: "{{ multica_backup_dir }}" state: directory mode: "0700" - name: Gather date facts ansible.builtin.setup: gather_subset: [date_time] - name: Dump database (redirect to file first so pg_dump's exit status counts) ansible.builtin.shell: >- docker compose -p {{ multica_compose_project }} -f {{ _multica_compose_file }} exec -T postgres pg_dump -U {{ multica_postgres_user }} {{ multica_postgres_db }} > {{ multica_backup_dir }}/multica-{{ ansible_date_time.iso8601_basic_short }}.sql && gzip {{ multica_backup_dir }}/multica-{{ ansible_date_time.iso8601_basic_short }}.sql args: chdir: "{{ multica_install_dir }}" changed_when: true # `make selfhost` only creates .env when missing; we do the same, then manage specific keys. - name: Create .env from .env.example (first run only) ansible.builtin.copy: src: "{{ multica_install_dir }}/.env.example" dest: "{{ multica_install_dir }}/.env" remote_src: true force: false mode: "0600" - name: Set managed keys in .env ansible.builtin.lineinfile: path: "{{ multica_install_dir }}/.env" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" mode: "0600" loop: "{{ _multica_env | dict2items }}" loop_control: label: "{{ item.key }}" no_log: "{{ multica_no_log }}" # .env.example ships a DATABASE_URL with the default password embedded. POSTGRES_PASSWORD alone does not # change it, so keep the URL's password in step (no-op if the line is absent or already matches). - name: Sync password inside DATABASE_URL ansible.builtin.replace: path: "{{ multica_install_dir }}/.env" regexp: '^(DATABASE_URL=[a-z]+://[^:/@]+:)[^@]*(@.*)$' replace: '\g<1>{{ multica_postgres_password }}\g<2>' mode: "0600" no_log: "{{ multica_no_log }}" # Compose re-reads .env and recreates containers whose config changed (equivalent of `up -d`). - name: Start / update Multica stack community.docker.docker_compose_v2: project_name: "{{ multica_compose_project }}" project_src: "{{ multica_install_dir }}" files: - "{{ _multica_compose_file }}" state: present pull: "{{ 'always' if multica_pull_latest | bool else 'missing' }}" recreate: auto register: _multica_up # Postgres only reads POSTGRES_PASSWORD when the data volume is first initialised. If the volume pre-dates this # run (or the vault value changed), the DB user keeps the old password and the backend can't log in. # Test over TCP (the local socket is trust-auth and proves nothing) and repair via the socket if needed. - name: Wait for Postgres to accept connections ansible.builtin.command: argv: ["docker", "compose", "-p", "{{ multica_compose_project }}", "-f", "{{ _multica_compose_file }}", "exec", "-T", "postgres", "pg_isready", "-U", "{{ multica_postgres_user }}", "-d", "{{ multica_postgres_db }}"] chdir: "{{ multica_install_dir }}" register: _multica_pg_ready until: _multica_pg_ready.rc == 0 retries: 24 delay: 5 changed_when: false - name: Test database login with the configured password (over TCP) ansible.builtin.command: argv: ["docker", "compose", "-p", "{{ multica_compose_project }}", "-f", "{{ _multica_compose_file }}", "exec", "-T", "-e", "PGPASSWORD", "postgres", "sh", "-c", "psql -h \"$(hostname -i | cut -d' ' -f1)\" -U {{ multica_postgres_user }} -d {{ multica_postgres_db }} -tAc 'select 1'"] chdir: "{{ multica_install_dir }}" environment: PGPASSWORD: "{{ multica_postgres_password }}" register: _multica_pg_auth changed_when: false failed_when: - _multica_pg_auth.rc != 0 - "'password authentication failed' not in _multica_pg_auth.stderr" no_log: "{{ multica_no_log }}" - name: Reset database user password to match .env when: _multica_pg_auth.rc != 0 block: - name: ALTER USER via local socket (password passed on stdin) ansible.builtin.command: argv: ["docker", "compose", "-p", "{{ multica_compose_project }}", "-f", "{{ _multica_compose_file }}", "exec", "-T", "postgres", "psql", "-v", "ON_ERROR_STOP=1", "-U", "{{ multica_postgres_user }}", "-d", "{{ multica_postgres_db }}"] chdir: "{{ multica_install_dir }}" stdin: "ALTER USER {{ multica_postgres_user }} PASSWORD '{{ multica_postgres_password }}';" changed_when: true no_log: "{{ multica_no_log }}" - name: Restart backend so it reconnects ansible.builtin.command: argv: ["docker", "compose", "-p", "{{ multica_compose_project }}", "-f", "{{ _multica_compose_file }}", "restart", "backend"] chdir: "{{ multica_install_dir }}" changed_when: true - name: Wait for backend /readyz (db + migrations ok) ansible.builtin.uri: url: "http://127.0.0.1:{{ _multica_backend_port }}/readyz" return_content: true status_code: 200 register: _multica_ready until: >- _multica_ready.status == 200 and (_multica_ready.json.checks.db | default('')) == 'ok' and (_multica_ready.json.checks.migrations | default('')) == 'ok' retries: 30 delay: 5 changed_when: false