- migrations: MIGRATION_TABLES now equals POSTGRES_SCHEMA tables
(uploads, task_attachments, task_followups, environments added);
SEQUENCE_TABLES matches IDENTITY tables; task_followups
sender_task_id column ensured before insert; UNMIGRATED_TABLES
and migration_coverage() report tables with no PG schema instead
of silently skipping them; CLI prints the coverage report.
- deploy: DEFAULT_TEST_SUITES gains deploy/tests (discover -s only,
never package path); regression tests pin the default list.
- monitor: MONITOR_SINGLE_HOST=1 makes the server check single-host;
default stays dual to avoid silent downgrade; server_mode is in
the JSON. Optional fail-only alert outlet (webhook or mail,
default off, alert body is pass/fail JSON only, no secrets).
- backup/rollback: README and restore-drill document what is
recoverable (DB, code) vs not (worktrees, traces, uploads, host
configs); rollback.sh shares the server-unit list and dual-port
health success standard with host_deploy.sh (sync test guards it).
- public entry: Caddyfile uses native {$DOMAIN}/{$ACME_EMAIL}
variables plus a caddy.service template; README documents the
steps and the current BT-nginx production status.
52 lines
1.9 KiB
Python
52 lines
1.9 KiB
Python
"""把 SQLite 控制面库迁移到 Postgres 的命令行入口。
|
|
|
|
用法(Postgres 连接参数只走 CLOUD_DB_* 环境变量,本脚本
|
|
不接受、不打印任何密码或密钥):
|
|
|
|
CLOUD_DB_HOST=... CLOUD_DB_PORT=5432 CLOUD_DB_NAME=... \
|
|
CLOUD_DB_USER=... CLOUD_DB_PASSWORD=... \
|
|
python3 -m cloud.migrate_sqlite_to_postgres --sqlite <sqlite 文件>
|
|
|
|
输出:每张表本次插入的行数(第二次跑应全为 0,即幂等,
|
|
不重复插)。驱动缺失时明确报错,绝不静默回落到 SQLite;
|
|
目标库须事先存在(本脚本不建库,建库见 cloud/README.md)。
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from cloud.migrations import (
|
|
migrate_sqlite_to_postgres,
|
|
migration_coverage,
|
|
)
|
|
|
|
|
|
def main(argv=None):
|
|
parser = argparse.ArgumentParser(
|
|
description="Migrate a SQLite control-plane DB to Postgres.")
|
|
parser.add_argument("--sqlite", required=True,
|
|
help="source SQLite file path")
|
|
args = parser.parse_args(argv)
|
|
result = migrate_sqlite_to_postgres(args.sqlite)
|
|
total = 0
|
|
for table, count in result.items():
|
|
print("%s: %d" % (table, count))
|
|
total += count
|
|
print("total inserted: %d" % total)
|
|
# 覆盖报告:只含表名,不含行内容与任何密钥。missing 非空
|
|
# 即清单与 schema 对齐回归;unmigrated 是目标库无此表、
|
|
# 本迁移无法迁移的表,须另行处理,不能读成“已迁完”。
|
|
coverage = migration_coverage()
|
|
if coverage["missing_from_migration"]:
|
|
print("WARNING missing_from_migration: %s"
|
|
% ", ".join(coverage["missing_from_migration"]))
|
|
print("unmigrated (no PG schema, NOT migrated): %s"
|
|
% ", ".join(coverage["unmigrated_no_pg_schema"]))
|
|
print("skipped by design: %s"
|
|
% ", ".join(coverage["skipped_by_design"]))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|