--- - name: Развернуть сервер AWatch-rus hosts: aw_server become: true gather_facts: true vars: aw_release_root: "/opt/activitywatch/releases" aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}" aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip" aw_bootstrap_dir: "/tmp/aw-rus-bootstrap" aw_release_install_dir: "{{ aw_release_root }}/aw-server-rust-{{ aw_server_version }}" aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}" aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}" aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}" aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}" tasks: - name: Установить базовые пакеты ansible.builtin.apt: name: - curl - rsync - unzip state: present update_cache: true - name: Создать системную группу сервиса ansible.builtin.group: name: "{{ aw_server_group }}" system: true state: present - name: Создать системную учётную запись сервиса ansible.builtin.user: name: "{{ aw_server_user }}" group: "{{ aw_server_group }}" home: "{{ aw_server_data_dir }}" shell: /usr/sbin/nologin system: true create_home: false - name: Создать обязательные каталоги ansible.builtin.file: path: "{{ item }}" state: directory mode: "0755" loop: - "{{ aw_release_root }}" - "{{ aw_release_dir }}" - "{{ aw_release_install_dir }}" - /opt/activitywatch - /opt/activitywatch/bin - "{{ aw_server_webui_dir }}" - "{{ aw_server_webui_dir }}/js" - "{{ aw_server_data_dir }}" - "{{ aw_server_log_dir }}" - /etc/activitywatch - "{{ aw_bootstrap_dir }}" - name: Настроить каталоги ActivityWatch с владельцем сервиса ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" mode: "0755" loop: - /opt/activitywatch - /opt/activitywatch/bin - "{{ aw_release_root }}" - "{{ aw_release_dir }}" - "{{ aw_release_install_dir }}" - "{{ aw_server_webui_dir }}" - "{{ aw_server_webui_dir }}/js" - "{{ aw_server_data_dir }}" - "{{ aw_server_log_dir }}" - name: Скачать архив релиза ActivityWatch ansible.builtin.get_url: url: "{{ aw_server_download_url }}" dest: "{{ aw_archive_path }}" mode: "0644" - name: Распаковать релиз ActivityWatch ansible.builtin.unarchive: src: "{{ aw_archive_path }}" dest: "{{ aw_release_dir }}" remote_src: true extra_opts: ["-o"] - name: Найти распакованный каталог ActivityWatch ansible.builtin.find: paths: "{{ aw_release_dir }}" file_type: directory patterns: "activitywatch*" register: aw_release_find - name: Найти бинарный файл AW server ansible.builtin.find: paths: "{{ aw_release_dir }}" file_type: file patterns: - aw-server-rust - aw-server register: aw_server_binary_find - name: Найти каталог WebUI ansible.builtin.find: paths: "{{ aw_release_dir }}" file_type: directory patterns: - aw-webui - webui register: aw_webui_dir_find - name: Сохранить пути распакованного релиза ansible.builtin.set_fact: aw_release_extracted: "{{ (aw_release_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}" aw_server_binary_path: "{{ (aw_server_binary_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}" aw_webui_source_path: "{{ (aw_webui_dir_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}" - name: Проверить, что компоненты релиза найдены ansible.builtin.assert: that: - aw_release_extracted is defined - aw_release_extracted | length > 0 - aw_server_binary_path is defined - aw_server_binary_path | length > 0 - aw_webui_source_path is defined - aw_webui_source_path | length > 0 fail_msg: "Не удалось найти бинарный файл или WebUI в распакованном релизе ActivityWatch." - name: Создать каталог установленного релиза ansible.builtin.file: path: "{{ aw_release_install_dir }}" state: directory owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" mode: "0755" - name: Установить бинарный файл AW server ansible.builtin.copy: remote_src: true src: "{{ aw_server_binary_path }}" dest: "{{ aw_release_install_dir }}/aw-server-rust" owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" mode: "0755" - name: Создать ссылку на активный бинарный файл AW server ansible.builtin.file: src: "{{ aw_release_install_dir }}/aw-server-rust" dest: /opt/activitywatch/bin/aw-server-rust owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" state: link force: true - name: Синхронизировать WebUI в RU каталог ansible.builtin.command: cmd: "rsync -a {{ aw_webui_source_path }}/ {{ aw_server_webui_dir }}/" - name: Настроить владельца файлов /opt/activitywatch ansible.builtin.file: path: /opt/activitywatch state: directory owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" recurse: true - name: Установить systemd service из шаблона репозитория ansible.builtin.copy: dest: /etc/systemd/system/activitywatch-server.service mode: "0644" content: >- {{ lookup('file', aw_repo_root + '/aw-server/activitywatch-server.service') | replace('__AW_SERVER_USER__', aw_server_user) | replace('__AW_SERVER_GROUP__', aw_server_group) | replace('__AW_SERVER_DATA_DIR__', aw_server_data_dir) }} notify: - Перезагрузить systemd - Перезапустить activitywatch - name: Скопировать RU patch файлы WebUI из репозитория ansible.builtin.copy: src: "{{ item.src }}" dest: "{{ item.dest }}" mode: "{{ item.mode }}" owner: "{{ aw_server_user }}" group: "{{ aw_server_group }}" loop: - { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" } - { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" } - { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" } - name: Проверить наличие index.html после копирования ansible.builtin.stat: path: "{{ aw_server_webui_dir }}/index.html" register: aw_webui_ru_index - name: Проверить, что index.html доступен для RU patch ansible.builtin.assert: that: - aw_webui_ru_index.stat.exists fail_msg: "Не найден index.html WebUI для применения RU patch." - name: Удалить старые теги RU patch из index.html ansible.builtin.replace: path: "{{ aw_server_webui_dir }}/index.html" regexp: ']+(?:ru-patch-v5\.js|sw-cleanup\.js|aw-ru-patch\.js|aw-sw-cleanup\.js)[^>]*>' replace: '' - name: Добавить cleanup script RU patch в index.html ansible.builtin.replace: path: "{{ aw_server_webui_dir }}/index.html" regexp: '' replace: '' - name: Добавить загрузчик RU patch перед закрытием body ansible.builtin.replace: path: "{{ aw_server_webui_dir }}/index.html" regexp: '' replace: '' - name: Записать /etc/activitywatch/aw-server.env ansible.builtin.copy: dest: /etc/activitywatch/aw-server.env mode: "0640" owner: root group: root content: | AW_SERVER_BIND_HOST={{ aw_server_bind_host }} AW_SERVER_PORT={{ aw_server_port }} AW_SERVER_DATA_DIR={{ aw_server_data_dir }} AW_SERVER_LOG_DIR={{ aw_server_log_dir }} AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }} AW_SERVER_USER={{ aw_server_user }} AW_SERVER_GROUP={{ aw_server_group }} - name: Включить и запустить сервис ansible.builtin.systemd: name: activitywatch-server.service enabled: true state: restarted daemon_reload: true - name: Дождаться ответа API ansible.builtin.uri: url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info" method: GET status_code: 200 register: aw_api retries: 10 delay: 3 until: aw_api.status == 200 - name: Применить базовые worktime settings (classes) ansible.builtin.uri: url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes" method: POST body: "{{ aw_worktime_classes }}" body_format: json status_code: [200, 201] when: aw_apply_worktime_settings | default(false) | bool - name: Применить базовые views для DLP и worktime ansible.builtin.uri: url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views" method: POST body: "{{ aw_default_views }}" body_format: json status_code: [200, 201] when: aw_apply_worktime_settings | default(false) | bool - name: Вычислить worktime durationDefault из aw_worktime_from/to ansible.builtin.set_fact: aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}" aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}" aw_worktime_to_h: "{{ (aw_worktime_to | default('17:00')).split(':')[0] | int }}" aw_worktime_to_m: "{{ (aw_worktime_to | default('17:00')).split(':')[1] | int }}" aw_worktime_duration_default_derived: >- {{ ( ( ((aw_worktime_to_h | int) * 60 + (aw_worktime_to_m | int)) - ((aw_worktime_from_h | int) * 60 + (aw_worktime_from_m | int)) ) * 60 ) }} when: aw_apply_worktime_settings | default(false) | bool - name: Нормализовать durationDefault для ночных смен ansible.builtin.set_fact: aw_worktime_duration_default_effective: >- {{ (aw_worktime_duration_default_derived | int) if (aw_worktime_duration_default_derived | int) > 0 else ((aw_worktime_duration_default_derived | int) + 86400) }} when: aw_apply_worktime_settings | default(false) | bool - name: Проверить корректность durationDefault ansible.builtin.assert: that: - aw_worktime_duration_default_effective | int > 0 - aw_worktime_duration_default_effective | int <= 86400 fail_msg: "Некорректный интервал рабочего времени: {{ aw_worktime_from }}..{{ aw_worktime_to }}" when: aw_apply_worktime_settings | default(false) | bool - name: Применить базовый период worktime (startOfDay) ansible.builtin.uri: url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay" method: POST body: "{{ aw_worktime_start_of_day }}" body_format: json status_code: 200 when: aw_apply_worktime_settings | default(false) | bool - name: Применить базовый период worktime (durationDefault seconds) ansible.builtin.uri: url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault" method: POST body: "{{ aw_worktime_duration_default_effective }}" body_format: json status_code: 200 when: aw_apply_worktime_settings | default(false) | bool handlers: - name: Перезагрузить systemd ansible.builtin.systemd: daemon_reload: true - name: Перезапустить activitywatch ansible.builtin.systemd: name: activitywatch-server.service state: restarted