更新docker构建cd (#10)

* update github actions

* update github actions

* update github actions

* update github actions

* update github actions

* update github actions

* update github actions

---------

Co-authored-by: Kevin <kevin@brighteng.org>
This commit is contained in:
Sully
2026-03-13 23:41:56 +08:00
committed by GitHub
parent 55b342b623
commit 9636c059d0
5 changed files with 89 additions and 45 deletions

View File

@@ -197,33 +197,21 @@ jobs:
EFFECTIVE_MIGRATION_DATABASE_URL=""
resolve_db_config() {
local database_url=""
database_url="$(sed -n 's/^DATABASE_URL=//p' "$CANDIDATE_ENV" | head -n 1)"
if [ -z "$database_url" ]; then
echo "candidate env 中未找到 DATABASE_URL"
exit 1
fi
case "$database_url" in
\"*\") database_url="${database_url:1:${#database_url}-2}" ;;
\'*\') database_url="${database_url:1:${#database_url}-2}" ;;
esac
mapfile -t parsed_db_parts < <(
CANDIDATE_ENV_PATH="$CANDIDATE_ENV" python3 - <<'PY'
import os
from pathlib import Path
from urllib.parse import unquote, urlsplit
env_path = Path(os.environ["CANDIDATE_ENV_PATH"])
database_url = None
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
if line.startswith("DATABASE_URL="):
value = line.split("=", 1)[1].strip()
if value[:1] == value[-1:] and value[:1] in {'"', "'"}:
value = value[1:-1]
database_url = value
break
if not database_url:
raise SystemExit("DATABASE_URL not found in candidate env")
parts = urlsplit(database_url)
print(unquote(parts.username or ""))
print(unquote(parts.password or ""))
print((parts.path or "/").lstrip("/"))
PY
python3 -c 'import sys; from urllib.parse import unquote, urlsplit; parts = urlsplit(sys.argv[1]); print(unquote(parts.username or "")); print(unquote(parts.password or "")); print((parts.path or "/").lstrip("/"))' "$database_url"
)
EFFECTIVE_DB_USER="${DB_USER:-${parsed_db_parts[0]}}"
@@ -240,22 +228,7 @@ jobs:
EFFECTIVE_DB_PASSWORD="$EFFECTIVE_DB_PASSWORD" \
EFFECTIVE_DB_NAME="$EFFECTIVE_DB_NAME" \
DB_CONTAINER="$DB_CONTAINER" \
python3 - <<'PY'
import os
from urllib.parse import quote
user = quote(os.environ["EFFECTIVE_DB_USER"], safe="")
password = os.environ.get("EFFECTIVE_DB_PASSWORD", "")
database = quote(os.environ["EFFECTIVE_DB_NAME"], safe="")
host = os.environ["DB_CONTAINER"]
if password:
auth = f"{user}:{quote(password, safe='')}@"
else:
auth = f"{user}@"
print(f"postgresql://{auth}{host}:5432/{database}")
PY
python3 -c 'import os; from urllib.parse import quote; user = quote(os.environ["EFFECTIVE_DB_USER"], safe=""); password = os.environ.get("EFFECTIVE_DB_PASSWORD", ""); database = quote(os.environ["EFFECTIVE_DB_NAME"], safe=""); host = os.environ["DB_CONTAINER"]; auth = (user + ":" + quote(password, safe="") + "@") if password else (user + "@"); print("postgresql://%s%s:5432/%s" % (auth, host, database))'
)"
}
@@ -314,6 +287,33 @@ jobs:
mkdir -p "$BACKUP_DIR"
resolve_db_config
# region agent log
echo "=== DEBUG: migration database url selection ==="
CANDIDATE_DATABASE_URL="$(sed -n 's/^DATABASE_URL=//p' "$CANDIDATE_ENV" | head -n 1)"
CANDIDATE_DATABASE_URL="$CANDIDATE_DATABASE_URL" \
EFFECTIVE_MIGRATION_DATABASE_URL="$EFFECTIVE_MIGRATION_DATABASE_URL" \
python3 - <<'PY'
import os
from urllib.parse import urlsplit
def host_of(value: str) -> str:
if not value:
return "<empty>"
value = value.strip().strip('"').strip("'")
return urlsplit(value).hostname or "<missing-host>"
print(f"candidate_database_host={host_of(os.environ.get('CANDIDATE_DATABASE_URL', ''))}")
print(f"migration_database_host={host_of(os.environ.get('EFFECTIVE_MIGRATION_DATABASE_URL', ''))}")
PY
docker run --rm \
--network "$NETWORK_NAME" \
--env-file "$CANDIDATE_ENV" \
-e MIGRATION_DATABASE_URL="$EFFECTIVE_MIGRATION_DATABASE_URL" \
--entrypoint python \
"$IMAGE_TAG" -c "import os; from urllib.parse import urlsplit; mig=os.getenv('MIGRATION_DATABASE_URL','').strip(); db=os.getenv('DATABASE_URL','').strip(); print('container_migration_database_host=' + (urlsplit(mig).hostname if mig else '<empty>')); print('container_database_host=' + (urlsplit(db).hostname if db else '<empty>'))"
# endregion agent log
if docker ps --format '{{.Names}}' | grep -qx "$API_CONTAINER"; then
CURRENT_API_RUNNING=1
fi

View File

@@ -102,7 +102,7 @@ ENABLE_TEST_SUBSCRIPTION=1
# 回忆录图片生成配置
# =============================================================================
# 总开关true 启用,不设置则关闭)
MEMOIR_IMAGE_ENABLED=false
MEMOIR_IMAGE_ENABLED=true
# 每章最多生成图片数
MEMOIR_IMAGE_MAX_PER_CHAPTER=2
# 轮询间隔(秒)和最大尝试次数

View File

@@ -14,6 +14,22 @@ CREATE TABLE IF NOT EXISTS chapter_sections (
CREATE INDEX IF NOT EXISTS ix_chapter_sections_chapter_id ON chapter_sections(chapter_id);
CREATE INDEX IF NOT EXISTS ix_chapter_sections_order ON chapter_sections(chapter_id, order_index);
-- 若 chapter_sections 已存在且已提前切换到 image_id 结构,重跑迁移时需要临时补回 image 列,
-- 以便后续 Python 脚本先完成 JSON -> memoir_images 的数据搬迁。
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'chapter_sections'
AND column_name = 'image'
) THEN
ALTER TABLE chapter_sections ADD COLUMN image JSONB;
RAISE NOTICE '已补回 chapter_sections.image供数据迁移使用';
END IF;
END $$;
-- ========== 2. chapters 表增加 cover_image ==========
DO $$
BEGIN

View File

@@ -22,13 +22,27 @@ load_dotenv()
import psycopg
from sqlalchemy import create_engine, text
from sqlalchemy.engine import Engine
from urllib.parse import urlsplit
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
def get_engine() -> Engine:
url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/life_echo")
migration_url = os.getenv("MIGRATION_DATABASE_URL")
database_url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/life_echo")
url = migration_url or database_url
# region agent log
logger.info(
"DEBUG migration env selection: migration_present=%s database_present=%s selected=%s selected_host=%s migration_host=%s database_host=%s",
bool(migration_url),
bool(database_url),
"MIGRATION_DATABASE_URL" if migration_url else "DATABASE_URL",
urlsplit(url).hostname or "<missing-host>",
urlsplit(migration_url).hostname if migration_url else "<empty>",
urlsplit(database_url).hostname or "<missing-host>",
)
# endregion agent log
return create_engine(url.replace("postgresql://", "postgresql+psycopg://"), pool_pre_ping=True)

View File

@@ -19,6 +19,7 @@ load_dotenv()
from sqlalchemy import text
from sqlalchemy.engine import Engine
from urllib.parse import urlsplit
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
@@ -26,7 +27,20 @@ logger = logging.getLogger(__name__)
def get_engine() -> Engine:
from sqlalchemy import create_engine
url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/life_echo")
migration_url = os.getenv("MIGRATION_DATABASE_URL")
database_url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@localhost:5432/life_echo")
url = migration_url or database_url
# region agent log
logger.info(
"DEBUG migration env selection: migration_present=%s database_present=%s selected=%s selected_host=%s migration_host=%s database_host=%s",
bool(migration_url),
bool(database_url),
"MIGRATION_DATABASE_URL" if migration_url else "DATABASE_URL",
urlsplit(url).hostname or "<missing-host>",
urlsplit(migration_url).hostname if migration_url else "<empty>",
urlsplit(database_url).hostname or "<missing-host>",
)
# endregion agent log
return create_engine(url.replace("postgresql://", "postgresql+psycopg://"), pool_pre_ping=True)