diff --git a/.github/workflows/build-bootstrap.yaml b/.github/workflows/build-bootstrap.yaml deleted file mode 100644 index f23c2ba7f92..00000000000 --- a/.github/workflows/build-bootstrap.yaml +++ /dev/null @@ -1,141 +0,0 @@ -name: Build bootstrap container - -# Сборка и публикация образа `dicoop/bootstrap` (DockerHub) — -# one-shot оркестратора первичного деплоя контрактов на KE-узел -# (см. `components/boot/docker/Dockerfile`). Образ потребляется -# в docker-compose рядом с `dicoop/blockchain_v5.1.1`-нодой как -# `depends_on: ke-node: service_healthy`. -# -# Триггер — push в одну из веток-окружений (dev / testnet / main). -# Маппинг по convention `feedback_branch_convention`: -# -# dev → tag=dev, CONTRACTS_TAG=dev -# testnet → tag=testnet, CONTRACTS_TAG=testnet -# main → tag=main + latest, CONTRACTS_TAG=main -# -# Зависимость: образ `dicoop/contracts:` должен существовать -# (он публикуется через `build-contracts.yaml` отдельным workflow'ом). -# Если contracts ещё не собраны для целевой ветки — `docker build` -# упадёт на multi-stage `FROM dicoop/contracts:${CONTRACTS_TAG}`. -# Пересборка триггерится через `workflow_dispatch` после ручного -# обновления contracts. -# -# Path-фильтр на `components/boot/**`, `components/cooptypes/**`, -# `components/factory/**` (boot их импортирует через workspace) и -# на сам Dockerfile/workflow. Изменения контрактов сами по себе -# bootstrap не триггерят — они идут через `dicoop/contracts:`, -# и для подхвата обновлённого образа нужно либо пушнуть ветку -# заново, либо запустить workflow вручную. -# -# Секреты: -# DOCKERHUB_USERNAME / DOCKERHUB_TOKEN — публикация в dicoop/* -# TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID — уведомления - -on: - push: - branches: [dev, testnet, main] - paths: - - 'components/boot/**' - - 'components/cooptypes/**' - - 'components/factory/**' - - 'pnpm-lock.yaml' - - 'pnpm-workspace.yaml' - - '.github/workflows/build-bootstrap.yaml' - workflow_dispatch: - inputs: - contracts_tag: - description: 'Тег dicoop/contracts (override; default = тег ветки)' - required: false - default: '' - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Determine docker tag and contracts tag - run: | - case "${{ github.ref_name }}" in - main) - echo "DOCKER_TAG=main" >> $GITHUB_ENV - echo "EXTRA_TAG=latest" >> $GITHUB_ENV - echo "CONTRACTS_TAG=main" >> $GITHUB_ENV - ;; - testnet) - echo "DOCKER_TAG=testnet" >> $GITHUB_ENV - echo "EXTRA_TAG=" >> $GITHUB_ENV - echo "CONTRACTS_TAG=testnet" >> $GITHUB_ENV - ;; - dev) - echo "DOCKER_TAG=dev" >> $GITHUB_ENV - echo "EXTRA_TAG=" >> $GITHUB_ENV - echo "CONTRACTS_TAG=dev" >> $GITHUB_ENV - ;; - *) - echo "Unsupported branch ${{ github.ref_name }}" >&2 - exit 1 - ;; - esac - - # workflow_dispatch override - if [ -n "${{ github.event.inputs.contracts_tag }}" ]; then - echo "CONTRACTS_TAG=${{ github.event.inputs.contracts_tag }}" >> $GITHUB_ENV - fi - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Pull base contracts image (sanity check) - run: | - docker pull "dicoop/contracts:${CONTRACTS_TAG}" - docker run --rm "dicoop/contracts:${CONTRACTS_TAG}" list | head -25 - - - name: Build and push image - run: | - IMAGE="dicoop/bootstrap" - docker build \ - --build-arg "CONTRACTS_TAG=${CONTRACTS_TAG}" \ - --label "org.opencontainers.image.revision=${{ github.sha }}" \ - --label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - --label "build.contracts_tag=${CONTRACTS_TAG}" \ - -t "${IMAGE}:${DOCKER_TAG}" \ - -f components/boot/docker/Dockerfile \ - . - docker push "${IMAGE}:${DOCKER_TAG}" - - if [ -n "${EXTRA_TAG}" ]; then - docker tag "${IMAGE}:${DOCKER_TAG}" "${IMAGE}:${EXTRA_TAG}" - docker push "${IMAGE}:${EXTRA_TAG}" - fi - - # SHA-pinned tag для воспроизводимости (тот же подход, что и - # в build-contracts.yaml — позволяет запинить bootstrap к - # конкретному коммиту, а не двигающемуся branch-тегу). - SHORT_SHA="${GITHUB_SHA::7}" - docker tag "${IMAGE}:${DOCKER_TAG}" "${IMAGE}:${DOCKER_TAG}-${SHORT_SHA}" - docker push "${IMAGE}:${DOCKER_TAG}-${SHORT_SHA}" - - - name: Verify pushed image - run: | - IMAGE="dicoop/bootstrap" - docker run --rm "${IMAGE}:${DOCKER_TAG}" verify - - - name: Telegram notify success - if: ${{ success() }} - run: | - curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \ - -d text="✅ [GITHUB MONO] dicoop/bootstrap:${DOCKER_TAG} (contracts=${CONTRACTS_TAG}, sha=${GITHUB_SHA::7})" - - - name: Telegram notify failure - if: ${{ failure() }} - run: | - curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \ - -d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \ - -d text="❌ [GITHUB MONO] Ошибка сборки dicoop/bootstrap на ${{ github.ref_name }} (sha=${GITHUB_SHA::7})" diff --git a/components/boot/docker/Dockerfile b/components/boot/docker/Dockerfile deleted file mode 100644 index cdd0d3394d8..00000000000 --- a/components/boot/docker/Dockerfile +++ /dev/null @@ -1,134 +0,0 @@ -# ke-bootstrap: образ-оркестратор первичного деплоя контрактов на KE-ноду. -# -# Назначение: -# Один раз при поднятии узла (testnet/local-dev) этот контейнер ждёт -# готовности RPC `${CHAIN_URL}/v1/chain/get_info`, затем запускает -# `boot:remote` — деплоит все контракты из примонтированного образа -# `dicoop/contracts` через RPC (`eosjs::setContract`), активирует -# protocol features, создаёт системный токен и т.д. После завершения -# контейнер выходит с кодом 0. -# -# Не управляет docker-контейнером ноды (в отличие от `boot`/`boot:clean`/ -# `boot:extra` — тех режимов, что вызывают `dockerode` для запуска ноды -# рядом с собой). Нода поднимается отдельным сервисом docker-compose -# (`dicoop/blockchain_v5.1.1`), и зависимость декларируется через -# `depends_on: ke-node: service_healthy`. -# -# Параметры (env): -# CHAIN_URL — URL RPC ноды (default `http://localhost:8888`). -# EOSIO_PUB_KEY — public key для системных аккаунтов (обязателен). -# EOSIO_PRV_KEY — private key для подписи (обязателен). -# PASSWORD — пароль для cleos-wallet (используется командой `unlock`). -# CONTRACTS_DIR — корень wasm/abi (default `/contracts`). -# RPC_WAIT_TIMEOUT_MS — таймаут ожидания RPC (default 120000). -# INSTALL_INITIAL_DATA=1 — опционально: записать кооператив `voskhod` в -# MongoDB+PostgreSQL (требует `MONGO_URI`, -# `PG_HOST`/`PG_USER`/...). Без этого флага -# bootstrap делает только on-chain работу. -# INSTALL_EXTRA_DATA=1 — то же + расширенный совет (4 доп. пайщика). -# -# Layout слоёв: -# Stage 1 (deps) — `pnpm install` всего workspace, нужен для -# workspace-зависимостей `@coopenomics/factory`, -# `cooptypes` (boot их импортирует). -# Stage 2 (build) — `pnpm run build` для cooptypes/factory/boot -# (unbuild → dist/index.cjs + .mjs). -# Stage 3 (deploy) — `pnpm deploy --filter @coopenomics/boot --prod` -# складывает приложение + только prod-deps в /app. -# workspace:* → реальные пакеты, без cross-link. -# Stage 4 (contracts) — alias для `dicoop/contracts:`, отсюда -# берутся wasm/abi через `COPY --from`. -# Stage 5 (runtime) — slim node:20-alpine, `/app` + `/contracts`. - -# Глобальный ARG до первого FROM — иначе он не виден в FROM следующих -# стадий (BuildKit делает ARG, объявленный после FROM, локальным для -# предыдущей стадии). -ARG CONTRACTS_TAG=dev - -# ── Stage 1: install dependencies ──────────────────────────────────── -FROM node:20-alpine AS deps - -RUN corepack enable && corepack prepare pnpm@9.0.6 --activate \ - && apk add --no-cache git python3 make g++ - -WORKDIR /repo - -# Минимальные root-файлы для мини-workspace (boot + его workspace-deps). -# Полный root /package.json + /pnpm-workspace.yaml сюда не годятся — -# они ссылаются на 14+ workspace-пакетов (cleos, controller, desktop, -# ...), которые в bootstrap-образе не нужны и в build-context не -# копируются. Использовать их → ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. -COPY components/boot/docker/bootstrap.package.json ./package.json -COPY components/boot/docker/bootstrap.pnpm-workspace.yaml ./pnpm-workspace.yaml - -# Рабочие пакеты, нужные для boot: -# - cooptypes / factory — workspace:* зависимости -# - boot — собственно сборка -COPY components/cooptypes/ ./components/cooptypes/ -COPY components/factory/ ./components/factory/ -COPY components/boot/ ./components/boot/ - -# `--no-frozen-lockfile` — мини-workspace не совпадает с корневым -# /pnpm-lock.yaml, поэтому pnpm генерирует свой lock внутри образа. -# В build-stage лок не сохраняется, поэтому drift здесь безопасен; -# воспроизводимость дают branch+sha tags на финальном образе. -RUN pnpm install --no-frozen-lockfile - -# ── Stage 2: build TS пакетов ──────────────────────────────────────── -FROM deps AS build - -# Порядок сборки важен: cooptypes → factory → boot (downstream зависят -# от dist/ upstream-пакетов). Lerna-граф решает это автоматически через -# --include-dependencies, но явное перечисление дешевле и предсказуемее. -RUN pnpm --filter cooptypes run build \ - && pnpm --filter @coopenomics/factory run build \ - && pnpm --filter @coopenomics/boot run build - -# ── Stage 3: pnpm deploy → standalone /app ─────────────────────────── -FROM build AS deploy - -# `pnpm deploy --prod` создаёт автономную копию пакета с разрешёнными -# `workspace:*` зависимостями (тянет реальные пакеты из workspace, не -# симлинки). devDeps отрезаются, так что esno/typescript/vitest не -# попадают в финальный образ. -RUN pnpm deploy --filter @coopenomics/boot --prod /app - -# ── Stage 4: contracts (wasm/abi артефакты) ────────────────────────── -# Тег этого образа должен совпадать с тегом ke-bootstrap (см. -# `.github/workflows/build-bootstrap.yaml`). Multi-stage `COPY` не -# зависит от того, что dicoop/contracts что-то делает в ENTRYPOINT — -# нам нужен только filesystem. ARG объявлен в самом начале файла -# (до первого FROM) — здесь только используется. -FROM dicoop/contracts:${CONTRACTS_TAG} AS contracts - -# ── Stage 5: runtime ───────────────────────────────────────────────── -FROM node:20-alpine AS runtime - -# bash + curl — entrypoint использует bash (set -e, конструкции `[[ ]]`) -# и опционально curl для дебага healthcheck'а. -RUN apk add --no-cache bash curl - -# Приложение -COPY --from=deploy /app /app - -# Артефакты контрактов (flat layout: /contracts//.{wasm,abi}) -COPY --from=contracts /contracts /contracts - -# Entrypoint скрипт лежит в исходниках boot, копируем отдельно -COPY components/boot/docker/entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - -WORKDIR /app - -# Дефолтные env. CHAIN_URL переопределяется в docker-compose, остальное -# — sane defaults для bootstrap-сценария. -ENV CONTRACTS_DIR=/contracts \ - CHAIN_URL=http://ke-node:8888 \ - RPC_WAIT_TIMEOUT_MS=120000 - -LABEL org.opencontainers.image.title="coopenomics-bootstrap" -LABEL org.opencontainers.image.description="One-shot bootstrapper для KE-ноды: ждёт RPC, деплоит контракты и инициализирует протокол" -LABEL org.opencontainers.image.source="https://github.com/coopenomics/mono" - -ENTRYPOINT ["/entrypoint.sh"] -CMD ["boot:remote"] diff --git a/components/boot/docker/bootstrap.package.json b/components/boot/docker/bootstrap.package.json deleted file mode 100644 index 4572bc8b792..00000000000 --- a/components/boot/docker/bootstrap.package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "monocoop-bootstrap-build", - "private": true, - "packageManager": "pnpm@9.0.6", - "_comment": "Минимальный root package.json для multi-stage сборки ke-bootstrap образа. Не используется в обычном workspace — там действует /package.json. Здесь только то, что нужно pnpm для построения графа из мини-набора пакетов: boot + его workspace-зависимости (cooptypes, factory). Все другие коопенономиксовые пакеты (cleos, controller, desktop, ...) сюда не попадают, чтобы не раздувать образ." -} diff --git a/components/boot/docker/bootstrap.pnpm-workspace.yaml b/components/boot/docker/bootstrap.pnpm-workspace.yaml deleted file mode 100644 index 383fdd496a1..00000000000 --- a/components/boot/docker/bootstrap.pnpm-workspace.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Минимальный pnpm-workspace для сборки ke-bootstrap образа. -# Состав совпадает с тем, что копируется в `components/boot/docker/Dockerfile` -# (boot + его workspace-зависимости). Полный список пакетов -# `components/*` сюда не попадает, чтобы pnpm install не пытался -# резолвить отсутствующие компоненты (cleos, controller, desktop, ...). -link-workspace-packages: true -packages: - - 'components/boot' - - 'components/cooptypes' - - 'components/factory' diff --git a/components/boot/docker/entrypoint.sh b/components/boot/docker/entrypoint.sh deleted file mode 100755 index 4fac66a07e2..00000000000 --- a/components/boot/docker/entrypoint.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# -# Entrypoint для образа ke-bootstrap (`dicoop/bootstrap`). -# -# Один и тот же бинарь поддерживает несколько режимов через первый -# аргумент CMD (по умолчанию `boot:remote`). Все режимы — short-lived, -# контейнер выходит после завершения. Перезапускать его при сбое RPC не -# нужно: `boot:remote` сам ждёт готовности ноды по `CHAIN_URL`. -# -# Доступные режимы: -# boot:remote — деплой контрактов на уже работающую ноду (default) -# verify — sanity-check: вывести версию + список контрактов -# shell — открыть bash для отладки (только если контейнер -# запущен интерактивно) -# -# В CI (`build-bootstrap.yaml`) `verify` запускается после push'а как -# smoke-test, чтобы убедиться что образ хотя бы стартует. - -set -euo pipefail - -CMD="${1:-boot:remote}" - -# Проверяем минимальный набор обязательных env'ов перед стартом — так -# падение происходит сразу с понятной ошибкой, а не где-то внутри boot -# при попытке подписать транзакцию пустым ключом. -require_env() { - local var - for var in "$@"; do - if [ -z "${!var:-}" ]; then - echo "ERROR: env $var is required for mode '$CMD'" >&2 - exit 1 - fi - done -} - -case "$CMD" in - boot:remote) - require_env CHAIN_URL EOSIO_PUB_KEY EOSIO_PRV_KEY - echo "[ke-bootstrap] CHAIN_URL=$CHAIN_URL" - echo "[ke-bootstrap] CONTRACTS_DIR=${CONTRACTS_DIR:-/contracts}" - echo "[ke-bootstrap] starting boot:remote..." - # node dist/index.cjs — pnpm deploy укладывает unbuild-output сюда - exec node /app/dist/index.cjs boot:remote - ;; - - verify) - # Smoke-test для CI после push'а образа. - echo "[ke-bootstrap] node version:"; node --version - echo "[ke-bootstrap] dist/index.cjs:"; ls -la /app/dist/index.cjs || exit 1 - echo "[ke-bootstrap] CONTRACTS_DIR contents:"; ls /contracts | head -30 - echo "[ke-bootstrap] manifest:" - if [ -f /contracts/manifest.json ]; then - cat /contracts/manifest.json - else - echo "(missing)" - fi - echo "[ke-bootstrap] cli help (sanity-check, RPC недоступен — это ожидаемо):" - # `--help` не делает RPC-вызовов, только парсит commander - node /app/dist/index.cjs --help || true - ;; - - shell) - exec /bin/bash - ;; - - *) - # Любой другой аргумент пробрасываем дальше как arg для node (ESC-hatch - # для ad-hoc-сценариев типа `docker run ... cleos get info`). - exec node /app/dist/index.cjs "$@" - ;; -esac