更新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:
84
.github/workflows/docker-build-deploy.yml
vendored
84
.github/workflows/docker-build-deploy.yml
vendored
@@ -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
|
||||
|
||||
@@ -102,7 +102,7 @@ ENABLE_TEST_SUBSCRIPTION=1
|
||||
# 回忆录图片生成配置
|
||||
# =============================================================================
|
||||
# 总开关(true 启用,不设置则关闭)
|
||||
MEMOIR_IMAGE_ENABLED=false
|
||||
MEMOIR_IMAGE_ENABLED=true
|
||||
# 每章最多生成图片数
|
||||
MEMOIR_IMAGE_MAX_PER_CHAPTER=2
|
||||
# 轮询间隔(秒)和最大尝试次数
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user