From 74e291f3c8f1260cf26366ad4a5cf97b6b019777 Mon Sep 17 00:00:00 2001 From: Fedorov Vladimir Date: Tue, 13 Oct 2026 15:30:11 +0700 Subject: [PATCH] up --- Makefile | 10 +++++- README.md | 18 +++++++++- prod | 2 +- scripts/dev.sh | 35 +++++++++++++++++++ tests/dev_commands.py | 78 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 scripts/dev.sh create mode 100644 tests/dev_commands.py diff --git a/Makefile b/Makefile index 796268e..5292afa 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: check build prod-build publish venue venue-update venue-https +.PHONY: check build prod-build publish dev game stop venue venue-update venue-https PROD_DIR ?= prod @@ -25,6 +25,14 @@ venue: venue-update venue-https: venue +dev: + bash scripts/dev.sh + +game: venue + +stop: + docker compose -f venue/docker-compose.yml -f venue/docker-compose.https.yml --env-file venue/.env down + publish: @test -n "$(MSG)" || (echo 'Usage: make publish MSG="commit message"' >&2; exit 1) @$(MAKE) build diff --git a/README.md b/README.md index beed3b6..57899c8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,22 @@ Требования: Go, Docker Compose, Node.js 20.19+ или 22.12+, npm. +Короткие команды из корня проекта: + +```sh +make dev # разработка: локальные БД/RustFS, Go-бэкенд и Vite-фронтенд +make game # локальная игра: запуск venue с HTTPS из готовых артефактов +make stop # остановка venue, данные сохраняются +``` + +`make dev` запускает приложения в текущем терминале; `Ctrl+C` останавливает оба +приложения, БД и RustFS остаются запущены. Перед первым запуском подготовьте +зависимости и `.env` по инструкции ниже; схема БД должна быть подготовлена через +`make -C evening_detective_server migrate-up` (требуется Goose). +`make game` — алиас `make venue`: перед первым запуском нужны сборка +`make prod-build`, сертификат и локальный DNS, как описано в разделе площадки. +`make stop` останавливает только площадку; процессы разработки не затрагивает. + ### 1. Зависимости сервера ```sh @@ -104,7 +120,7 @@ DNS по `http://:`. Ссылки на материа Миграции Goose выполняются до старта приложения. Для остановки без потери данных: ```sh -docker compose -f venue/docker-compose.yml -f venue/docker-compose.https.yml --env-file venue/.env down +make stop ``` Не используйте `down -v`: это удаляет данные. После изменений кода или миграций diff --git a/prod b/prod index 1400500..4205e86 160000 --- a/prod +++ b/prod @@ -1 +1 @@ -Subproject commit 1400500d504f40371dbecde776a91a3e1b7ddce8 +Subproject commit 4205e86ef6b8a58cedfffbbd6053cbcf853cd420 diff --git a/scripts/dev.sh b/scripts/dev.sh new file mode 100644 index 0000000..73a199a --- /dev/null +++ b/scripts/dev.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +if [[ ! -f evening_detective_server/.env ]]; then + echo 'Сначала скопируйте evening_detective_server/.env.example в evening_detective_server/.env.' >&2 + exit 1 +fi +if [[ ! -d evening_detective_frontend/node_modules ]]; then + echo 'Сначала выполните npm --prefix evening_detective_frontend ci.' >&2 + exit 1 +fi + +docker compose -f evening_detective_server/docker-compose-db.yml up -d --wait + +# Отдельные группы позволяют остановить и go run/npm, и их дочерние процессы. +set -m +pids=() +cleanup() { + trap - EXIT INT TERM + for pid in "${pids[@]}"; do + kill -TERM -- "-$pid" 2>/dev/null || true + done + wait || true +} +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +make -C evening_detective_server run & +pids+=("$!") +(cd evening_detective_frontend && exec npm run dev) & +pids+=("$!") +echo 'Разработка: http://localhost:5173; API: http://localhost:8090. Остановка: Ctrl+C.' +wait -n "${pids[@]}" diff --git a/tests/dev_commands.py b/tests/dev_commands.py new file mode 100644 index 0000000..38f64b6 --- /dev/null +++ b/tests/dev_commands.py @@ -0,0 +1,78 @@ +"""Check development startup/cleanup with stubs; never touch running services.""" + +import os +from pathlib import Path +import signal +import subprocess +import tempfile +import time + + +ROOT = Path(__file__).resolve().parents[1] +with tempfile.TemporaryDirectory(prefix='detective-dev-') as directory: + root = Path(directory) + (root / 'scripts').mkdir() + (root / 'scripts/dev.sh').write_text((ROOT / 'scripts/dev.sh').read_text()) + (root / 'evening_detective_server').mkdir() + (root / 'evening_detective_frontend/node_modules').mkdir(parents=True) + bin_dir = root / 'bin' + bin_dir.mkdir() + log = root / 'commands.log' + env = {**os.environ, 'PATH': str(bin_dir) + os.pathsep + os.environ['PATH'], 'TEST_LOG': str(log)} + for name in ['docker', 'make', 'npm']: + stub = bin_dir / name + stub.write_text( + '#!/usr/bin/env bash\n' + f'echo "{name} $*" >> "$TEST_LOG"\n' + + ('exit 0\n' if name == 'docker' else + f'trap \'echo "{name} stopped" >> "$TEST_LOG"; exit 0\' TERM\n' + 'sleep 60 &\n' + 'wait\n') + ) + stub.chmod(0o755) + + script = str(root / 'scripts/dev.sh') + result = subprocess.run(['bash', script], env=env, capture_output=True, text=True) + assert result.returncode != 0 and '.env.example' in result.stderr + assert not log.exists(), 'Missing config must fail before Docker' + (root / 'evening_detective_server/.env').touch() + + def wait_for(text): + deadline = time.monotonic() + 5 + while time.monotonic() < deadline: + if log.exists() and text in log.read_text(): + return + time.sleep(.05) + raise AssertionError('Missing log entry: ' + text) + + for sig in [signal.SIGINT, signal.SIGTERM]: + log.unlink(missing_ok=True) + process = subprocess.Popen(['bash', script], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + try: + wait_for('npm run dev') + wait_for('make -C evening_detective_server run') + process.send_signal(sig) + process.communicate(timeout=5) + assert process.returncode == 128 + sig + commands = log.read_text() + assert 'compose -f evening_detective_server/docker-compose-db.yml up -d --wait' in commands + assert 'make stopped' in commands and 'npm stopped' in commands, commands + finally: + if process.poll() is None: + process.send_signal(signal.SIGTERM) + process.communicate(timeout=5) + + (bin_dir / 'make').write_text('#!/bin/sh\nsleep .2\nexit 7\n') + log.unlink(missing_ok=True) + result = subprocess.run(['bash', script], env=env, capture_output=True, timeout=5) + assert result.returncode == 7 + assert 'npm stopped' in log.read_text(), 'Backend failure must stop frontend' + +stop = subprocess.check_output(['make', '-n', 'stop'], cwd=ROOT, text=True) +assert 'venue/docker-compose.yml' in stop and 'venue/docker-compose.https.yml' in stop +assert '--env-file venue/.env down' in stop and '-v' not in stop.split() +game = subprocess.check_output(['make', '-n', 'game'], cwd=ROOT, text=True) +assert game.strip() == 'bash venue/deploy.sh' +dev = subprocess.check_output(['make', '-n', 'dev'], cwd=ROOT, text=True) +assert dev.strip() == 'bash scripts/dev.sh' +print('PASS: command aliases, config preflight, startup, Ctrl+C/SIGTERM cleanup, backend failure; stop preserves volumes')