DEV setup on macos

This commit is contained in:
Alex Ant
2026-05-24 12:09:31 +05:00
parent a0e169e9b4
commit 970e107261
3 changed files with 256 additions and 0 deletions
+6
View File
@@ -1 +1,7 @@
store-dir=./.pnpm-store
supported-architectures.os[]=darwin
supported-architectures.os[]=linux
supported-architectures.cpu[]=x64
supported-architectures.cpu[]=arm64
supported-architectures.libc[]=glibc
+139
View File
@@ -0,0 +1,139 @@
# Запуск mono dev-стека на macOS
Полная пошаговая инструкция, как поднять backend (`coopback`) + parser (`cooparser`) + блокчейн-ноду + БД на macOS через Docker Desktop. Прошёл — отметь, ниже разобраны типичные грабли.
## TL;DR
```bash
cd ~/dacom-code/foundation/monocoop
docker compose up -d # 1. поднять базу
./scripts/dev-setup-macos.sh # 2. одной командой (см. ниже скрипт)
```
Если скрипта ещё нет — выполняй шаги ниже руками.
## Шаги
### 1. Поднять контейнеры
```bash
docker compose up -d
```
Поднимутся: `node` (NodeOS), `mongo`, `monoredis`, `postgres`, `coopback`, `cooparser`. **MinIO** входит в дефолт. **OpenSearch** — нет (тяжёлый, см. опц. сервисы).
### 2. Проверить чтоб .env'ы указывали на service-имена, а не localhost
`components/controller/.env`:
- `MONGODB_URL=mongodb://mongo:27017/cooperative-x`
- `REDIS_HOST=monoredis`
- `POSTGRES_HOST=postgres`, `POSTGRES_PASSWORD=postgres!23!23`
- `BLOCKCHAIN_RPC=http://node:8888`
- `SIMPLE_EXPLORER_API=http://cooparser:4000`
- `MINIO_ENDPOINT=minio:9000`
- `CHAIN_ID=<реальный chain_id, см. ниже>`
`components/parser/.env`:
- `MONGO_EXPLORER_URI=mongodb://mongo:27017/cooperative-x`
- `REDIS_HOST=monoredis`
- `API=http://node:8888`
- `SHIP=ws://node:8080`
**Почему service-имена:** все контейнеры в bridge-сети `monocoop_default`. Внутри контейнера `127.0.0.1` = сам контейнер, не host. На macOS `network_mode: host` в Docker Desktop работает плохо — используем bridge + service names.
### 3. Узнать живой chain_id
```bash
curl -s http://localhost:8888/v1/chain/get_info | jq -r .chain_id
```
И вписать в `components/controller/.env``CHAIN_ID=...`. **Если chain_id в .env не совпадает с живым — on-chain транзакции упадут на verify, хотя приложение запустится.**
### 4. Если правил .env — пересоздать контейнер (не restart!)
```bash
docker compose up -d --force-recreate --no-deps coopback cooparser
```
`docker restart` **не перечитывает** `env_file`. `--no-deps` — чтобы не пересоздавать БД (потеряются данные).
### 5. Native binding libxmljs2 — пересборка под Linux
На свежем чекауте `pnpm install` запускается на macOS, и `libxmljs2` собирает Mach-O бинарник. В Linux-контейнере он падает:
```
Error: .../xmljs.node: invalid ELF header ERR_DLOPEN_FAILED
```
**Фикс** (один раз после `pnpm install`):
```bash
docker exec monocoop-coopback-1 sh -c '
cd /app/node_modules/.pnpm/libxmljs2@0.37.0/node_modules/libxmljs2 \
&& rm -rf build \
&& PATH="$PWD/node_modules/.bin:$PATH" npm run install
'
docker restart monocoop-coopback-1
```
Проверка — magic bytes Linux ELF:
```bash
docker exec monocoop-coopback-1 sh -c \
'head -c 4 /app/node_modules/.pnpm/libxmljs2@0.37.0/node_modules/libxmljs2/build/Release/xmljs.node | od -c -N 4'
# Должно быть: 177 E L F
# Если cf fa ed fe — это macOS Mach-O, фикс не применился.
```
### 6. Дождаться cold-start coopback (~56 минут)
`ts-node` без cache + DI tree NestJS = долго. Первые логи появляются спустя ~5 минут после старта контейнера.
```bash
docker logs -f monocoop-coopback-1 | grep -E 'NestJS app|Nest application|Error'
```
Готово, когда видишь:
```
Nest application successfully started
NestJS app with Express routes running on port 2998
```
Проверка:
```bash
curl -s -X POST -H 'Content-Type: application/json' \
-d '{"query":"{__typename}"}' http://localhost:2998/v1/graphql
# {"data":{"__typename":"Query"}}
```
## Опциональные сервисы
### OpenSearch (поиск)
```bash
docker compose --profile search up -d opensearch
```
Затем в `components/controller/.env`:
```
OPENSEARCH_ENABLED=true
```
И пересоздать coopback:
```bash
docker compose up -d --force-recreate --no-deps coopback
```
## Грабли (если опять провозился пол-дня)
| Симптом | Причина | Фикс |
|---|---|---|
| `invalid ELF header` | host'овый `pnpm install` положил Mach-O | См. п. 5 — пересборка libxmljs2 в контейнере |
| `MongooseServerSelectionError: ECONNREFUSED 127.0.0.1:27017` | .env на localhost вместо service-имени | См. п. 2 |
| `password authentication failed for user "postgres"` | в .env `POSTGRES_PASSWORD=postgres` вместо `postgres!23!23` | См. п. 2 |
| Coopback ушёл в restart-loop, но рядом есть другой `monocoop-coopback-1` Up | Случайно запустился старый `components/controller/docker-compose.yml` (network_mode: host, без bind-mount, без CMD) | `docker stop coopback && docker rm coopback` |
| `docker restart` не подхватил новые переменные .env | `restart` не перечитывает `env_file` | `docker compose up -d --force-recreate --no-deps <svc>` |
| Coopback зависает на 5+ минут, лога нет | Это нормально для cold-start ts-node | Подождать. CPU должен крутиться ≥50%. Если CPU = 0% — другая проблема |
| `[VaultDomainService] Ошибка при получении WIF... bad decrypt` | SERVER_SECRET не совпадает с тем, чем шифровали WIF в БД | См. CLAUDE.md → раздел Vault & SERVER_SECRET. Дефолт — `SECRET` |
| Транзакции падают на on-chain verify | CHAIN_ID в .env не совпадает с живым | См. п. 3 |
| `Custom endpoint \`minio://9000\` was not a valid URI` на bootstrap | `MINIO_ENDPOINT` без схемы | `MINIO_ENDPOINT=http://minio:9000` (со схемой!) |
| nodeos в restart-loop с `Database dirty flag set` после sleep/crash Docker Desktop | unclean shutdown chain state | `docker stop monocoop-node-1`; `mv blockchain-data/state-history{,.broken-$(date +%s)}`; `docker compose up -d node` — nodeos сам сделает replay |
| Cold-start coopback 10+ минут (вместо обычных 3-5) | ts-node без cache + bind-mount через osxfs + параллельная нагрузка (nodeos replay) | Подождать; в будущем — `tsx` / `ts-node --swc` для dev-режима |
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# Поднять mono dev-стек на macOS с нуля. Идемпотентно — можно гонять повторно.
# См. DEV-SETUP-MACOS.md для контекста.
set -euo pipefail
cd "$(dirname "$0")/.."
ROOT="$PWD"
log() { printf '\033[1;36m[dev-setup] %s\033[0m\n' "$*"; }
warn() { printf '\033[1;33m[dev-setup] %s\033[0m\n' "$*"; }
die() { printf '\033[1;31m[dev-setup] %s\033[0m\n' "$*" >&2; exit 1; }
# 1. Docker daemon
if ! docker info >/dev/null 2>&1; then
log "Docker daemon не отвечает — пробую запустить Docker Desktop..."
open -a Docker
log "Жду готовности Docker daemon (до 3 мин)..."
for _ in $(seq 1 180); do
docker info >/dev/null 2>&1 && break
sleep 1
done
docker info >/dev/null 2>&1 || die "Docker Desktop не запустился. Открой его руками и повтори."
fi
# 2. Снести зомби-контейнер coopback (если есть)
if docker ps -a --format '{{.Names}}' | grep -qx 'coopback'; then
warn "Найден старый контейнер 'coopback' (не monocoop-*). Сношу — он дубль от components/controller/docker-compose.yml."
docker stop coopback >/dev/null 2>&1 || true
docker rm coopback >/dev/null 2>&1 || true
fi
# 3. Поднять стек
log "docker compose up -d"
docker compose up -d
# 4. Дождаться NodeOS RPC
log "Жду NodeOS RPC (http://localhost:8888)..."
for _ in $(seq 1 60); do
curl -fsS --max-time 2 http://localhost:8888/v1/chain/get_info >/dev/null 2>&1 && break
sleep 2
done
CHAIN_INFO=$(curl -fsS --max-time 5 http://localhost:8888/v1/chain/get_info) \
|| die "NodeOS RPC не отвечает. Глянь логи: docker logs monocoop-node-1"
LIVE_CHAIN_ID=$(printf '%s' "$CHAIN_INFO" | python3 -c "import sys, json; print(json.load(sys.stdin)['chain_id'])")
log "Живой chain_id: $LIVE_CHAIN_ID"
# 5. Сверить и при необходимости подправить CHAIN_ID в .env
ENV_FILE="components/controller/.env"
if [[ -f "$ENV_FILE" ]]; then
ENV_CHAIN_ID=$(grep -E '^CHAIN_ID=' "$ENV_FILE" | head -1 | cut -d= -f2- || true)
if [[ "$ENV_CHAIN_ID" != "$LIVE_CHAIN_ID" ]]; then
warn "CHAIN_ID в $ENV_FILE ($ENV_CHAIN_ID) != живому ($LIVE_CHAIN_ID). Обновляю."
# macOS BSD sed
sed -i '' "s|^CHAIN_ID=.*|CHAIN_ID=$LIVE_CHAIN_ID|" "$ENV_FILE"
NEED_RECREATE=1
fi
fi
# 6. Native binding libxmljs2 — проверить magic bytes, пересобрать если Mach-O
log "Проверяю libxmljs2 native binding..."
LIBXML_NODE=/app/node_modules/.pnpm/libxmljs2@0.37.0/node_modules/libxmljs2/build/Release/xmljs.node
MAGIC=$(docker exec monocoop-coopback-1 sh -c "head -c 4 $LIBXML_NODE 2>/dev/null | od -An -c | tr -d ' '" 2>/dev/null || echo "")
case "$MAGIC" in
'177ELF'|*'ELF'*)
log "libxmljs2 — Linux ELF, OK."
;;
'')
warn "libxmljs2 файла нет — собираю..."
REBUILD_XMLJS=1
;;
*)
warn "libxmljs2 не Linux ELF ($MAGIC) — пересобираю под Linux..."
REBUILD_XMLJS=1
;;
esac
if [[ "${REBUILD_XMLJS:-0}" == "1" ]]; then
docker exec monocoop-coopback-1 sh -c '
cd /app/node_modules/.pnpm/libxmljs2@0.37.0/node_modules/libxmljs2 \
&& rm -rf build \
&& PATH="$PWD/node_modules/.bin:$PATH" npm run install
'
NEED_RECREATE=1
fi
# 7. Если что-то правили — пересоздать контейнеры
if [[ "${NEED_RECREATE:-0}" == "1" ]]; then
log "Пересоздаю coopback/cooparser, чтобы подхватить новый env / новые бинарники..."
docker compose up -d --force-recreate --no-deps coopback cooparser
fi
# 8. Дождаться coopback HTTP
log "Жду пока coopback ответит по http://localhost:2998/v1/graphql (cold-start ts-node ~56 мин)..."
DEADLINE=$(($(date +%s) + 600))
while [[ $(date +%s) -lt $DEADLINE ]]; do
if curl -fsS --max-time 3 -X POST -H 'Content-Type: application/json' \
-d '{"query":"{__typename}"}' http://localhost:2998/v1/graphql >/dev/null 2>&1; then
log "Coopback готов."
break
fi
if docker logs --tail 5 monocoop-coopback-1 2>&1 | grep -qiE 'crashed|FATAL|invalid ELF|ERR_INVALID_PACKAGE'; then
docker logs --tail 60 monocoop-coopback-1
die "Coopback падает — см. логи выше."
fi
sleep 5
done
log "Готово. Backend: http://localhost:2998/v1/graphql"
log "Опц. сервисы: docker compose --profile search up -d opensearch"