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-юнитами, шаблонными скриптами деплоя и эксплуатационной документацией.
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
- `docs/deployment.md` — пошаговый деплой LXC и ActivityWatch Server.
|
||||
- `docs/runbook.md` — быстрый runbook для оператора.
|
||||
- `docs/operations.md` — регламент сопровождения, бэкапов, обновлений и rollback.
|
||||
- `docs/windows/ensemble.md` — orchestration-пакет для Windows-деплоя и проверки.
|
||||
- `proxmox/` — шаблонные скрипты подготовки и наполнения CT на стороне Proxmox.
|
||||
- `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`.
|
||||
6. Применить русификацию Web UI через `aw-server/apply_webui_ru_patch.sh`.
|
||||
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` по умолчанию читают:
|
||||
|
||||
@@ -42,15 +47,17 @@
|
||||
|
||||
## Ограничения
|
||||
|
||||
- Windows-клиенты и watcher-автоматизация в этой части каркаса не описываются.
|
||||
- Интеграции с InfluxDB/Grafana/LDAP оставлены как следующий слой, не как обязательная база.
|
||||
|
||||
## Быстрые ссылки
|
||||
|
||||
- `/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/deployment.md`
|
||||
- `docs/runbook.md`
|
||||
- `docs/operations.md`
|
||||
- `proxmox/create-ct.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/windows/deploy-single-user.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/validate-deployment.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'`
|
||||
- `-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 развёртывание
|
||||
|
||||
```powershell
|
||||
@@ -171,6 +189,14 @@ C:\Deploy\ActivityWatch-Russian\windows\hardening-recovery.ps1 `
|
||||
-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,7 +4,9 @@
|
||||
|
||||
- `windows/deploy-single-user.ps1` — развёртывание для одного пользователя.
|
||||
- `windows/deploy-domain-users.ps1` — массовое развёртывание по списку пользователей.
|
||||
- `windows/deploy-ensemble.ps1` — orchestration-скрипт полного цикла (deploy + hardening + validation).
|
||||
- `windows/hardening-recovery.ps1` — повторная регистрация задач, ACL и recovery-loop.
|
||||
- `windows/validate-deployment.ps1` — машинная проверка состояния и JSON-отчёт.
|
||||
- `windows/browser-domains-native-collector.ps1` — native collector доменов браузера с категоризацией.
|
||||
- `windows/web-category-rules.example.json` — пример кастомных правил категоризации.
|
||||
|
||||
@@ -88,6 +90,21 @@ CSV-формат: колонка `User`, `Username`, `SamAccountName` или `Lo
|
||||
|
||||
Если список уже содержит `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
|
||||
|
||||
## Автоматическая проверка (рекомендуется)
|
||||
|
||||
```powershell
|
||||
$report = .\windows\validate-deployment.ps1 `
|
||||
-ConfigPath C:\ProgramData\ActivityWatch\deployment-config.json
|
||||
$report | ConvertTo-Json -Depth 12
|
||||
```
|
||||
|
||||
Критерий:
|
||||
|
||||
- `overallOk = true`
|
||||
|
||||
## Базовая проверка после развёртывания
|
||||
|
||||
### 1. Проверить установленные файлы
|
||||
@@ -59,7 +71,7 @@ Invoke-WebRequest https://aw.example.local/api/0/info
|
||||
|
||||
- `aw-watcher-window_<hostname>`
|
||||
- `aw-watcher-web-edge_<hostname>` или другой browser bucket
|
||||
- `aw-watcher-web-category_<hostname>`
|
||||
- `aw-detmir-web-category_<hostname>`
|
||||
|
||||
Проверка через 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