standkit 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {standkit-0.6.0/standkit.egg-info → standkit-0.7.0}/PKG-INFO +7 -2
- {standkit-0.6.0 → standkit-0.7.0}/README.md +6 -1
- {standkit-0.6.0 → standkit-0.7.0}/docs/ARCHITECTURE.md +8 -0
- standkit-0.7.0/docs/BACKLOG.md +112 -0
- standkit-0.7.0/docs/CHANGELOG.md +344 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/REMOTE_STANDS.md +90 -9
- {standkit-0.6.0 → standkit-0.7.0}/docs/adr/0004-iis-site-scoped-lifecycle.md +37 -0
- {standkit-0.6.0 → standkit-0.7.0}/projects.sample.json +6 -0
- {standkit-0.6.0 → standkit-0.7.0}/pyproject.toml +1 -1
- {standkit-0.6.0 → standkit-0.7.0}/standkit/__init__.py +1 -1
- {standkit-0.6.0 → standkit-0.7.0}/standkit/health.py +30 -6
- standkit-0.7.0/standkit/hosting.py +1492 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/models.py +39 -2
- {standkit-0.6.0 → standkit-0.7.0}/standkit/platform.py +51 -5
- {standkit-0.6.0 → standkit-0.7.0/standkit.egg-info}/PKG-INFO +7 -2
- {standkit-0.6.0 → standkit-0.7.0}/standkit.egg-info/SOURCES.txt +3 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/__main__.py +21 -2
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/server.py +108 -1
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/shortcut.py +6 -1
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/app.js +67 -21
- standkit-0.7.0/standkit_hub/web/cookbook.html +1868 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/index.html +16 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/style.css +35 -0
- standkit-0.7.0/tests/test_health.py +319 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hosting.py +543 -34
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_server.py +20 -0
- standkit-0.7.0/tests/test_hub_single_instance.py +200 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_models.py +46 -0
- standkit-0.7.0/tests/test_no_window.py +298 -0
- standkit-0.6.0/docs/BACKLOG.md +0 -86
- standkit-0.6.0/docs/CHANGELOG.md +0 -111
- standkit-0.6.0/standkit/hosting.py +0 -895
- standkit-0.6.0/tests/test_health.py +0 -146
- {standkit-0.6.0 → standkit-0.7.0}/LICENSE +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/MANIFEST.in +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/SECURITY.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/HOSTING.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/ROADMAP.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/adr/0001-hosting-backends.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/adr/0002-k8s-backend.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/docs/adr/0003-remote-log-browsing.md +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/setup.cfg +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/adopt.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/lifecycle.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/logs.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/registry.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit/secrets.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit.egg-info/dependency_links.txt +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit.egg-info/entry_points.txt +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit.egg-info/requires.txt +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit.egg-info/top_level.txt +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_agent/__init__.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_agent/__main__.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_agent/audit.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_agent/security.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_agent/server.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/__init__.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/agent_control.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/assets/bpmkit-icon.ico +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/assets/icon.png +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/client.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/config.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/logs_browser.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/poller.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/redis_min.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/security.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/bpmkit-logo-dark.svg +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/bpmkit-logo.svg +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/favicon.png +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/favicon.svg +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/standkit_hub/web/manifest.webmanifest +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_adopt.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_agent_security.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_agent_server_integration.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_agent_control.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_config.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_fast_paint.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_logs_browser.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_pwa_and_compact.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_redis_min.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_register.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_hub_shortcut.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_lifecycle.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_logs.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_registry.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_registry_resolver.py +0 -0
- {standkit-0.6.0 → standkit-0.7.0}/tests/test_secrets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: standkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Свободное ядро (MIT) для управления жизненным циклом локальных и удалённых стендов BPMSoft: headless start/stop/restart, health-пробы, логи, реестр стендов. Часть экосистемы BPMkit.
|
|
5
5
|
Author: standkit contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -58,6 +58,7 @@ Dynamic: license-file
|
|
|
58
58
|
- **Логи** — просмотр логов текущей сессии стенда, открытие папки логов стенда и папки BPMkit-проекта.
|
|
59
59
|
- **Redis** — очистка кэша стенда (номер БД берётся из конфигурации стенда).
|
|
60
60
|
- **Тёмная и светлая тема** с переключателем.
|
|
61
|
+
- **Встроенная справка** — кнопка «?» в шапке открывает кукбук: установка, работа с дашбордом, удалённые агенты, траблшутинг. Один самодостаточный файл, работает офлайн.
|
|
61
62
|
- **Безопасность по умолчанию** — дашборд слушает только `127.0.0.1`, сессионный токен, защита мутаций (CSRF + проверка Origin).
|
|
62
63
|
- **Удалённые стенды** *(в развитии)* — через лёгкие кроссплатформенные headless-агенты с TLS/mTLS.
|
|
63
64
|
|
|
@@ -116,9 +117,12 @@ TLS, TLS 1.2+/mTLS, скоупы control/readonly, lockout по IP, аудит.
|
|
|
116
117
|
Нужен Python 3.10+.
|
|
117
118
|
|
|
118
119
|
```bash
|
|
119
|
-
pip install
|
|
120
|
+
pip install standkit
|
|
120
121
|
```
|
|
121
122
|
|
|
123
|
+
Обновление — `pip install -U standkit`. Свежий `main` до релиза (если нужна ещё не выпущенная
|
|
124
|
+
правка) — `pip install --force-reinstall "git+https://github.com/thinkquattro/BPMkitStand.git"`.
|
|
125
|
+
|
|
122
126
|
Запуск дашборда:
|
|
123
127
|
|
|
124
128
|
```bash
|
|
@@ -142,6 +146,7 @@ BPMkitStand использует тот же реестр, что и MCP BPMkit
|
|
|
142
146
|
|
|
143
147
|
## Документация проекта
|
|
144
148
|
|
|
149
|
+
- [standkit_hub/web/cookbook.html](standkit_hub/web/cookbook.html) — **кукбук**: обзор, установка (отдельно Windows и Linux), обновление, дашборд, реестр стендов, виды хостинга, удалённые стенды и агент-демон, безопасность, траблшутинг. Один standalone-файл: в работающем диспетчере открывается кнопкой «?» в шапке, без него — прямо с диска в браузере.
|
|
145
150
|
- [docs/ROADMAP.md](docs/ROADMAP.md) — дорожная карта приложения (что сделано / ближайшее / бэклог).
|
|
146
151
|
- [docs/CHANGELOG.md](docs/CHANGELOG.md) — история изменений по версиям.
|
|
147
152
|
- [docs/BACKLOG.md](docs/BACKLOG.md) — честный список «упомянуто в коде, но не реализовано» (заделы/заглушки) со ссылками на код.
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
- **Логи** — просмотр логов текущей сессии стенда, открытие папки логов стенда и папки BPMkit-проекта.
|
|
35
35
|
- **Redis** — очистка кэша стенда (номер БД берётся из конфигурации стенда).
|
|
36
36
|
- **Тёмная и светлая тема** с переключателем.
|
|
37
|
+
- **Встроенная справка** — кнопка «?» в шапке открывает кукбук: установка, работа с дашбордом, удалённые агенты, траблшутинг. Один самодостаточный файл, работает офлайн.
|
|
37
38
|
- **Безопасность по умолчанию** — дашборд слушает только `127.0.0.1`, сессионный токен, защита мутаций (CSRF + проверка Origin).
|
|
38
39
|
- **Удалённые стенды** *(в развитии)* — через лёгкие кроссплатформенные headless-агенты с TLS/mTLS.
|
|
39
40
|
|
|
@@ -92,9 +93,12 @@ TLS, TLS 1.2+/mTLS, скоупы control/readonly, lockout по IP, аудит.
|
|
|
92
93
|
Нужен Python 3.10+.
|
|
93
94
|
|
|
94
95
|
```bash
|
|
95
|
-
pip install
|
|
96
|
+
pip install standkit
|
|
96
97
|
```
|
|
97
98
|
|
|
99
|
+
Обновление — `pip install -U standkit`. Свежий `main` до релиза (если нужна ещё не выпущенная
|
|
100
|
+
правка) — `pip install --force-reinstall "git+https://github.com/thinkquattro/BPMkitStand.git"`.
|
|
101
|
+
|
|
98
102
|
Запуск дашборда:
|
|
99
103
|
|
|
100
104
|
```bash
|
|
@@ -118,6 +122,7 @@ BPMkitStand использует тот же реестр, что и MCP BPMkit
|
|
|
118
122
|
|
|
119
123
|
## Документация проекта
|
|
120
124
|
|
|
125
|
+
- [standkit_hub/web/cookbook.html](standkit_hub/web/cookbook.html) — **кукбук**: обзор, установка (отдельно Windows и Linux), обновление, дашборд, реестр стендов, виды хостинга, удалённые стенды и агент-демон, безопасность, траблшутинг. Один standalone-файл: в работающем диспетчере открывается кнопкой «?» в шапке, без него — прямо с диска в браузере.
|
|
121
126
|
- [docs/ROADMAP.md](docs/ROADMAP.md) — дорожная карта приложения (что сделано / ближайшее / бэклог).
|
|
122
127
|
- [docs/CHANGELOG.md](docs/CHANGELOG.md) — история изменений по версиям.
|
|
123
128
|
- [docs/BACKLOG.md](docs/BACKLOG.md) — честный список «упомянуто в коде, но не реализовано» (заделы/заглушки) со ссылками на код.
|
|
@@ -62,6 +62,14 @@ standkit_hub — MIT, stdlib-only локальный веб-дашборд (
|
|
|
62
62
|
ветки для `sys.platform == "win32"` (скрытое консольное окно,
|
|
63
63
|
`CREATE_NO_WINDOW` + отдельная группа процессов) и POSIX
|
|
64
64
|
(`start_new_session=True`, эквивалент `setsid`).
|
|
65
|
+
- Запуск ВНЕШНИХ КОНСОЛЬНЫХ УТИЛИТ (`appcmd`, `sc`, `docker`, `kubectl`,
|
|
66
|
+
`taskkill`, `tasklist`, `powershell`) — ТОЛЬКО через
|
|
67
|
+
`standkit/platform.py::run_console`, который на win32 добавляет тот же
|
|
68
|
+
`CREATE_NO_WINDOW`. Прямой `subprocess.run` в остальных модулях пакета
|
|
69
|
+
запрещён и стережётся тестом `tests/test_no_window.py`: у родителя без
|
|
70
|
+
своей консоли (`pythonw`, служба, фоновый поллер хаба) каждый такой вызов
|
|
71
|
+
рождает на экране мигающее чёрное окно, а из терминала дефект не виден
|
|
72
|
+
(GAP-138: поллер опрашивал IIS-стенд парой `appcmd` раз в ~12 с).
|
|
65
73
|
- `standkit_agent` — только `stdlib` (`http.server`, `subprocess`, `socket`,
|
|
66
74
|
`urllib`), потому что стенды BPMSoft на .NET штатно живут и под Linux, а
|
|
67
75
|
агент должен разворачиваться на голом хосте без сборки колёс под
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Бэклог standkit / BPMkitStand
|
|
2
|
+
|
|
3
|
+
Честный список того, что **упомянуто в коде, но ещё не реализовано / не
|
|
4
|
+
настроено** (каркас, заглушки, зарезервированные значения), с точной ссылкой на
|
|
5
|
+
место в коде. Цель — чтобы пользователь пакета и разработчик не принимали
|
|
6
|
+
«задел» за готовую фичу.
|
|
7
|
+
|
|
8
|
+
Метка **[упомянут в коде]** = в коде есть символ/ветка/поле, но реального
|
|
9
|
+
поведения за ним нет (или оно возвращает заглушку).
|
|
10
|
+
|
|
11
|
+
Порядок дорожной карты — см. [ROADMAP.md](ROADMAP.md). Остаточная безопасность
|
|
12
|
+
агента — `SECURITY.md` §6.
|
|
13
|
+
|
|
14
|
+
## Транспорты подключения к стенду
|
|
15
|
+
|
|
16
|
+
| Пункт | Статус | Упомянут в коде |
|
|
17
|
+
|------|--------|-----------------|
|
|
18
|
+
| Транспорт **`ssh`** | зарезервирован в enum, реализации нет → `NotImplementedError` | `standkit/models.py::Transport.SSH` («задел на будущее»); фолбэк-`raise` в `standkit_hub/client.py::FederatedClient.status/_dispatch_action/logs` |
|
|
19
|
+
| Транспорт **`winrm`** | то же | `standkit/models.py::Transport.WINRM`; там же в `client.py` |
|
|
20
|
+
|
|
21
|
+
Рабочие транспорты — только `local` и `agent` (оба реализованы; в UI регистрации
|
|
22
|
+
предлагаются только они). `ssh`/`winrm` достижимы лишь ручной правкой
|
|
23
|
+
`projects.json` и упадут с `NotImplementedError`.
|
|
24
|
+
|
|
25
|
+
## Health-пробы
|
|
26
|
+
|
|
27
|
+
| Пункт | Статус | Упомянут в коде |
|
|
28
|
+
|------|--------|-----------------|
|
|
29
|
+
| Глубокая проба БД (`SELECT 1` через psycopg2/pyodbc) | заглушка, всегда `SKIPPED`; флаг `deep_db` из хаба не передаётся | `standkit/health.py::db_deep_check`, параметр `check_stand(deep_db=…)` |
|
|
30
|
+
| Глубокая проба Redis (`PING` через redis-py) | заглушка, всегда `SKIPPED` | `standkit/health.py::redis_deep_check`, `check_stand(deep_redis=…)` |
|
|
31
|
+
| `stand_scheme` / `verify_tls` в форме регистрации дашборда | поля есть в реестре и в пробе, но форма регистрации построена по фиксированному белому списку полей — задать схему можно только правкой `projects.json` | `standkit_hub/server.py::_REGISTER_ALLOWED_FIELDS`, `standkit_hub/web/app.js::_REGISTER_FIELD_NAMES` |
|
|
32
|
+
| `last_deploy` (состояние последнего деплоя) | всегда `UNKNOWN`, источник данных не определён, в UI не выводится | `standkit/health.py::check_stand` (комментарий `last_deploy`), `standkit/models.py::StandStatus.last_deploy` |
|
|
33
|
+
|
|
34
|
+
Рабочие пробы (без заглушек): процесс (pid/бэкенд), HTTP, БД/Redis по **открытому
|
|
35
|
+
TCP-порту** — их достаточно для дашборда.
|
|
36
|
+
|
|
37
|
+
## Жизненный цикл (kestrel)
|
|
38
|
+
|
|
39
|
+
| Пункт | Статус | Упомянут в коде |
|
|
40
|
+
|------|--------|-----------------|
|
|
41
|
+
| Polling готовности веб-хоста после `start()` | не реализован (возвращается pid; готовность проверяется отдельно) | `standkit/lifecycle.py` (докстринг модуля) |
|
|
42
|
+
| Блокировка pidfile от гонки двух `start()` | не реализована | там же |
|
|
43
|
+
| Восстановление «потерянного» pidfile | **реализовано** — поиск владельца порта, валидация (порт + allowlist образа + рабочий каталог внутри `stand_dir`) и усыновление с подтверждением | `standkit/adopt.py`, `standkit/lifecycle.py::adopt` |
|
|
44
|
+
| Доп. окружение процесса (`ASPNETCORE_ENVIRONMENT` и т.п.) | «зона расширения следующей итерации» | `standkit/lifecycle.py::_kestrel_start` |
|
|
45
|
+
| Graceful stop (SIGTERM→таймаут→SIGKILL / CTRL_BREAK_EVENT) | **реализовано** — эскалация мягко → таймаут → жёстко | `standkit/platform.py::stop` |
|
|
46
|
+
| Рабочий каталог процесса для WOW64 (32-битный стенд под 64-битным Python) | не реализовано — усыновление таких процессов даёт честный отказ | `standkit/adopt.py::_windows_process_cwd` |
|
|
47
|
+
| Windows Job Object / Linux double-fork | упрощённо (CREATE_NO_WINDOW / setsid) | `standkit/platform.py` (докстринг модуля) |
|
|
48
|
+
|
|
49
|
+
## Логи
|
|
50
|
+
|
|
51
|
+
| Пункт | Статус | Упомянут в коде |
|
|
52
|
+
|------|--------|-----------------|
|
|
53
|
+
| Обработка ротации лог-файла в `follow()` | не реализована (при пересоздании файла позиция «уезжает») | `standkit/logs.py::follow` |
|
|
54
|
+
| Постраничное чтение очень больших логов | частично: `tail(max_bytes=…)` читает хвост; полноценной постраничной навигации нет | `standkit/logs.py::tail` |
|
|
55
|
+
|
|
56
|
+
## Хаб / клиент
|
|
57
|
+
|
|
58
|
+
| Пункт | Статус | Упомянут в коде |
|
|
59
|
+
|------|--------|-----------------|
|
|
60
|
+
| Параллельный опрос агентов | **реализовано** — `ThreadPoolExecutor`, порядок результатов сохраняет порядок реестра | `standkit_hub/client.py::FederatedClient.status_all` |
|
|
61
|
+
| Кэш/дебаунс частых опросов | **реализовано** — фоновый поллер держит снапшот состояния, конфиг и реестр кэшируются по отпечатку файла | `standkit_hub/poller.py`, `standkit_hub/server.py::build_snapshot` |
|
|
62
|
+
| CLI регистрации стенда (`standkit register …`) | не реализовано: запись в реестр возможна только из UI (`POST /api/stand/register`), правкой `projects.json` руками или `Registry.add_existing()` из Python. На headless-хосте агента UI недоступен (хаб слушает loopback) — обходной рецепт живёт в кукбуке | `standkit/registry.py::Registry.add_existing`, `standkit_hub/server.py` (маршрут регистрации) |
|
|
63
|
+
| Ярлык на рабочем столе для **macOS** | осознанно не реализовано — возвращается понятный статус | `standkit_hub/shortcut.py` (докстринг) |
|
|
64
|
+
| Нативный трей (иконка со сводным состоянием, баллоны при падении) | не реализовано — потребовало бы `pystray`/`Pillow` против инварианта stdlib-only. Вместо него PWA-установка и компактный режим `?view=compact` | `standkit_hub/web/manifest.webmanifest` |
|
|
65
|
+
| Автозапуск хаба при входе в систему | не реализовано; механика ярлыка есть | `standkit_hub/shortcut.py` |
|
|
66
|
+
| Остановка хаба, когда дашборд никто не смотрит (idle-shutdown / heartbeat от фронта) | не реализовано — запущенный ярлыком процесс живёт под `pythonw` без окна до перезагрузки или ручного снятия. Дубли при повторном клике по ярлыку закрыты single-instance проверкой, но сам «вечный» процесс остаётся | `standkit_hub/__main__.py` (`serve_forever` без таймаута), `standkit_hub/server.py::probe_hub_instance` |
|
|
67
|
+
| Остановка хаба из интерфейса (кнопка «Завершить работу диспетчера») | не реализовано — единственный штатный способ Ctrl+C недоступен при запуске ярлыком (консоли нет) | `standkit_hub/__main__.py` (обработка `KeyboardInterrupt`) |
|
|
68
|
+
|
|
69
|
+
## Секреты
|
|
70
|
+
|
|
71
|
+
| Пункт | Статус | Упомянут в коде |
|
|
72
|
+
|------|--------|-----------------|
|
|
73
|
+
| CLI-обёртка `set/get/status/rotate/delete` | только программное API; CLI нет. ⚠️ У модуля нет и `__main__`-блока, поэтому `python -m standkit.secrets set <ref>` **молча завершается с rc=0**, ничего не задав (до 18.08.2026 такая команда фигурировала в `docs/REMOTE_STANDS.md` — исправлено). Рабочие пути: `standkit.secrets.set_secret()` из Python, кнопка «Задать секрет…» в дашборде, переменная `STANDKIT_SECRET__<REF>` | `standkit/secrets.py` (комментарий «Бэклог следующих итераций») |
|
|
74
|
+
| Файловый фолбэк `secrets.enc` (машины без keyring) | не реализован | там же |
|
|
75
|
+
|
|
76
|
+
## Приёмка
|
|
77
|
+
|
|
78
|
+
| Пункт | Статус |
|
|
79
|
+
|------|--------|
|
|
80
|
+
| **Агент на Linux** | **первая живая установка 18.08.2026** — Ubuntu 24.04 (`BPM-docker-test`), стенд `survey9` в Docker: установка через pipx, регистрация стенда из Python (`Registry.add_existing`), секреты через `STANDKIT_SECRET__*`, служба systemd, проверка API токеном. Вскрыла: жёсткую схему `http://` в HTTP-пробе (строка выше), отсутствие CLI регистрации (строка выше) и набор типовых ошибок оператора (PEP 668, забытый префикс секрета, владелец `agent.env`, `stand_host=0.0.0.0`, чтение реестра только при старте) — всё разобрано в кукбуке |
|
|
81
|
+
| **Docker** бэкенд (одиночный контейнер + compose) | **живая приёмка проведена 17.08.2026** на Docker Engine 29.6.1: 50 проверок основного цикла (start/stop/restart по фактическому `State.StartedAt`, логи, health, идемпотентность, негативы, работа без `docker` в PATH) + 6 проверок граничных состояний (paused, crash-loop, exited, подстрочные имена сервисов, кириллица в логах). Вскрыла два ложных «стенд жив» — исправлены в этой же ветке (см. CHANGELOG) |
|
|
82
|
+
| **k8s** бэкенд | **живая приёмка проведена 17.08.2026** на кластере k3s 1.30 (k3d): 28 проверок — scale/rollout restart с проверкой пересоздания подов, `readyReplicas`, `k8s_replicas`, логи, NodePort-проба, негативы по деплойменту/namespace/контексту/PATH. Вскрыла ложное «жив» при нуле реплик — исправлено |
|
|
83
|
+
| **IIS** бэкенд | **живая приёмка проведена 17.08.2026** на настоящем стенде BPMSoft 1.9.1.2 (.NET Framework + PostgreSQL) в IIS 10 / .NET Framework 4.8: сайт `iis19` (порт 5081) + вложенное приложение `/0` + сайт-сосед на ТОМ ЖЕ пуле; 66 живых проверок (окружение и автодетект 13, жизненный цикл site-scoped 17, `w3wp` 7, логи 4, отсутствие elevation 10 + 4 кросс-платформенных, ручки хаба 6, граничные состояния канала управления 5). Вскрыла 4 дефекта (пул при автодетекте, `state:Unknown`, ложный диагноз «нет прав», логи в подпапках-датах) — исправлены в этой же ветке, см. CHANGELOG |
|
|
84
|
+
| Контур для повторной приёмки k8s | на Windows с **cgroup v1** (проверить: `docker info --format {{.CgroupVersion}}`) Kubernetes ≥1.32 не поднимается — `kind` с `kindest/node:v1.36.1` падает на `kubeadm init`, k3s 1.35 не доходит до CoreDNS. Рабочий вариант — k3d с `--image rancher/k3s:v1.30.6-k3s1`; после создания кластера поправить kubeconfig: k3d прописывает `host.docker.internal`, нужен `kubectl config set-cluster <ctx> --server=https://127.0.0.1:<порт из docker port k3d-<cluster>-serverlb>` |
|
|
85
|
+
|
|
86
|
+
### Остатки после приёмки 17.08.2026 — закрыты в тот же день
|
|
87
|
+
|
|
88
|
+
| Пункт | Как закрыт |
|
|
89
|
+
|------|--------|
|
|
90
|
+
| Контейнер в crash-loop | `describe_state` объясняет состояние словами: «контейнер в цикле перезапуска (перезапусков: N)», а у контейнера, пойманного в running-фазе, — «уже перезапускался N раз, возможен цикл падений». Причина уходит в `StandStatus.details['process_reason']`, поэтому витрина показывает диагноз вместо мигающего индикатора. Сам вердикт по-прежнему отражает мгновенное состояние — это честно, контейнер и правда попеременно жив |
|
|
91
|
+
| Контракт `read_logs` при отсутствующем CLI | оба бэкенда возвращают `None`; контракт зафиксирован в докстринге протокола `HostingBackend` |
|
|
92
|
+
| `KubernetesBackend.stop` | бросает `HostingError` с текстом kubectl, как `DockerBackend.stop` |
|
|
93
|
+
| `read_logs` при нескольких репликах (k8s) | чтение по label-селектору (`logs -l ... --prefix`) со всех подов. Отдельно: общий вывод НЕ обрезается хвостом до `n` — `--tail` уже применён kubectl к каждому поду, а хвост принадлежит поду, писавшему последним |
|
|
94
|
+
| `restart` (k8s) не ждёт готовности | после `rollout restart` выполняется `rollout status` (`K8S_ROLLOUT_WAIT_SEC`, по умолчанию 60 с; `0` возвращает прежнее поведение) |
|
|
95
|
+
|
|
96
|
+
### Открытые остатки
|
|
97
|
+
|
|
98
|
+
| Пункт | Статус |
|
|
99
|
+
|------|--------|
|
|
100
|
+
| Признак «краш-луп» в самом вердикте | сейчас различить «стенд работает» и «стенд поднялся на секунду между падениями» можно только по тексту `reason`. Возможный шаг — отдельное состояние в витрине (жёлтый вместо зелёного) при `restart_count > 0` и свежем `StartedAt` |
|
|
101
|
+
| Живая приёмка на нескольких узлах | всё проверено на одноузловом кластере: поведение при подах, разъехавшихся по узлам, и при `NodePort` за внешним балансировщиком не проверялось |
|
|
102
|
+
| Усыновление стенда (kestrel) | **живая приёмка проведена** на реальном стенде, поднятом мимо диспетчера: поиск владельца порта, признание своего стенда и отказ для чужого, отсутствие действий без подтверждения, остановка по подтверждению, протухший pidfile. Именно она вскрыла, что на Windows рабочий каталог процесса приходится читать из PEB — путь к стенду не встречается ни в `ExecutablePath`, ни в командной строке |
|
|
103
|
+
| Усыновление стенда (IIS) | **проверено живьём 17.08.2026**: автоопределение сайта по physical path и по биндингу (в т.ч. `%SystemDrive%`-путь и игнор `net.tcp`-биндинга), снятие `w3wp` по пулу с проверкой, что соседний пул не тронут, различение «сайт остановлен / пул остановлен / порт держит http.sys / состояние не выяснено». Именно она вскрыла, что пул при автодетекте не заполнялся (атрибут `APPPOOL.NAME`) |
|
|
104
|
+
| Контур для повторной приёмки IIS | на этом хосте **служба WAS падает** (System log: «Служба активации Windows была неожиданно завершена», W3SVC уходит с кодом 1067) при сценарии «создать новый App Pool + сайт и сразу дать запрос» — воспроизведено 3/3, в т.ч. с отдельным физическим каталогом. Лечится `iisreset /restart` (либо `sc start WAS` + `sc start W3SVC`). Проверки, которым нужен ВТОРОЙ пул, поэтому переиспользуют существующие пул и сайт (`set app <site>/ /applicationPool:DefaultAppPool`), а не создают новые. Побочно этот же контур дал живые примеры транзиентных RPC-сбоев `appcmd` |
|
|
105
|
+
|
|
106
|
+
## Крупные направления (отдельные ADR)
|
|
107
|
+
|
|
108
|
+
| Пункт | Статус | ADR |
|
|
109
|
+
|------|--------|-----|
|
|
110
|
+
| Браузинг/скачивание логов удалённых стендов | предложено, не реализовано | [ADR-0003](adr/0003-remote-log-browsing.md) |
|
|
111
|
+
| Остаточная безопасность агента (PKI, ротация токенов, per-stand ACL, CN→scope, ротация аудита, bounded thread pool, chunked) | не реализовано | `SECURITY.md` §6 |
|
|
112
|
+
| Companion (платная редакция: автоапдейт/лицензия/подпись) | вне этого репозитория | экосистема BPMkit |
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Changelog — standkit / BPMkitStand
|
|
2
|
+
|
|
3
|
+
Формат — [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/); версии —
|
|
4
|
+
[SemVer](https://semver.org/lang/ru/). Даты — 2026 год.
|
|
5
|
+
|
|
6
|
+
## [0.7.0] — 2026-08-18
|
|
7
|
+
|
|
8
|
+
### Добавлено
|
|
9
|
+
- **Кукбук `standkit_hub/web/cookbook.html`.** Standalone-документ в фирменном стиле BPMkit
|
|
10
|
+
(один файл, без CDN и сборки): обзор продукта, установка и ключи запуска,
|
|
11
|
+
разбор дашборда, реестр стендов, виды хостинга (kestrel/iis/docker/k8s),
|
|
12
|
+
удалённые стенды и агент, модель безопасности, траблшутинг, границы
|
|
13
|
+
free/paid и незакрытые заделы. Есть поиск по документу, тёмная и светлая
|
|
14
|
+
тема, кнопки «копировать» на командах. Скриншоты дашборда — настоящие
|
|
15
|
+
(сняты с текущей сборки хаба, встроены в файл; меняются вместе с темой
|
|
16
|
+
документа), а не рисованные схемы. Собран по README, ARCHITECTURE,
|
|
17
|
+
HOSTING, REMOTE_STANDS, SECURITY, ROADMAP, BACKLOG и CHANGELOG.
|
|
18
|
+
Отдельно расписаны: установка **раздельно для Windows и Linux** (в Linux —
|
|
19
|
+
pipx или venv, потому что системный Python закрыт PEP 668), **обновление
|
|
20
|
+
диспетчера** (pip / pipx / venv, что при этом не трогается), три источника
|
|
21
|
+
секретов с приоритетом, и **развёртывание агента-демона на удалённом хосте**
|
|
22
|
+
— раскладка каталогов, где лежит его `projects.json`, токены через
|
|
23
|
+
`STANDKIT_SECRET__…`, systemd-юнит и служба Windows через NSSM.
|
|
24
|
+
- **Документация синхронизирована с поддержкой HTTPS-пробы** (кукбук 1.2). Кукбук:
|
|
25
|
+
раздел «известное ограничение HTTPS» заменён описанием полей `stand_scheme` и
|
|
26
|
+
`verify_tls` — как настроить стенд за TLS и почему self-signed требует
|
|
27
|
+
`verify_tls: false`; поля добавлены в таблицу записи реестра; в колонке HTTP
|
|
28
|
+
сказано, что ссылка «Открыть стенд» строится по той же схеме; строка
|
|
29
|
+
траблшутинга про `http: down` теперь даёт рецепт, а не констатацию.
|
|
30
|
+
`docs/REMOTE_STANDS.md` — то же в шаге про реестр и в траблшутинге, пункт из
|
|
31
|
+
«Ограничений» убран. `docs/BACKLOG.md` — строка про HTTP-пробу за HTTPS снята:
|
|
32
|
+
реализовано.
|
|
33
|
+
- **Кукбук 1.1 — по итогам первой живой установки агента на Linux** (Ubuntu 24.04,
|
|
34
|
+
стенд в Docker, 18.08.2026). Что добавлено: предупреждение про
|
|
35
|
+
`--break-system-packages` и указание, каким питоном звать скрипты
|
|
36
|
+
(`~/.local/share/pipx/venvs/standkit/bin/python` — системный `python3` модуля не
|
|
37
|
+
видит); отсутствие реестра на чистой машине как норма и полный порядок его
|
|
38
|
+
резолва; рецепт регистрации стенда через `Registry.add_existing()` — CLI для
|
|
39
|
+
этого нет; таблица «один стенд — две разные записи в двух реестрах»; отдельный
|
|
40
|
+
подраздел «Docker: какой адрес писать в реестр» (`0.0.0.0` даёт ложный
|
|
41
|
+
`http: down`, левый порт из `docker ps`, внутренний IP не писать); таблица
|
|
42
|
+
примеров имён `STANDKIT_SECRET__*` и три грабли вокруг них (забытый префикс,
|
|
43
|
+
владелец `agent.env`, лишний `_` от `echo | tr`); обязательность `--token-ref`
|
|
44
|
+
и явные `--run-dir`/`--log-dir` для службы; отсутствие эндпоинта `/health` и
|
|
45
|
+
разбор `409 adopt_required`; `curl.exe`/`Invoke-RestMethod` вместо алиаса
|
|
46
|
+
`curl` в PowerShell; рамка безопасности с `ufw`; известное ограничение
|
|
47
|
+
HTTPS-пробы. Траблшутинг вырос на десять строк — все симптомы реальные.
|
|
48
|
+
- **Кнопка «Справка» (?) в шапке дашборда и ссылка в окне «О программе»** —
|
|
49
|
+
открывают кукбук, который отдаётся хабом как `/static/cookbook.html` и входит
|
|
50
|
+
в пакет (package-data `web/*`). Кукбук — обычная статика, а не страница
|
|
51
|
+
приложения: тот же файл открывается с диска, когда диспетчер остановлен.
|
|
52
|
+
Тест `test_cookbook_is_served_as_static_and_self_contained` держит и раздачу,
|
|
53
|
+
и самодостаточность (ни одной внешней ссылки, ни одного обращения к
|
|
54
|
+
`/static/*` изнутри файла).
|
|
55
|
+
- **Отдельный диагноз «службы IIS остановлены».** Новый тип
|
|
56
|
+
`IisServiceUnavailableError` и подсказка «поднимите WAS/W3SVC»
|
|
57
|
+
(`sc start WAS` / `iisreset /start`). До живой приёмки IIS 17.08.2026 этот
|
|
58
|
+
случай (код 50, «Служба WAS недоступна») выдавался за нехватку прав, и
|
|
59
|
+
пользователь перезапускал диспетчер от администратора вместо запуска службы.
|
|
60
|
+
- **Однократный повтор ЧИТАЮЩЕЙ команды `appcmd` при транзиентном сбое RPC**
|
|
61
|
+
(код 1726 / `hresult:800706be`, код 2147549190 / `hresult:80010006`). Живьём
|
|
62
|
+
такие ответы приходили сразу после операций над пулами, а через секунду та же
|
|
63
|
+
команда работала. Изменяющие команды автоматически НЕ повторяются — к тексту
|
|
64
|
+
ошибки добавляется подсказка про канал управления.
|
|
65
|
+
- **Состояние стенда объясняется словами.** У docker- и k8s-бэкендов появился
|
|
66
|
+
`describe_state` (как у IIS): кроме вердикта «жив/не жив» он отдаёт причину,
|
|
67
|
+
которую `check_stand` кладёт в `StandStatus.details['process_reason']` —
|
|
68
|
+
«контейнер приостановлен (docker pause)», «контейнер в цикле перезапуска
|
|
69
|
+
(перезапусков: N)», «деплоймент масштабирован в 0 реплик», «готово 1 из 3
|
|
70
|
+
реплик». Вердикт, вынесенный TCP-фолбэком, помечается `status='unknown'` и
|
|
71
|
+
прямо сообщает, что состояние выяснить не удалось, — «зелёный по порту»
|
|
72
|
+
больше не выглядит как подтверждённый ответ CLI.
|
|
73
|
+
- **k8s: `restart` дожидается завершения выката** (`rollout status`,
|
|
74
|
+
`K8S_ROLLOUT_WAIT_SEC`, по умолчанию 60 секунд; `0` возвращает прежнее
|
|
75
|
+
поведение). Раньше вызов возвращался за доли секунды, старые поды были ещё
|
|
76
|
+
готовы, и стенд сразу после «перезапуска» выглядел здоровым, даже если новые
|
|
77
|
+
поды не поднимались.
|
|
78
|
+
|
|
79
|
+
### Изменено
|
|
80
|
+
- **`docs/REMOTE_STANDS.md` — по тем же итогам:** явно сказано, что CLI регистрации
|
|
81
|
+
стенда нет (и чем её заменить), добавлено правило адреса `stand_host`/`stand_port`
|
|
82
|
+
для контейнеров, отмечено, что агент читает реестр только при старте, в примере
|
|
83
|
+
реестра `0.0.0.0` заменён на адрес обращения, к шагу запуска добавлены
|
|
84
|
+
обязательность `--token-ref` и каталоги службы, к таблице эндпоинтов — «`/health`
|
|
85
|
+
нет», в траблшутинг — десять строк живых симптомов, в «Ограничения» — HTTPS-проба.
|
|
86
|
+
- **`docs/BACKLOG.md`:** добавлены строки про HTTP-пробу за HTTPS и отсутствие CLI
|
|
87
|
+
регистрации, а в раздел приёмки — первая живая установка агента на Linux.
|
|
88
|
+
- **Кукбук переехал из `docs/` в `standkit_hub/web/`** — один файл вместо двух
|
|
89
|
+
копий: он и документ репозитория, и встроенная справка приложения.
|
|
90
|
+
`docs/cookbook.html` удалён, ссылка в README ведёт на новое место.
|
|
91
|
+
- **Скриншоты `docs/img/*.png` пересняты** на 0.6.1: в прежних (0.5.x) не было
|
|
92
|
+
ни колонки Redis, ни кнопки «Зарегистрировать стенд».
|
|
93
|
+
- **Забрана работа из ветки `docs/cookbook-and-screenshots` (28.07)**, которая
|
|
94
|
+
так и не получила PR: оттуда взяты кнопка «Справка», ссылка в «О программе»,
|
|
95
|
+
правила компактного режима и тест раздачи кукбука. Ветка удалена, чтобы не
|
|
96
|
+
висеть «потерянной работой».
|
|
97
|
+
- **Установка в README — с PyPI (`pip install standkit`)**, а не из git. Пакет
|
|
98
|
+
публикуется на PyPI с 0.3.7, но README всё это время предлагал единственный
|
|
99
|
+
способ — установку из репозитория: она ставит незарелизенный `main` и не
|
|
100
|
+
обновляется командой `pip install -U`. Git-вариант остался в README как явный
|
|
101
|
+
«свежий `main` до релиза».
|
|
102
|
+
- **`docs/REMOTE_STANDS.md`, установка агента — в выделенный venv** с созданием
|
|
103
|
+
сервисного аккаунта и каталогов, вместо `pip install` из git в текущее
|
|
104
|
+
окружение: агент оформляется службой, а системный Python в свежих
|
|
105
|
+
дистрибутивах закрыт PEP 668.
|
|
106
|
+
- **Единый контракт `read_logs`:** при отсутствии CLI все бэкенды возвращают
|
|
107
|
+
`None`. Раньше docker отдавал `None`, а k8s бросал `HostingError`.
|
|
108
|
+
- **k8s: логи читаются со всех подов деплоймента** по label-селектору с
|
|
109
|
+
префиксом имени пода. `kubectl logs deployment/X` показывает один под,
|
|
110
|
+
выбранный самим kubectl, — при нескольких репликах часть событий стенда была
|
|
111
|
+
не видна.
|
|
112
|
+
- **k8s: `stop` бросает `HostingError`** с текстом kubectl вместо молчаливого
|
|
113
|
+
`False` по ненулевому коду возврата — как `DockerBackend.stop`.
|
|
114
|
+
|
|
115
|
+
### Исправлено
|
|
116
|
+
- **Стенд за HTTPS всегда показывался `http: down`.** Проба `check_stand`
|
|
117
|
+
строила URL жёстко как `http://<host>:<port>`, а `http_ok` шёл в `urlopen`
|
|
118
|
+
с дефолтным SSL-контекстом, проверяющим цепочку сертификатов. Живой стенд
|
|
119
|
+
BPMSoft за TLS с self-signed сертификатом (типовой дев-контур; найдено на
|
|
120
|
+
живой приёмке агента на Ubuntu 18.08.2026) давал `down` при исправных
|
|
121
|
+
`process`/`db` — индикатор врал, оператор видел «стенд лежит» на работающем
|
|
122
|
+
стенде. Появились два поля записи реестра: `stand_scheme` (`http`|`https`,
|
|
123
|
+
по умолчанию `http` — старые реестры не меняют поведения) и `verify_tls`
|
|
124
|
+
(по умолчанию `true`; `false` — не проверять цепочку, для self-signed).
|
|
125
|
+
`http_ok` принимает `verify=` и отключает проверку ТОЛЬКО для `https://`-
|
|
126
|
+
адресов, через публичный `ssl.create_default_context()`. Правка в ядре
|
|
127
|
+
чинит сразу всех потребителей пробы — агент, хаб и локальный путь. Заодно
|
|
128
|
+
ссылка «Открыть стенд» в дашборде строится по той же схеме, а не по `http`.
|
|
129
|
+
Поля НЕ добавлены в форму регистрации дашборда (её белый список полей
|
|
130
|
+
фиксирован в `standkit_hub/server.py` и `web/app.js`) — задавать их пока
|
|
131
|
+
нужно в `projects.json`; см. `docs/BACKLOG.md`.
|
|
132
|
+
- **Документация предлагала несуществующую команду задания секрета.**
|
|
133
|
+
`docs/REMOTE_STANDS.md` в трёх местах советовал `python -m standkit.secrets set <ref>`,
|
|
134
|
+
но у модуля нет `__main__`-блока: команда молча завершается с rc=0 и ничего не
|
|
135
|
+
сохраняет, а администратор узнаёт об этом позже — по `401` от агента. Заменено на
|
|
136
|
+
рабочие пути (переменная `STANDKIT_SECRET__<REF>` для службы, `set_secret()` из
|
|
137
|
+
Python для машины с keyring, кнопка «Задать секрет…» в дашборде); в `docs/BACKLOG.md`
|
|
138
|
+
строка про отсутствующий CLI дополнена этим поведением.
|
|
139
|
+
- **Внешние утилиты больше не мигают консольным окном** (`appcmd`, `sc`,
|
|
140
|
+
`docker`, `kubectl`, `taskkill`, `tasklist`, `powershell`). У родителя без
|
|
141
|
+
собственной консоли — `pythonw.exe`, служба, фоновый поллер хаба — Windows
|
|
142
|
+
выдавала консоль каждому такому вызову: на экране всплывало и мгновенно
|
|
143
|
+
исчезало чёрное окно. Живьём это выглядело как два мигающих окна раз в
|
|
144
|
+
~12 секунд (поллер опрашивает IIS-стенд парой `appcmd list site|apppool`).
|
|
145
|
+
Появилась ЕДИНАЯ точка запуска `standkit.platform.run_console` с
|
|
146
|
+
`CREATE_NO_WINDOW` на win32; `hosting._run` (то есть весь путь
|
|
147
|
+
appcmd/docker/kubectl, включая пробы `standkit.adopt`), `sc query`,
|
|
148
|
+
`taskkill`, фолбэк `tasklist` и создание ярлыка через PowerShell переведены
|
|
149
|
+
на неё. Прямой `subprocess.run` в пакете теперь запрещён и стережётся
|
|
150
|
+
тестом `tests/test_no_window.py` (13 тестов, из них два — статическая
|
|
151
|
+
проверка исходников от регресса). Из обычного терминала дефект не
|
|
152
|
+
воспроизводился — дочерний процесс наследовал консоль родителя.
|
|
153
|
+
- **IIS: автоопределение сайта заполняет и пул приложений.** `appcmd list apps
|
|
154
|
+
/xml` называет атрибут пула `APPPOOL.NAME`, а код читал `applicationPool` —
|
|
155
|
+
`detect_iis_site` ВСЕГДА возвращал пустой `app_pool`. Следствия: кнопка
|
|
156
|
+
«Определить автоматически» заполняла только сайт, а `kill_worker_processes`
|
|
157
|
+
(требует явного `iis_app_pool`) оставался недоступен для усыновлённого стенда.
|
|
158
|
+
Прежнее имя атрибута оставлено фолбэком. Юнит-тесты этот атрибут не мокали
|
|
159
|
+
вовсе — найдено живой приёмкой IIS 17.08.2026.
|
|
160
|
+
- **IIS: `state:Unknown` больше не считается «сайт остановлен».** При
|
|
161
|
+
остановленных службах IIS `appcmd` отдаёт `Unknown` — то есть «сам не знаю».
|
|
162
|
+
Раньше вердикт был `DOWN` с причиной «сайт остановлен (state=Unknown)», то
|
|
163
|
+
есть сломанный канал управления выглядел как штатно погашенный стенд. Теперь
|
|
164
|
+
это неопределённое состояние: вердикт по TCP-порту, а причина прямо называет
|
|
165
|
+
остановленные службы («состояние не выяснено: остановлены службы IIS (WAS,
|
|
166
|
+
W3SVC)»).
|
|
167
|
+
- **IIS: ошибка «объект не найден» не выдаётся за нехватку прав.** Список
|
|
168
|
+
признаков elevation был слишком широким (`error ( message:` ловил ЛЮБУЮ
|
|
169
|
+
ошибку appcmd, а код 1168 — это `ERROR_NOT_FOUND`, не отказ в доступе):
|
|
170
|
+
elevated-диспетчер на несуществующем сайте получал совет «запустите от имени
|
|
171
|
+
администратора». Теперь признаки узкие, а «любая ошибка = права» применяется
|
|
172
|
+
только когда процесс действительно НЕ elevated (`IsUserAnAdmin`).
|
|
173
|
+
- **IIS: остановка пула больше не падает по таймауту.** `appcmd stop apppool`
|
|
174
|
+
ждёт завершения рабочего процесса, а IIS отводит на это `shutdownTimeLimit`
|
|
175
|
+
(по умолчанию 90 секунд) — общий таймаут внешних команд 20 секунд был мал:
|
|
176
|
+
живьём остановка пула прогретого стенда BPMSoft (.NET Framework) заняла
|
|
177
|
+
20.6 с, диспетчер рапортовал ошибку на штатной операции. У изменяющих
|
|
178
|
+
операций IIS (start/stop/recycle сайта и пула) теперь свой таймаут
|
|
179
|
+
`_IIS_LIFECYCLE_TIMEOUT` = 120 секунд; читающие пробы состояния остались
|
|
180
|
+
быстрыми (дашборд опрашивает их часто).
|
|
181
|
+
- **IIS: логи стенда читаются из подпапок-дат.** BPMSoft под .NET Framework
|
|
182
|
+
пишет `Logs\<ГГГГ_ММ_ДД>\Application.log`, в корне каталога `*.log` нет вовсе —
|
|
183
|
+
`read_logs` возвращал `None`, и консоль стенда в диспетчере оставалась пустой
|
|
184
|
+
при живых файлах лога. Плоские логи (kestrel) по-прежнему в приоритете.
|
|
185
|
+
- **Остановленный стенд больше не показывается работающим.** Общее правило для
|
|
186
|
+
всех бэкендов хостинга: ответ CLI авторитетнее открытого TCP-порта, фолбэк на
|
|
187
|
+
порт применяется только там, где состояние выяснить не удалось (утилита не
|
|
188
|
+
найдена, команда упала, вывод не разобран). Раньше это правило действовало
|
|
189
|
+
только для IIS; живая приёмка на настоящих Docker и Kubernetes 17.08.2026
|
|
190
|
+
показала два его нарушения:
|
|
191
|
+
- **k8s:** после `stop` (scale 0) `kubectl` честно отвечал «готовых реплик
|
|
192
|
+
нет» — пустой `status.readyReplicas`, — но вердикт перебивался открытым
|
|
193
|
+
NodePort, который слушается всё время, пока существует Service. Стенд
|
|
194
|
+
отображался живым, а сводный статус выходил противоречивым:
|
|
195
|
+
`process=ok, http=down`. Теперь пустой ответ при rc=0 трактуется как
|
|
196
|
+
определённое «не запущен»; нечисловой ответ по-прежнему считается
|
|
197
|
+
неопределённостью и уходит в фолбэк.
|
|
198
|
+
- **docker:** состояние контейнера при `rc=0` теперь окончательное, порт его
|
|
199
|
+
не переопределяет. Заодно вместо `.State.Running` спрашивается
|
|
200
|
+
`.State.Status`: у приостановленного контейнера (`docker pause`)
|
|
201
|
+
`Running=true`, хотя он не обслуживает запросы, — такой стенд показывался
|
|
202
|
+
зелёным.
|
|
203
|
+
- **compose-режим больше не путает сервисы с похожими именами.** `is_running`
|
|
204
|
+
сопоставлял имя сервиса как подстроку любой строки `docker compose ps`:
|
|
205
|
+
при поднятом `webhook` неподнятый `web` считался запущенным. Теперь
|
|
206
|
+
используется машиночитаемый `docker compose ps --format json` с точным
|
|
207
|
+
сравнением поля `Service`, а на версиях compose без этого формата —
|
|
208
|
+
табличный вывод с разбором по токенам, а не по подстроке.
|
|
209
|
+
|
|
210
|
+
### Прочее
|
|
211
|
+
- Докстринг `HostKind.K8S` больше не утверждает, что логики k8s ещё нет — она
|
|
212
|
+
есть с 0.5.0 (ADR-0002) и прошла живую приёмку.
|
|
213
|
+
- `docs/BACKLOG.md`: раздел приёмки актуализирован, добавлены остатки, найденные
|
|
214
|
+
живым прогоном, и рецепт воспроизводимого k8s-контура на Windows с cgroup v1.
|
|
215
|
+
|
|
216
|
+
## [0.6.1] — 2026-07-27
|
|
217
|
+
|
|
218
|
+
### Исправлено
|
|
219
|
+
- **Повторный запуск по ярлыку больше не поднимает второй диспетчер.** Ярлык
|
|
220
|
+
стартует хаб через `pythonw.exe`, окна нет, а закрытие браузера процесс не
|
|
221
|
+
останавливает — idle-shutdown у хаба нет. Поэтому второй клик по ярлыку
|
|
222
|
+
запускал ещё один экземпляр: два фоновых поллера дёргали health-пробы и
|
|
223
|
+
внешние команды бэкендов хостинга над одним `projects.json`, а окно
|
|
224
|
+
открывалось на другом origin со своей копией localStorage (сбрасывалась
|
|
225
|
+
тема). На Windows было хуже, чем предполагалось: из-за `SO_REUSEADDR` bind
|
|
226
|
+
на уже слушаемый 8770 проходил успешно, и два хаба молча делили один порт.
|
|
227
|
+
Теперь перед стартом проверяется, не работает ли диспетчер уже — если да,
|
|
228
|
+
просто открывается браузер на нём. Откат на свободный порт сохранён для
|
|
229
|
+
случая, когда 8770 занял чужой сервис.
|
|
230
|
+
- **Дашборд честно сообщает, что диспетчер остановлен.** Раньше вкладка с
|
|
231
|
+
умершим хабом продолжала показывать последний снапшот как живой, а
|
|
232
|
+
единственными признаками были красная точка в углу и браузерное
|
|
233
|
+
`Failed to fetch` в тостах. Теперь показывается баннер с объяснением и тем,
|
|
234
|
+
что делать, устаревшая таблица гасится, а сетевые ошибки и 401 («сессия не
|
|
235
|
+
подтверждена» после перезапуска диспетчера) переведены на понятный язык.
|
|
236
|
+
|
|
237
|
+
## [0.6.0] — 2026-07-27
|
|
238
|
+
|
|
239
|
+
### Добавлено
|
|
240
|
+
- **Усыновление стендов, поднятых вне диспетчера.** Стенд, запущенный руками
|
|
241
|
+
(`dotnet BPMSoft.WebHost.dll` из консоли, скриптом, чужой сессией), теперь
|
|
242
|
+
можно остановить и перезапустить: диспетчер находит владельца порта,
|
|
243
|
+
проверяет, что это действительно процесс ЭТОГО стенда (порт + имя образа из
|
|
244
|
+
allowlist + рабочий каталог внутри `stand_dir`), и берёт его под управление —
|
|
245
|
+
**только с явного подтверждения**. Без подтверждения процесс не трогается.
|
|
246
|
+
В таблице стендов появился бейдж «вне диспетчера».
|
|
247
|
+
- **IIS: автоопределение сайта** по physical path и биндингу для стенда,
|
|
248
|
+
развёрнутого мимо диспетчера; снятие зависшего `w3wp` по pid; различение
|
|
249
|
+
состояний «сайт остановлен» / «пул остановлен» / «порт держит http.sys».
|
|
250
|
+
- **PWA.** `manifest.webmanifest` + иконки: Chrome и Edge предлагают
|
|
251
|
+
«Установить приложение» — дашборд получает отдельное окно без адресной
|
|
252
|
+
строки, свою иконку в панели задач и место в Alt+Tab. Зависимостей не
|
|
253
|
+
добавилось.
|
|
254
|
+
- **Компактный режим** `?view=compact` — узкое окно-виджет: имена стендов,
|
|
255
|
+
состояния и старт/стоп. Доступен ярлыком из установленного PWA.
|
|
256
|
+
- `GET /api/stands?probe=0` — слепок реестра без сетевых проб, и
|
|
257
|
+
`GET /api/events` — SSE-поток обновлений состояния.
|
|
258
|
+
|
|
259
|
+
### Изменено
|
|
260
|
+
- **Дашборд открывается мгновенно.** Раньше первый экран ждал полного обхода
|
|
261
|
+
всех стендов: пробы шли последовательно, до 9 секунд на стенд, а конфиг и
|
|
262
|
+
реестр перечитывались с диска на каждый запрос. Три недоступных стенда за
|
|
263
|
+
firewall давали около 27 секунд серого экрана. Теперь состояние собирает
|
|
264
|
+
фоновый поток, а страница рисуется сразу и обновляется по мере готовности.
|
|
265
|
+
- **Фиксированный порт по умолчанию — 8770** (с откатом на свободный, если
|
|
266
|
+
занят). Побочный эффект прежнего эфемерного порта: браузер каждый запуск
|
|
267
|
+
считал дашборд новым сайтом, из-за чего сбрасывалась выбранная тема и
|
|
268
|
+
заново качалась статика.
|
|
269
|
+
- Тема хранится в конфиге хаба и применяется до загрузки скриптов — тёмная
|
|
270
|
+
больше не мигает светлой при открытии.
|
|
271
|
+
- Настройка «интервал автообновления» наконец работает: раньше значение из
|
|
272
|
+
формы ни на что не влияло, интервал был жёстко зашит.
|
|
273
|
+
- **Форма настроек разгружена**: сразу видны четыре основных поля, параметры
|
|
274
|
+
федерации и демона-агента убраны под спойлеры, причём блок агента скрыт
|
|
275
|
+
целиком, пока в реестре нет удалённых стендов. У полей появились пояснения
|
|
276
|
+
и подсказки со значениями по умолчанию.
|
|
277
|
+
- Остановка стенда стала мягкой: сначала штатное завершение с ожиданием и
|
|
278
|
+
только потом принудительное — стенд успевает закрыть соединения с БД и
|
|
279
|
+
Redis. Раньше на Windows сразу шёл `taskkill /T /F`.
|
|
280
|
+
- Статика отдаётся с валидаторами кэша (`ETag`, `304`).
|
|
281
|
+
|
|
282
|
+
## [0.5.2] — 2026-07-25
|
|
283
|
+
|
|
284
|
+
### Изменено
|
|
285
|
+
- **IIS: жизненный цикл по Site.** Стоп/рестарт/детект работают по **Site**
|
|
286
|
+
стенда; **App Pool не трогаем** (может быть общим с другими приложениями).
|
|
287
|
+
App Pool задействуется только когда `iis_site` не задан. Детект «жив» — по
|
|
288
|
+
состоянию сайта; открытый TCP-порт больше не маскирует остановленный сайт
|
|
289
|
+
(http.sys держит порт). См. [ADR-0004](adr/0004-iis-site-scoped-lifecycle.md).
|
|
290
|
+
- **Читаемые ошибки внешних команд.** Вывод `appcmd`/`docker`/`kubectl`
|
|
291
|
+
декодируется utf-8 → OEM (cp866) → cp1251 — больше не «кракозябры».
|
|
292
|
+
- **Хаб — тяжёлые логи IIS.** Панель «Текущее состояние» берёт логи только за
|
|
293
|
+
сегодня и читает лишь хвост файла (до 4 МБ) — дневной IIS-лог в сотни МБ не
|
|
294
|
+
грузится целиком.
|
|
295
|
+
- Упаковка: SPDX-выражение лицензии (`license = "MIT"`, `license-files`) —
|
|
296
|
+
убран deprecation-warning setuptools.
|
|
297
|
+
|
|
298
|
+
### Добавлено
|
|
299
|
+
- Подсказка «запустите диспетчер от имени администратора» при ошибках `appcmd`
|
|
300
|
+
из-за нехватки прав (IIS требует elevation).
|
|
301
|
+
- Кнопка «Обновить» даёт обратную связь (блокировка + «Обновление…» + время).
|
|
302
|
+
- Колонка **Redis** в таблице стендов с номером базы.
|
|
303
|
+
- Обратная связь и надёжность первого запуска: папка реестра создаётся при старте
|
|
304
|
+
диспетчера и при первой записи.
|
|
305
|
+
|
|
306
|
+
### Исправлено
|
|
307
|
+
- **Честный отказ** при остановке стенда, запущенного вне диспетчера (нет
|
|
308
|
+
pidfile): вместо мнимого «остановлено» — понятная ошибка.
|
|
309
|
+
- Логи стенда ищутся в подпапках по датам (рекурсивный листинг) — раньше хаб
|
|
310
|
+
ошибочно сообщал «в каталоге нет файлов».
|
|
311
|
+
- Модалки не закрываются при выделении текста мышью с отпусканием за краем окна.
|
|
312
|
+
- Колонки таблицы не «прыгают» при смене статуса; HTTP-значение — кликабельная
|
|
313
|
+
ссылка; кнопки в модалках не слипаются.
|
|
314
|
+
- `Ctrl+C` завершает `standkit-hub` чисто, без трейсбека `KeyboardInterrupt`.
|
|
315
|
+
- Дедуп SVG-ассетов; TODO-докстринги урезаны до ссылок на документацию/бэклог.
|
|
316
|
+
|
|
317
|
+
## [0.5.1] — 2026-07-24
|
|
318
|
+
|
|
319
|
+
Технический релиз: агрегировал правки хаба/IIS/логов и упаковки (позже целиком
|
|
320
|
+
переиздано в 0.5.2 из ветки `main`).
|
|
321
|
+
|
|
322
|
+
## [0.5.0] — 2026-07-24
|
|
323
|
+
|
|
324
|
+
### Добавлено
|
|
325
|
+
- **Kubernetes** hosting backend (`host_kind=k8s`) через `kubectl`
|
|
326
|
+
(scale/rollout/get/logs) — [ADR-0002](adr/0002-k8s-backend.md).
|
|
327
|
+
- **Регистрация стенда из UI** — модалка «Зарегистрировать стенд» в дашборде
|
|
328
|
+
(пишет в общий `projects.json`).
|
|
329
|
+
- Hosting backends kestrel/iis/docker — [ADR-0001](adr/0001-hosting-backends.md)
|
|
330
|
+
(v0.4.0), k8s — v0.5.0.
|
|
331
|
+
|
|
332
|
+
## [0.3.7] — 2026-07
|
|
333
|
+
|
|
334
|
+
### Добавлено
|
|
335
|
+
- Первый публичный релиз ядра `standkit` + агента `standkit_agent` + веб-дашборда
|
|
336
|
+
`standkit_hub` на PyPI; фронт-README, `docs/REMOTE_STANDS.md`, скриншоты.
|
|
337
|
+
|
|
338
|
+
[0.7.0]: https://pypi.org/project/standkit/0.7.0/
|
|
339
|
+
[0.6.1]: https://pypi.org/project/standkit/0.6.1/
|
|
340
|
+
[0.6.0]: https://pypi.org/project/standkit/0.6.0/
|
|
341
|
+
[0.5.2]: https://pypi.org/project/standkit/0.5.2/
|
|
342
|
+
[0.5.1]: https://pypi.org/project/standkit/0.5.1/
|
|
343
|
+
[0.5.0]: https://pypi.org/project/standkit/0.5.0/
|
|
344
|
+
[0.3.7]: https://pypi.org/project/standkit/0.3.7/
|