feat: add professional deployment ensemble, PowerShell orchestration, and CI standards
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
* @igor04091968
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
## Summary
|
||||||
|
|
||||||
|
- what changed
|
||||||
|
- why it changed
|
||||||
|
- risk and rollback notes
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] No real secrets or credentials committed
|
||||||
|
- [ ] Server-side scripts validated (`bash -n`)
|
||||||
|
- [ ] PowerShell scripts validated (`Invoke-ScriptAnalyzer`)
|
||||||
|
- [ ] Docs updated (full paths and runbook steps)
|
||||||
|
- [ ] Rollback steps documented
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
name: shell-and-powershell-ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shell-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install shellcheck
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y shellcheck
|
||||||
|
|
||||||
|
- name: Run shellcheck
|
||||||
|
run: |
|
||||||
|
find . -type f -name "*.sh" -print0 | xargs -0 -r shellcheck
|
||||||
|
|
||||||
|
powershell-analyzer:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install PSScriptAnalyzer
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
|
||||||
|
Install-Module PSScriptAnalyzer -Scope CurrentUser -Force
|
||||||
|
|
||||||
|
- name: Analyze PowerShell scripts
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$targets = @(
|
||||||
|
"windows/*.ps1",
|
||||||
|
"windows/*.psm1",
|
||||||
|
"windows/*.psd1"
|
||||||
|
)
|
||||||
|
$issues = Invoke-ScriptAnalyzer -Path $targets -Recurse -Severity Error,Warning
|
||||||
|
if ($issues) {
|
||||||
|
$issues | Format-Table -AutoSize
|
||||||
|
throw "PSScriptAnalyzer detected issues."
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Local secrets
|
||||||
|
secrets/deploy.secrets.env
|
||||||
|
|
||||||
|
# Runtime / reports
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
windows/*.report.json
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Branching
|
||||||
|
|
||||||
|
- Работайте в feature-ветке, не пушьте напрямую в `main`.
|
||||||
|
- Именование: `feat/...`, `fix/...`, `docs/...`, `chore/...`.
|
||||||
|
|
||||||
|
## Commit style
|
||||||
|
|
||||||
|
- Предпочтительно Conventional Commits:
|
||||||
|
- `feat(...)`
|
||||||
|
- `fix(...)`
|
||||||
|
- `docs(...)`
|
||||||
|
- `chore(...)`
|
||||||
|
|
||||||
|
## Required checks before PR
|
||||||
|
|
||||||
|
- `bash -n` для всех `*.sh`.
|
||||||
|
- `Invoke-ScriptAnalyzer` для `windows/*.ps1`, `windows/*.psm1`, `windows/*.psd1`.
|
||||||
|
- Проверка, что нет секретов (`secrets/deploy.secrets.env` не должен быть в индексе git).
|
||||||
|
- Обновлены инструкции и runbook при изменении поведения.
|
||||||
|
|
||||||
|
## PR content
|
||||||
|
|
||||||
|
- Изменения и обоснование.
|
||||||
|
- Риск и rollback.
|
||||||
|
- Какие команды валидации были выполнены.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# ActivityWatch-Russian
|
# AWatch-rus
|
||||||
|
|
||||||
Практический каркас проекта для повторного развёртывания ActivityWatch Server в новом окружении с LXC-контейнером на Proxmox, русифицированным Web UI, systemd-юнитами, шаблонными скриптами деплоя и эксплуатационной документацией.
|
Практический каркас проекта для повторного развёртывания ActivityWatch Server в новом окружении с LXC-контейнером на Proxmox, русифицированным Web UI, systemd-юнитами, шаблонными скриптами деплоя и эксплуатационной документацией.
|
||||||
|
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
- `docs/deployment.md` — пошаговый деплой LXC и ActivityWatch Server.
|
- `docs/deployment.md` — пошаговый деплой LXC и ActivityWatch Server.
|
||||||
- `docs/runbook.md` — быстрый runbook для оператора.
|
- `docs/runbook.md` — быстрый runbook для оператора.
|
||||||
- `docs/operations.md` — регламент сопровождения, бэкапов, обновлений и rollback.
|
- `docs/operations.md` — регламент сопровождения, бэкапов, обновлений и rollback.
|
||||||
|
- `docs/windows/ensemble.md` — orchestration-пакет для Windows-деплоя и проверки.
|
||||||
- `proxmox/` — шаблонные скрипты подготовки и наполнения CT на стороне Proxmox.
|
- `proxmox/` — шаблонные скрипты подготовки и наполнения CT на стороне Proxmox.
|
||||||
- `aw-server/` — установочные скрипты, env-шаблон, systemd unit и RU patch для Web UI.
|
- `aw-server/` — установочные скрипты, env-шаблон, systemd unit и RU patch для Web UI.
|
||||||
|
- `ansible/` — Ansible-ensemble для автоматизированного сервера (Debian/CT).
|
||||||
|
- `windows/` — PowerShell toolkit: single-user, domain-users, ensemble orchestration, hardening/recovery, validation.
|
||||||
|
|
||||||
## Базовый сценарий
|
## Базовый сценарий
|
||||||
|
|
||||||
@@ -20,6 +23,8 @@
|
|||||||
5. Внутри контейнера выполнить `aw-server/install_aw_server.sh`.
|
5. Внутри контейнера выполнить `aw-server/install_aw_server.sh`.
|
||||||
6. Применить русификацию Web UI через `aw-server/apply_webui_ru_patch.sh`.
|
6. Применить русификацию Web UI через `aw-server/apply_webui_ru_patch.sh`.
|
||||||
7. Проверить API, Web UI и состояние systemd по `docs/runbook.md`.
|
7. Проверить API, Web UI и состояние systemd по `docs/runbook.md`.
|
||||||
|
8. Развернуть Windows-клиентов через `windows/deploy-ensemble.ps1`.
|
||||||
|
9. Проверить итог через `windows/validate-deployment.ps1`.
|
||||||
|
|
||||||
Скрипты `proxmox/create-ct.sh` и `proxmox/push-aw-artifacts.sh` по умолчанию читают:
|
Скрипты `proxmox/create-ct.sh` и `proxmox/push-aw-artifacts.sh` по умолчанию читают:
|
||||||
|
|
||||||
@@ -42,15 +47,17 @@
|
|||||||
|
|
||||||
## Ограничения
|
## Ограничения
|
||||||
|
|
||||||
- Windows-клиенты и watcher-автоматизация в этой части каркаса не описываются.
|
|
||||||
- Интеграции с InfluxDB/Grafana/LDAP оставлены как следующий слой, не как обязательная база.
|
- Интеграции с InfluxDB/Grafana/LDAP оставлены как следующий слой, не как обязательная база.
|
||||||
|
|
||||||
## Быстрые ссылки
|
## Быстрые ссылки
|
||||||
|
|
||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/docs/FULL_DEPLOYMENT_MANUAL_RU.md`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/docs/FULL_DEPLOYMENT_MANUAL_RU.md`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/docs/windows/ensemble.md`
|
||||||
- `docs/preparation.md`
|
- `docs/preparation.md`
|
||||||
- `docs/deployment.md`
|
- `docs/deployment.md`
|
||||||
- `docs/runbook.md`
|
- `docs/runbook.md`
|
||||||
- `docs/operations.md`
|
- `docs/operations.md`
|
||||||
- `proxmox/create-ct.sh`
|
- `proxmox/create-ct.sh`
|
||||||
- `aw-server/install_aw_server.sh`
|
- `aw-server/install_aw_server.sh`
|
||||||
|
- `windows/deploy-ensemble.ps1`
|
||||||
|
- `windows/validate-deployment.ps1`
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Ansible ensemble for AWatch-rus
|
||||||
|
|
||||||
|
Эта директория содержит минимальный Ansible-ensemble для повторяемого развёртывания ActivityWatch Server в Debian/LXC.
|
||||||
|
|
||||||
|
## Файлы
|
||||||
|
|
||||||
|
- `/home/igor/tmp/AWatch-rus/ansible/deploy_aw_server.yml` — основной playbook.
|
||||||
|
- `/home/igor/tmp/AWatch-rus/ansible/inventory.example.ini` — шаблон inventory.
|
||||||
|
- `/home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml` — шаблон переменных.
|
||||||
|
|
||||||
|
## Быстрый запуск
|
||||||
|
|
||||||
|
1. Скопируйте шаблоны:
|
||||||
|
- `cp /home/igor/tmp/AWatch-rus/ansible/inventory.example.ini /home/igor/tmp/AWatch-rus/ansible/inventory.ini`
|
||||||
|
- `cp /home/igor/tmp/AWatch-rus/ansible/group_vars/all.example.yml /home/igor/tmp/AWatch-rus/ansible/group_vars/all.yml`
|
||||||
|
2. Заполните значения в `inventory.ini` и `group_vars/all.yml`.
|
||||||
|
3. Запустите:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/igor/tmp/AWatch-rus/ansible
|
||||||
|
ansible-playbook -i inventory.ini deploy_aw_server.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
- Установлен ActivityWatch Server.
|
||||||
|
- Создан systemd-unit `activitywatch-server.service`.
|
||||||
|
- Установлен RU Web UI patch.
|
||||||
|
- Выполнена валидация API `http://127.0.0.1:5600/api/0/info`.
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy AWatch-rus server
|
||||||
|
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"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Install base packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- curl
|
||||||
|
- unzip
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Ensure service account exists
|
||||||
|
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: Ensure required directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ aw_server_user }}"
|
||||||
|
group: "{{ aw_server_group }}"
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- "{{ aw_release_root }}"
|
||||||
|
- "{{ aw_release_dir }}"
|
||||||
|
- "{{ aw_server_webui_dir }}"
|
||||||
|
- "{{ aw_server_data_dir }}"
|
||||||
|
- "{{ aw_server_log_dir }}"
|
||||||
|
- /etc/activitywatch
|
||||||
|
- "{{ aw_bootstrap_dir }}"
|
||||||
|
|
||||||
|
- name: Download ActivityWatch release archive
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ aw_server_download_url }}"
|
||||||
|
dest: "{{ aw_archive_path }}"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Unpack ActivityWatch release
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "{{ aw_archive_path }}"
|
||||||
|
dest: "{{ aw_release_dir }}"
|
||||||
|
remote_src: true
|
||||||
|
extra_opts: ["-o"]
|
||||||
|
|
||||||
|
- name: Discover extracted AW directory
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: "{{ aw_release_dir }}"
|
||||||
|
file_type: directory
|
||||||
|
patterns: "activitywatch*"
|
||||||
|
register: aw_release_find
|
||||||
|
|
||||||
|
- name: Set release extracted path
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
aw_release_extracted: "{{ (aw_release_find.files | sort(attribute='path') | map(attribute='path') | list | first) }}"
|
||||||
|
|
||||||
|
- name: Verify extracted directory exists
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- aw_release_extracted is defined
|
||||||
|
- aw_release_extracted | length > 0
|
||||||
|
fail_msg: "Cannot locate extracted ActivityWatch release directory."
|
||||||
|
|
||||||
|
- name: Sync release content to /opt/activitywatch
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "rsync -a --delete {{ aw_release_extracted }}/ /opt/activitywatch/"
|
||||||
|
|
||||||
|
- name: Copy bootstrap files from repository
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dest }}"
|
||||||
|
mode: "{{ item.mode }}"
|
||||||
|
loop:
|
||||||
|
- { src: "{{ aw_repo_root }}/aw-server/activitywatch-server.service", dest: "/etc/systemd/system/activitywatch-server.service", mode: "0644" }
|
||||||
|
- { 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" }
|
||||||
|
notify:
|
||||||
|
- Reload systemd
|
||||||
|
- Restart activitywatch
|
||||||
|
|
||||||
|
- name: Copy WebUI index template from installed distribution
|
||||||
|
ansible.builtin.copy:
|
||||||
|
remote_src: true
|
||||||
|
src: "/opt/activitywatch/aw-webui/index.html"
|
||||||
|
dest: "{{ aw_server_webui_dir }}/index.html"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Insert RU patch scripts into index.html
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
|
regexp: '</head>'
|
||||||
|
replace: '<script src="/js/sw-cleanup.js?v=ansible1"></script></head>'
|
||||||
|
|
||||||
|
- name: Insert RU patch loader before body end
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: "{{ aw_server_webui_dir }}/index.html"
|
||||||
|
regexp: '</body>'
|
||||||
|
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v=ansible1"></script></body>'
|
||||||
|
|
||||||
|
- name: Write /etc/activitywatch/aw-server.env
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/activitywatch/aw-server.env
|
||||||
|
mode: "0640"
|
||||||
|
content: |
|
||||||
|
AW_SERVER_HOST={{ aw_server_bind_host }}
|
||||||
|
AW_SERVER_PORT={{ aw_server_port }}
|
||||||
|
AW_DATA_DIR={{ aw_server_data_dir }}
|
||||||
|
AW_LOG_DIR={{ aw_server_log_dir }}
|
||||||
|
AW_WEBUI_DIR={{ aw_server_webui_dir }}
|
||||||
|
AW_SERVER_USER={{ aw_server_user }}
|
||||||
|
AW_SERVER_GROUP={{ aw_server_group }}
|
||||||
|
|
||||||
|
- name: Enable and start service
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: activitywatch-server.service
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Wait for 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
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: Reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Restart activitywatch
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: activitywatch-server.service
|
||||||
|
state: restarted
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
aw_server_version: "v0.13.2"
|
||||||
|
aw_server_download_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.zip"
|
||||||
|
aw_server_bind_host: "0.0.0.0"
|
||||||
|
aw_server_port: 5600
|
||||||
|
aw_server_webui_dir: "/opt/activitywatch/webui-ru"
|
||||||
|
aw_server_data_dir: "/var/lib/activitywatch"
|
||||||
|
aw_server_log_dir: "/var/log/activitywatch"
|
||||||
|
aw_server_user: "activitywatch"
|
||||||
|
aw_server_group: "activitywatch"
|
||||||
|
|
||||||
|
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[aw_server]
|
||||||
|
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||||
@@ -13,8 +13,11 @@
|
|||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/aw-server/apply_webui_ru_patch.sh`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/aw-server/apply_webui_ru_patch.sh`
|
||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/deploy-single-user.ps1`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/deploy-single-user.ps1`
|
||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/deploy-domain-users.ps1`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/deploy-domain-users.ps1`
|
||||||
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/deploy-ensemble.ps1`
|
||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/hardening-recovery.ps1`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/hardening-recovery.ps1`
|
||||||
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/validate-deployment.ps1`
|
||||||
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/browser-domains-native-collector.ps1`
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/windows/browser-domains-native-collector.ps1`
|
||||||
|
- `/mnt/usb_hdd2/Projects/ActivityWatch-Russian/ansible/deploy_aw_server.yml`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -154,6 +157,21 @@ C:\Deploy\ActivityWatch-Russian\windows\deploy-domain-users.ps1 `
|
|||||||
- `-Users 'CONTOSO\user01','CONTOSO\user02'`
|
- `-Users 'CONTOSO\user01','CONTOSO\user02'`
|
||||||
- `-UserListPath <txt|csv>`
|
- `-UserListPath <txt|csv>`
|
||||||
|
|
||||||
|
### 3.2.1 Ensemble orchestration (рекомендуется для production)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
C:\Deploy\ActivityWatch-Russian\windows\deploy-ensemble.ps1 `
|
||||||
|
-ServerHost aw.example.local `
|
||||||
|
-ServerPort 5600 `
|
||||||
|
-Domain CONTOSO `
|
||||||
|
-Users user1,user2,user3,user4,user5 `
|
||||||
|
-ValidateAfterDeploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Отчёт сохраняется в:
|
||||||
|
|
||||||
|
- `C:\ProgramData\ActivityWatch\ensemble-report-YYYYMMDD-HHMMSS.json`
|
||||||
|
|
||||||
### 3.3 Single-user развёртывание
|
### 3.3 Single-user развёртывание
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
@@ -171,6 +189,14 @@ C:\Deploy\ActivityWatch-Russian\windows\hardening-recovery.ps1 `
|
|||||||
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3.5 Валидация deployment-а (PowerShell report)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$report = C:\Deploy\ActivityWatch-Russian\windows\validate-deployment.ps1 `
|
||||||
|
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
||||||
|
$report | ConvertTo-Json -Depth 12
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4) Что должно появиться на Windows после установки
|
## 4) Что должно появиться на Windows после установки
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
- `windows/deploy-single-user.ps1` — развёртывание для одного пользователя.
|
- `windows/deploy-single-user.ps1` — развёртывание для одного пользователя.
|
||||||
- `windows/deploy-domain-users.ps1` — массовое развёртывание по списку пользователей.
|
- `windows/deploy-domain-users.ps1` — массовое развёртывание по списку пользователей.
|
||||||
|
- `windows/deploy-ensemble.ps1` — orchestration-скрипт полного цикла (deploy + hardening + validation).
|
||||||
- `windows/hardening-recovery.ps1` — повторная регистрация задач, ACL и recovery-loop.
|
- `windows/hardening-recovery.ps1` — повторная регистрация задач, ACL и recovery-loop.
|
||||||
|
- `windows/validate-deployment.ps1` — машинная проверка состояния и JSON-отчёт.
|
||||||
- `windows/browser-domains-native-collector.ps1` — native collector доменов браузера с категоризацией.
|
- `windows/browser-domains-native-collector.ps1` — native collector доменов браузера с категоризацией.
|
||||||
- `windows/web-category-rules.example.json` — пример кастомных правил категоризации.
|
- `windows/web-category-rules.example.json` — пример кастомных правил категоризации.
|
||||||
|
|
||||||
@@ -88,6 +90,21 @@ CSV-формат: колонка `User`, `Username`, `SamAccountName` или `Lo
|
|||||||
|
|
||||||
Если список уже содержит `DOMAIN\user`, параметр `-Domain` не нужен.
|
Если список уже содержит `DOMAIN\user`, параметр `-Domain` не нужен.
|
||||||
|
|
||||||
|
## Ensemble deploy (production workflow)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\windows\deploy-ensemble.ps1 `
|
||||||
|
-ServerHost aw.example.local `
|
||||||
|
-ServerPort 5600 `
|
||||||
|
-Domain CONTOSO `
|
||||||
|
-Users user1,user2,user3,user4,user5 `
|
||||||
|
-ValidateAfterDeploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Итоговый отчёт:
|
||||||
|
|
||||||
|
- `C:\ProgramData\ActivityWatch\ensemble-report-YYYYMMDD-HHMMSS.json`
|
||||||
|
|
||||||
## Категоризация доменов
|
## Категоризация доменов
|
||||||
|
|
||||||
- Встроенные категории покрывают базовые рабочие, нейтральные и личные домены.
|
- Встроенные категории покрывают базовые рабочие, нейтральные и личные домены.
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Windows deployment ensemble (PowerShell)
|
||||||
|
|
||||||
|
Документ фиксирует профессиональный workflow развёртывания и контроля ActivityWatch-клиентов в домене Windows.
|
||||||
|
|
||||||
|
## Полные пути
|
||||||
|
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/deploy-ensemble.ps1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/deploy-domain-users.ps1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/deploy-single-user.ps1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/hardening-recovery.ps1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/validate-deployment.ps1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/ActivityWatch.Windows.Common.psm1`
|
||||||
|
- `/home/igor/tmp/AWatch-rus/windows/ActivityWatch.Windows.Common.psd1`
|
||||||
|
|
||||||
|
## Рекомендованный запуск
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
|
||||||
|
|
||||||
|
C:\Deploy\AWatch-rus\windows\deploy-ensemble.ps1 `
|
||||||
|
-ServerHost 10.10.10.13 `
|
||||||
|
-ServerPort 5600 `
|
||||||
|
-Domain AD `
|
||||||
|
-Users user1,user2,user3,user4,user5 `
|
||||||
|
-ValidateAfterDeploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что делает `deploy-ensemble.ps1`
|
||||||
|
|
||||||
|
1. Нормализует список пользователей (`DOMAIN\user`).
|
||||||
|
2. Вызывает массовый деплой `deploy-domain-users.ps1`.
|
||||||
|
3. Применяет hardening/recovery (`hardening-recovery.ps1`), если не задан `-SkipHardening`.
|
||||||
|
4. Опционально запускает контроль (`validate-deployment.ps1`) при `-ValidateAfterDeploy`.
|
||||||
|
5. Пишет итоговый JSON-отчёт в `C:\ProgramData\ActivityWatch\ensemble-report-*.json`.
|
||||||
|
|
||||||
|
## Быстрый health-check
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$report = C:\Deploy\AWatch-rus\windows\validate-deployment.ps1 `
|
||||||
|
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
||||||
|
$report | ConvertTo-Json -Depth 12
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
|
||||||
|
- `overallOk = true`
|
||||||
|
- нет missing-файлов
|
||||||
|
- все `ActivityWatch*` Scheduled Task присутствуют
|
||||||
|
- активны процессы `aw-watcher-afk` и `aw-watcher-window`
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
# Windows validation
|
# Windows validation
|
||||||
|
|
||||||
|
## Автоматическая проверка (рекомендуется)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$report = .\windows\validate-deployment.ps1 `
|
||||||
|
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
||||||
|
$report | ConvertTo-Json -Depth 12
|
||||||
|
```
|
||||||
|
|
||||||
|
Критерий:
|
||||||
|
|
||||||
|
- `overallOk = true`
|
||||||
|
|
||||||
## Базовая проверка после развёртывания
|
## Базовая проверка после развёртывания
|
||||||
|
|
||||||
### 1. Проверить установленные файлы
|
### 1. Проверить установленные файлы
|
||||||
@@ -59,7 +71,7 @@ Invoke-WebRequest https://aw.example.local/api/0/info
|
|||||||
|
|
||||||
- `aw-watcher-window_<hostname>`
|
- `aw-watcher-window_<hostname>`
|
||||||
- `aw-watcher-web-edge_<hostname>` или другой browser bucket
|
- `aw-watcher-web-edge_<hostname>` или другой browser bucket
|
||||||
- `aw-watcher-web-category_<hostname>`
|
- `aw-detmir-web-category_<hostname>`
|
||||||
|
|
||||||
Проверка через API:
|
Проверка через API:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
@{
|
||||||
|
RootModule = 'ActivityWatch.Windows.Common.psm1'
|
||||||
|
ModuleVersion = '1.0.0'
|
||||||
|
GUID = '90b3fcf6-df9f-4f9b-9ee0-8a7de4dc0ee2'
|
||||||
|
Author = 'igor04091968'
|
||||||
|
CompanyName = 'Private'
|
||||||
|
Description = 'Common PowerShell functions for ActivityWatch Windows deployment, hardening and recovery.'
|
||||||
|
PowerShellVersion = '5.1'
|
||||||
|
FunctionsToExport = @(
|
||||||
|
'*-ActivityWatch*',
|
||||||
|
'Assert-Administrator',
|
||||||
|
'Normalize-ActivityWatchUsers',
|
||||||
|
'Get-ActivityWatchPackageUrl',
|
||||||
|
'Remove-LegacyActivityWatchEntries'
|
||||||
|
)
|
||||||
|
CmdletsToExport = @()
|
||||||
|
VariablesToExport = '*'
|
||||||
|
AliasesToExport = @()
|
||||||
|
PrivateData = @{
|
||||||
|
PSData = @{
|
||||||
|
Tags = @('ActivityWatch', 'Windows', 'Deployment', 'Recovery')
|
||||||
|
ProjectUri = 'https://github.com/igor04091968/AWatch-rus'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ServerHost,
|
||||||
|
[string[]]$Users,
|
||||||
|
[string]$UserListPath,
|
||||||
|
[string]$Domain,
|
||||||
|
[int]$ServerPort = 5600,
|
||||||
|
[ValidateSet('http', 'https')]
|
||||||
|
[string]$ServerScheme = 'http',
|
||||||
|
[string]$Version = 'v0.13.2',
|
||||||
|
[string]$PackageUrl,
|
||||||
|
[string]$PackageZipPath,
|
||||||
|
[string]$InstallRoot = 'C:\Program Files\ActivityWatch',
|
||||||
|
[string]$StateRoot = 'C:\ProgramData\ActivityWatch',
|
||||||
|
[int]$PollSeconds = 5,
|
||||||
|
[int]$PulseSeconds = 30,
|
||||||
|
[int]$RecoveryIntervalSeconds = 180,
|
||||||
|
[string]$CustomRulesPath,
|
||||||
|
[string]$ReportPath,
|
||||||
|
[switch]$SkipHardening,
|
||||||
|
[switch]$ValidateAfterDeploy
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$modulePath = Join-Path $PSScriptRoot 'ActivityWatch.Windows.Common.psm1'
|
||||||
|
Import-Module $modulePath -Force
|
||||||
|
|
||||||
|
Assert-Administrator
|
||||||
|
|
||||||
|
$resolvedUsers = Normalize-ActivityWatchUsers -Users $Users -UserListPath $UserListPath -Domain $Domain
|
||||||
|
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||||
|
$effectiveReportPath = if ($ReportPath) { $ReportPath } else { Join-Path $StateRoot "ensemble-report-$timestamp.json" }
|
||||||
|
$deployScript = Join-Path $PSScriptRoot 'deploy-domain-users.ps1'
|
||||||
|
$hardeningScript = Join-Path $PSScriptRoot 'hardening-recovery.ps1'
|
||||||
|
$validationScript = Join-Path $PSScriptRoot 'validate-deployment.ps1'
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $deployScript)) {
|
||||||
|
throw "Missing script: $deployScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
& $deployScript `
|
||||||
|
-ServerHost $ServerHost `
|
||||||
|
-Users $resolvedUsers `
|
||||||
|
-ServerPort $ServerPort `
|
||||||
|
-ServerScheme $ServerScheme `
|
||||||
|
-Version $Version `
|
||||||
|
-PackageUrl $PackageUrl `
|
||||||
|
-PackageZipPath $PackageZipPath `
|
||||||
|
-InstallRoot $InstallRoot `
|
||||||
|
-StateRoot $StateRoot `
|
||||||
|
-PollSeconds $PollSeconds `
|
||||||
|
-PulseSeconds $PulseSeconds `
|
||||||
|
-RecoveryIntervalSeconds $RecoveryIntervalSeconds `
|
||||||
|
-CustomRulesPath $CustomRulesPath
|
||||||
|
|
||||||
|
if (-not $SkipHardening) {
|
||||||
|
& $hardeningScript `
|
||||||
|
-ConfigPath (Join-Path $StateRoot 'deployment-config.json') `
|
||||||
|
-ServerHost $ServerHost `
|
||||||
|
-ServerPort $ServerPort `
|
||||||
|
-ServerScheme $ServerScheme `
|
||||||
|
-Users $resolvedUsers `
|
||||||
|
-InstallRoot $InstallRoot `
|
||||||
|
-StateRoot $StateRoot `
|
||||||
|
-PollSeconds $PollSeconds `
|
||||||
|
-PulseSeconds $PulseSeconds `
|
||||||
|
-RecoveryIntervalSeconds $RecoveryIntervalSeconds `
|
||||||
|
-CustomRulesPath $CustomRulesPath
|
||||||
|
}
|
||||||
|
|
||||||
|
$report = [ordered]@{
|
||||||
|
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
|
||||||
|
server = [ordered]@{
|
||||||
|
host = $ServerHost
|
||||||
|
port = $ServerPort
|
||||||
|
scheme = $ServerScheme
|
||||||
|
}
|
||||||
|
packageVersion = $Version
|
||||||
|
users = @($resolvedUsers)
|
||||||
|
paths = [ordered]@{
|
||||||
|
installRoot = $InstallRoot
|
||||||
|
stateRoot = $StateRoot
|
||||||
|
configPath = Join-Path $StateRoot 'deployment-config.json'
|
||||||
|
}
|
||||||
|
hardeningApplied = (-not $SkipHardening)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ValidateAfterDeploy) {
|
||||||
|
if (-not (Test-Path -LiteralPath $validationScript)) {
|
||||||
|
throw "Missing script: $validationScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
$validation = & $validationScript -ConfigPath (Join-Path $StateRoot 'deployment-config.json')
|
||||||
|
$report.validation = $validation
|
||||||
|
}
|
||||||
|
|
||||||
|
$reportDirectory = Split-Path -Path $effectiveReportPath -Parent
|
||||||
|
if ($reportDirectory) {
|
||||||
|
New-ActivityWatchDirectory -Path $reportDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
$report | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $effectiveReportPath -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host 'ActivityWatch ensemble deploy completed.'
|
||||||
|
Write-Host "Users: $($resolvedUsers -join ', ')"
|
||||||
|
Write-Host "Report: $effectiveReportPath"
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$ConfigPath = 'C:\ProgramData\ActivityWatch\deployment-config.json'
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$modulePath = Join-Path $PSScriptRoot 'ActivityWatch.Windows.Common.psm1'
|
||||||
|
Import-Module $modulePath -Force
|
||||||
|
|
||||||
|
$config = Read-ActivityWatchDeploymentConfig -Path $ConfigPath
|
||||||
|
$installRoot = [string]$config.paths.installRoot
|
||||||
|
$stateRoot = [string]$config.paths.stateRoot
|
||||||
|
$collectorScript = [string]$config.paths.collectorScript
|
||||||
|
$rulesPath = [string]$config.paths.rulesPath
|
||||||
|
$launchScript = [string]$config.paths.launchScript
|
||||||
|
$recoveryScript = [string]$config.paths.recoveryScript
|
||||||
|
|
||||||
|
$requiredFiles = @(
|
||||||
|
(Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe'),
|
||||||
|
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
|
||||||
|
$collectorScript,
|
||||||
|
$rulesPath,
|
||||||
|
$launchScript,
|
||||||
|
$recoveryScript,
|
||||||
|
$ConfigPath
|
||||||
|
)
|
||||||
|
|
||||||
|
$missingFiles = @(
|
||||||
|
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
|
||||||
|
)
|
||||||
|
|
||||||
|
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
|
||||||
|
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
|
||||||
|
|
||||||
|
$taskNames = @()
|
||||||
|
if ($config.userTasks) {
|
||||||
|
$taskNames += @($config.userTasks | ForEach-Object { [string]$_.launchTaskName })
|
||||||
|
}
|
||||||
|
$taskNames += [string]$config.recovery.taskName
|
||||||
|
$taskNames = $taskNames | Sort-Object -Unique
|
||||||
|
|
||||||
|
$tasks = foreach ($taskName in $taskNames) {
|
||||||
|
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||||
|
if ($task) {
|
||||||
|
[pscustomobject]@{
|
||||||
|
taskName = $task.TaskName
|
||||||
|
state = [string]$task.State
|
||||||
|
present = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[pscustomobject]@{
|
||||||
|
taskName = $taskName
|
||||||
|
state = 'Missing'
|
||||||
|
present = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverUrl = '{0}://{1}:{2}' -f [string]$config.server.scheme, [string]$config.server.host, [int]$config.server.port
|
||||||
|
$result = [ordered]@{
|
||||||
|
generatedAtUtc = (Get-Date).ToUniversalTime().ToString('o')
|
||||||
|
configPath = $ConfigPath
|
||||||
|
serverUrl = $serverUrl
|
||||||
|
installRoot = $installRoot
|
||||||
|
stateRoot = $stateRoot
|
||||||
|
files = [ordered]@{
|
||||||
|
required = $requiredFiles
|
||||||
|
missing = $missingFiles
|
||||||
|
ok = ($missingFiles.Count -eq 0)
|
||||||
|
}
|
||||||
|
tasks = [ordered]@{
|
||||||
|
list = $tasks
|
||||||
|
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
|
||||||
|
}
|
||||||
|
processes = [ordered]@{
|
||||||
|
list = @($runningProcesses)
|
||||||
|
ok = [bool](($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result.overallOk = [bool]($result.files.ok -and $result.tasks.ok -and $result.processes.ok)
|
||||||
|
|
||||||
|
$result
|
||||||
Reference in New Issue
Block a user