--- # Role: pote # Purpose: Deploy POTE (python/venv) and schedule cron jobs. - name: Ensure POTE system dependencies are installed ansible.builtin.apt: name: "{{ pote_system_packages }}" state: present update_cache: true cache_valid_time: 3600 - name: Ensure POTE group exists ansible.builtin.group: name: "{{ pote_group }}" state: present - name: Ensure POTE user exists ansible.builtin.user: name: "{{ pote_user }}" group: "{{ pote_group }}" shell: /bin/bash create_home: true state: present - name: Ensure POTE app directory exists ansible.builtin.file: path: "{{ pote_app_dir }}" state: directory owner: "{{ pote_user }}" group: "{{ pote_group }}" mode: "0755" - name: Ensure SSH directory exists for POTE user ansible.builtin.file: path: "/home/{{ pote_user }}/.ssh" state: directory owner: "{{ pote_user }}" group: "{{ pote_group }}" mode: "0700" - name: Install Git SSH key for POTE (vault-backed) ansible.builtin.copy: dest: "/home/{{ pote_user }}/.ssh/id_ed25519" content: "{{ pote_git_ssh_key }}" owner: "{{ pote_user }}" group: "{{ pote_group }}" mode: "0600" no_log: true when: (pote_git_ssh_key | default('')) | length > 0 - name: Fetch Git host key (ssh-keyscan) ansible.builtin.command: "ssh-keyscan -p {{ pote_git_port }} -H {{ pote_git_host }}" register: pote_ssh_keyscan changed_when: false failed_when: false when: (pote_git_host | default('')) | length > 0 - name: Ensure Git host is in known_hosts for POTE user ansible.builtin.known_hosts: path: "/home/{{ pote_user }}/.ssh/known_hosts" name: "{{ pote_git_host }}" key: "{{ pote_ssh_keyscan.stdout }}" state: present when: - (pote_git_host | default('')) | length > 0 - (pote_ssh_keyscan.stdout | default('')) | length > 0 - name: Clone/update POTE repository block: - name: Clone/update POTE repository (git over SSH) ansible.builtin.git: repo: "{{ pote_git_repo }}" dest: "{{ pote_app_dir }}" version: "{{ pote_git_branch }}" key_file: "/home/{{ pote_user }}/.ssh/id_ed25519" accept_hostkey: true update: true become: true become_user: "{{ pote_user }}" register: pote_git_result rescue: - name: Abort with actionable Git SSH guidance ansible.builtin.fail: msg: >- Failed to clone {{ pote_git_repo }} (branch={{ pote_git_branch }}) as user {{ pote_user }}. Common causes: - vault_pote_git_ssh_key is not a valid OpenSSH private key (or is passphrase-protected) - the public key is not added to Gitea as a deploy key / user key with access to ilia/POTE - repo or branch name is wrong Error: {{ pote_git_result.msg | default(pote_git_result.stderr | default('unknown error')) }} - name: Ensure PostgreSQL is running ansible.builtin.systemd: name: postgresql state: started enabled: true - name: Check if PostgreSQL role exists ansible.builtin.command: "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='{{ pote_db_user }}'\"" become: true become_user: postgres register: pote_pg_role_check changed_when: false - name: Create PostgreSQL user for POTE ansible.builtin.command: "psql -c \"CREATE USER {{ pote_db_user }} WITH PASSWORD '{{ pote_db_password }}'\"" become: true become_user: postgres when: (pote_pg_role_check.stdout | trim) != '1' changed_when: true - name: Ensure PostgreSQL user password is set (idempotent) ansible.builtin.command: "psql -c \"ALTER USER {{ pote_db_user }} WITH PASSWORD '{{ pote_db_password }}'\"" become: true become_user: postgres when: (pote_db_password | default('')) | length > 0 changed_when: false - name: Check if PostgreSQL database exists ansible.builtin.command: "psql -tAc \"SELECT 1 FROM pg_database WHERE datname='{{ pote_db_name }}'\"" become: true become_user: postgres register: pote_pg_db_check changed_when: false - name: Create PostgreSQL database for POTE ansible.builtin.command: "psql -c \"CREATE DATABASE {{ pote_db_name }} OWNER {{ pote_db_user }}\"" become: true become_user: postgres when: (pote_pg_db_check.stdout | trim) != '1' changed_when: true - name: Ensure Python virtual environment exists ansible.builtin.command: "{{ pote_python_bin }} -m venv {{ pote_venv_dir }}" args: creates: "{{ pote_venv_dir }}/bin/activate" become: true become_user: "{{ pote_user }}" - name: Upgrade pip in venv ansible.builtin.pip: name: pip state: present virtualenv: "{{ pote_venv_dir }}" become: true become_user: "{{ pote_user }}" - name: Deploy POTE environment file ansible.builtin.template: src: env.j2 dest: "{{ pote_env_file }}" owner: "{{ pote_user }}" group: "{{ pote_group }}" mode: "{{ pote_env_file_mode }}" - name: Install POTE in editable mode (pyproject.toml) ansible.builtin.pip: name: "{{ pote_app_dir }}" editable: true virtualenv: "{{ pote_venv_dir }}" become: true become_user: "{{ pote_user }}" - name: Run Alembic migrations ansible.builtin.command: "{{ pote_venv_dir }}/bin/alembic upgrade head" args: chdir: "{{ pote_app_dir }}" become: true become_user: "{{ pote_user }}" changed_when: false - name: Ensure logs directory exists ansible.builtin.file: path: "{{ pote_logs_dir }}" state: directory owner: "{{ pote_user }}" group: "{{ pote_group }}" mode: "0755" - name: Ensure automation shell scripts are executable ansible.builtin.file: path: "{{ pote_app_dir }}/scripts/{{ item }}" mode: "0755" loop: - automated_daily_run.sh - automated_weekly_run.sh - setup_cron.sh - setup_automation.sh become: true become_user: "{{ pote_user }}" - name: Install cron job - daily report ansible.builtin.cron: name: "POTE daily report" minute: "{{ pote_daily_report_time.split()[0] }}" hour: "{{ pote_daily_report_time.split()[1] }}" job: "{{ pote_daily_job }}" user: "{{ pote_user }}" state: present when: - pote_enable_cron | bool - pote_daily_report_enabled | bool - name: Install cron job - weekly report ansible.builtin.cron: name: "POTE weekly report" minute: "{{ pote_weekly_report_time.split()[0] }}" hour: "{{ pote_weekly_report_time.split()[1] }}" weekday: "{{ pote_weekly_report_time.split()[2] }}" job: "{{ pote_weekly_job }}" user: "{{ pote_user }}" state: present when: - pote_enable_cron | bool - pote_weekly_report_enabled | bool - name: Install cron job - health check ansible.builtin.cron: name: "POTE health check" minute: "{{ pote_health_check_time.split()[0] }}" hour: "{{ pote_health_check_time.split()[1] }}" job: "{{ pote_health_check_job }}" user: "{{ pote_user }}" state: present when: - pote_enable_cron | bool - pote_health_check_enabled | bool