DEV setup on macos
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 (~5–6 минут)
|
||||
|
||||
`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-режима |
|
||||
Executable
+111
@@ -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 ~5–6 мин)..."
|
||||
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"
|
||||
Reference in New Issue
Block a user