Панель как надстройка: образ, subtree и разделение двух укладов

Dockerfile и .dockerignore: панель — один процесс и один образ, express
отдаёт и API, и собранный фронт. Контекст сборки — каталог панели, ядро
приезжает в образ закоммиченным, поэтому образ собирается из чистого
checkout'а без ma7engine рядом.

Скаффолдер больше не кладёт панели update.sh и хуки predev/prebuild: ядро
в продукт приходит через git subtree, а копирование затёрло бы поддерево.
Печатает готовые команды subtree add.

Доктор различает два уклада ядра — subtree в продукте и копию в шаблоне —
и проверяет каждый по своим правилам.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LeBkZLiQrvdECf5nfgiMz
This commit is contained in:
2026-08-26 20:46:26 +03:00
co-authored by Claude Opus 5
parent 7977ea6738
commit bb56e9dc3b
+46 -6
View File
@@ -27,9 +27,11 @@ core/ ядро; половины разъезжаются
frontend/ шаблон панели: конфиг + четыре вкладки frontend/ шаблон панели: конфиг + четыре вкладки
backend/server.ts шаблон бэкенда: конфиг + три демо-маршрута backend/server.ts шаблон бэкенда: конфиг + три демо-маршрута
scripts/update.sh копирует обе половины ядра в панель (npm run update) Dockerfile один процесс и один образ: express отдаёт API и фронт
scripts/new-panel.mjs заводит новую панель из шаблона (npm run new-panel) scripts/new-panel.mjs заводит новую панель из шаблона (npm run new-panel)
scripts/release.sh split-ветки половин ядра для subtree (npm run release)
scripts/doctor.mjs проверяет, что панель по стандарту (npm run doctor) scripts/doctor.mjs проверяет, что панель по стандарту (npm run doctor)
scripts/update.sh раскладывает ядро внутри шаблона (npm run update)
``` ```
Половины лежат врозь не по вкусу, а потому что это две разные сборки: у них Половины лежат врозь не по вкусу, а потому что это две разные сборки: у них
@@ -199,10 +201,10 @@ npm run new-panel -- admin-hub # → ../admin-hub, соседом m
npm run new-panel -- admin-hub ~/work/hub # явный путь npm run new-panel -- admin-hub ~/work/hub # явный путь
``` ```
Скаффолдер копирует **`frontend/` и `backend/`**, но не `core/`: ядро в панель Панель — надстройка: она лежит подкаталогом продуктового репозитория
приезжает копией через `npm run update`, а не форком. Панель кладётся соседом (`ghosthub/admin`, `awg-control/admin`), а не самостоятельным проектом.
ma7engine — именно там её найдёт перебор кандидатов в `update.sh`; переехав, она Скаффолдер копирует **`frontend/` и `backend/`**, но не `core/`: ядро приходит
найдёт ядро по `$MA7ENGINE`. отдельно, через `git subtree` — см. следующий раздел.
Что он делает за тебя — ровно то, что раньше правилось руками и потому забывалось: Что он делает за тебя — ровно то, что раньше правилось руками и потому забывалось:
@@ -213,12 +215,50 @@ ma7engine — именно там её найдёт перебор кандид
3. **Демо-часть**: вкладки `hello` и `secrets` и маршруты `/api/demo/*` уезжают, 3. **Демо-часть**: вкладки `hello` и `secrets` и маршруты `/api/demo/*` уезжают,
`mountRoutes` остаётся пустым с образцом в комментарии. `docs` и `kit` `mountRoutes` остаётся пустым с образцом в комментарии. `docs` и `kit`
остаются — обе самодостаточны, и панель запускается непустой. остаются — обе самодостаточны, и панель запускается непустой.
4. **`.gitignore`** и `scripts/dev.sh` (обе половины одной командой). 4. **`.gitignore`**, `Dockerfile`, `.dockerignore` и `scripts/dev.sh`.
Остаётся только **учётка и секрет** — `ADMIN_USER`, `ADMIN_PASS` (или Остаётся только **учётка и секрет** — `ADMIN_USER`, `ADMIN_PASS` (или
`ADMIN_PASS_HASH`), `ADMIN_JWT_SECRET` в окружении бэкенда; без них ядро не даст `ADMIN_PASS_HASH`), `ADMIN_JWT_SECRET` в окружении бэкенда; без них ядро не даст
процессу подняться. процессу подняться.
### Ядро в продукт — `git subtree`
У ядра две половины и два адресата, а `git subtree` работает с одним `prefix` за
раз, поэтому каждая половина выделяется в свою производную ветку:
```sh
npm run release # core/browser → dist/browser, core/server → dist/server
```
В репозитории продукта, где панель лежит подкаталогом `admin/`:
```sh
git subtree add --prefix=admin/frontend/src/core <ядро> dist/browser --squash
git subtree add --prefix=admin/backend/core <ядро> dist/server --squash
git subtree pull --prefix=admin/frontend/src/core <ядро> dist/browser --squash
git subtree pull --prefix=admin/backend/core <ядро> dist/server --squash
```
`<ядро>` — путь к ma7engine либо URL его репозитория.
**Почему поддерево, а не копирование.** `update.sh` сносил каталог целиком
(`rm -rf` + `cp -R`): локальная правка ядра в продукте исчезала молча, истории
не оставалось, и «панель разошлась с ядром» выяснялось постфактум. `git subtree`
делает настоящий merge — встречные правки одной строки дают конфликт **в момент
обновления**, а правку, сделанную в продукте, можно вернуть в ядро
(`git subtree push`). Ядро при этом лежит в репозитории продукта закоммиченным,
поэтому образ собирается из чистого checkout'а, без ma7engine рядом.
Ветки `dist/*` полностью производные: руками не правятся и не мержатся обратно —
правится `core/`, после чего `npm run release` гоняется снова.
**В самом ma7engine** ядро по-прежнему раскладывается копией (`npm run update`
из соседнего `core/`) — там источник лежит в том же репозитории, и поддерево
было бы поддеревом самого себя. Доктор различает эти два уклада и проверяет
каждый по своим правилам; в панели он ругается на оставшийся `update.sh`, потому
что тот затёр бы поддерево.
### Проверка соответствия — `npm run doctor` ### Проверка соответствия — `npm run doctor`
Скаффолдер заводит панель по стандарту, доктор отвечает, соответствует ли она Скаффолдер заводит панель по стандарту, доктор отвечает, соответствует ли она