revert(boot): откат отдельного dicoop/bootstrap образа
Boot — это и есть бутстрап. Дубликат через отдельный образ создаёт второй workflow и две точки сборки там, где для testnet/prod достаточно запустить existing pnpm -F @coopenomics/boot run boot:remote внутри dicoop/mono-base:<tag>, который уже собирается через build-containers.yaml. Что остаётся в dev (полезное из предыдущих коммитов): - boot:remote команда — без dockerode, для сценария когда нода поднята отдельным сервисом docker-compose. - CONTRACTS_DIR env-driven path в configs/contracts.ts — позволяет монтировать dicoop/contracts:<tag> как volume. - "build" script в boot/package.json для unbuild. Удалено: - components/boot/docker/ (Dockerfile + entrypoint + bootstrap.package.json + bootstrap.pnpm-workspace.yaml) - .github/workflows/build-bootstrap.yaml Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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:<tag>` должен существовать
|
||||
# (он публикуется через `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:<tag>`,
|
||||
# и для подхвата обновлённого образа нужно либо пушнуть ветку
|
||||
# заново, либо запустить 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})"
|
||||
@@ -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:<tag>`, отсюда
|
||||
# берутся 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/<name>/<name>.{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"]
|
||||
@@ -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, ...) сюда не попадают, чтобы не раздувать образ."
|
||||
}
|
||||
@@ -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'
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user