From 43ef260ae25befa295416761cadbc5c10caceeac Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 1 Apr 2026 16:14:38 +0800 Subject: [PATCH] =?UTF-8?q?fix/=20=E4=BF=AE=E5=A4=8D=E5=90=8E=E7=AB=AFdock?= =?UTF-8?q?erworkflow=E4=BC=9A=E8=B7=91=E4=B8=A4=E6=AC=A1alembic=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-build-deploy.yml | 13 +++++++++- api/Dockerfile | 6 ++--- .../0005_cleanup_cross_chapter_story_links.py | 4 +-- api/docker-compose.yml | 11 ++++---- api/tests/test_alembic_revision_ids.py | 25 +++++++++++++++++++ 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 api/tests/test_alembic_revision_ids.py diff --git a/.github/workflows/docker-build-deploy.yml b/.github/workflows/docker-build-deploy.yml index 6d03dc0..a9c2d3d 100644 --- a/.github/workflows/docker-build-deploy.yml +++ b/.github/workflows/docker-build-deploy.yml @@ -280,7 +280,17 @@ jobs: fi mv 'docker-compose.candidate.yml' '$COMPOSE_FILE' mv '.env.candidate' '.env' - docker compose -f '$COMPOSE_FILE' up -d --remove-orphans + if ! docker compose -f '$COMPOSE_FILE' up -d --remove-orphans; then + echo 'docker compose up 失败,输出 api 状态与最近日志...' + docker compose -f '$COMPOSE_FILE' ps || true + API_CID=\$(docker compose -f '$COMPOSE_FILE' ps -q api || true) + if [ -n \"\$API_CID\" ]; then + docker inspect -f '{{json .State}}' \"\$API_CID\" || true + fi + docker compose -f '$COMPOSE_FILE' logs --tail=120 api || true + docker compose -f '$COMPOSE_FILE' logs --tail=80 celery-worker || true + exit 1 + fi echo '等待服务启动...' sleep 20 docker image prune -f || true @@ -314,6 +324,7 @@ jobs: if [ \"\$API_HEALTH\" != 'healthy' ]; then echo 'api 容器未在预期时间内变为 healthy' + docker inspect -f '{{json .State}}' \"\$API_CID\" || true docker compose logs --tail=80 api || true exit 1 fi diff --git a/api/Dockerfile b/api/Dockerfile index f1d3f21..4b44549 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -37,8 +37,8 @@ USER appuser EXPOSE 8000 -HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ +HEALTHCHECK --interval=15s --timeout=5s --start-period=120s --retries=6 \ CMD python -c "import http.client; conn = http.client.HTTPConnection('localhost', 8000); conn.request('GET', '/health'); r = conn.getresponse(); exit(0 if r.status == 200 else 1)" || exit 1 -# 启动前执行 Alembic 迁移,再启动 uvicorn -CMD ["sh", "-c", "uv run alembic upgrade head && uv run uvicorn main:app --host 0.0.0.0 --port 8000"] +# Alembic 迁移放到 FastAPI startup(带重试与日志);入口仅启动 web 进程。 +CMD ["sh", "-c", "uv run uvicorn main:app --host 0.0.0.0 --port 8000"] diff --git a/api/alembic/versions/0005_cleanup_cross_chapter_story_links.py b/api/alembic/versions/0005_cleanup_cross_chapter_story_links.py index 15ef707..0a06c5a 100644 --- a/api/alembic/versions/0005_cleanup_cross_chapter_story_links.py +++ b/api/alembic/versions/0005_cleanup_cross_chapter_story_links.py @@ -2,7 +2,7 @@ 修复历史 bug:路由曾允许跨章节 append,导致同一 Story 挂到多章、内容重复。 -Revision ID: 0005_cleanup_cross_chapter_story_links +Revision ID: 0005_cleanup_story_links Revises: 0004_memory_embedding_1024 """ @@ -12,7 +12,7 @@ import sqlalchemy as sa from alembic import op -revision: str = "0005_cleanup_cross_chapter_story_links" +revision: str = "0005_cleanup_story_links" down_revision: Union[str, None] = "0004_memory_embedding_1024" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None diff --git a/api/docker-compose.yml b/api/docker-compose.yml index 5b63f20..eeba7b8 100644 --- a/api/docker-compose.yml +++ b/api/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: # PostgreSQL 数据库(使用最新版 17) postgres: @@ -66,6 +64,7 @@ services: - .env environment: - ASR_MODEL_CACHE_DIR=/app/models/whisper + - ALEMBIC_STARTUP_FAIL_FAST=true volumes: - /root/apiclient_key.pem:/app/certs/apiclient_key.pem:ro restart: always @@ -76,10 +75,10 @@ services: condition: service_healthy healthcheck: test: ["CMD", "python", "-c", "import http.client; conn = http.client.HTTPConnection('localhost', 8000); conn.request('GET', '/health'); r = conn.getresponse(); exit(0 if r.status == 200 else 1)"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 15s + interval: 15s + timeout: 5s + retries: 6 + start_period: 120s networks: - life-echo-network logging: diff --git a/api/tests/test_alembic_revision_ids.py b/api/tests/test_alembic_revision_ids.py new file mode 100644 index 0000000..30f18d8 --- /dev/null +++ b/api/tests/test_alembic_revision_ids.py @@ -0,0 +1,25 @@ +from pathlib import Path +import re + + +REVISION_RE = re.compile(r'^revision: str = "([^"]+)"$', re.MULTILINE) +ALEMBIC_VERSION_NUM_MAX_LEN = 32 + + +def test_alembic_revision_ids_fit_default_version_num_column() -> None: + versions_dir = Path(__file__).resolve().parent.parent / "alembic" / "versions" + too_long: list[tuple[str, int]] = [] + + for path in sorted(versions_dir.glob("*.py")): + text = path.read_text(encoding="utf-8") + match = REVISION_RE.search(text) + if match is None: + continue + revision = match.group(1) + if len(revision) > ALEMBIC_VERSION_NUM_MAX_LEN: + too_long.append((revision, len(revision))) + + assert not too_long, ( + "Alembic revision ids exceed the default alembic_version.version_num " + f"limit ({ALEMBIC_VERSION_NUM_MAX_LEN}): {too_long}" + )