Jun 26 MT-2 phase fixes migration script error

This commit is contained in:
2026-06-26 17:07:32 -04:00
parent 07af8ee35a
commit dd41c1fd59
3 changed files with 602 additions and 2 deletions
+74 -1
View File
@@ -46,6 +46,9 @@ _DEFAULT_VERSION_LOCATIONS = os.path.join(_REPO_ROOT, 'migrations', 'versions')
# Everything except a deleted tenant should be kept schema-current.
MIGRATABLE_STATUSES = ('provisioning', 'active', 'suspended')
# Squashed baseline that builds the full schema (migrations/versions root).
BASELINE_REVISION = '0003_add_user_active'
TenantRef = namedtuple('TenantRef', ['id', 'slug', 'db_uri'])
@@ -125,7 +128,56 @@ def upgrade_tenant(tenant, script_location=None, version_locations=None, record_
raise
# ── CLI ────────────────────────────────────────────────────────────────────
def bootstrap_tenant(tenant, script_location=None, version_locations=None,
baseline_rev=BASELINE_REVISION, record_job=True):
"""Build a FRESH tenant database, then mark it current.
Runs the squashed baseline (full schema) only, then `stamp head` — the
historical phase migrations are NOT replayed (several are not idempotent and
would conflict with the full-schema baseline). Use this for brand-new tenant
databases; use upgrade_tenant() for ongoing incremental migrations.
Returns the stamped head revision.
"""
db_uri = tenant.db_uri
cfg = _make_config(db_uri, script_location, version_locations)
job_id = None
if record_job:
with control_session() as s:
job = ProvisioningJob(tenant_id=tenant.id, action='migrate',
status='running', created_at=now_eastern())
s.add(job)
s.flush()
job_id = job.id
try:
with contextlib.redirect_stdout(io.StringIO()):
command.upgrade(cfg, baseline_rev) # build full schema (baseline only)
command.stamp(cfg, 'head') # mark at head without replaying phases
applied = current_revision(db_uri)
with control_session() as s:
t = s.get(Tenant, tenant.id)
if t is not None:
t.alembic_head = applied
if job_id is not None:
j = s.get(ProvisioningJob, job_id)
if j is not None:
j.status = 'ok'
j.finished_at = now_eastern()
j.log = f'bootstrapped (baseline {baseline_rev}) + stamped {applied}'
return applied
except Exception as e:
if job_id is not None:
with control_session() as s:
j = s.get(ProvisioningJob, job_id)
if j is not None:
j.status = 'failed'
j.finished_at = now_eastern()
j.log = f'{type(e).__name__}: {e}'
raise
def _select_tenants(selector):
with control_session() as s:
@@ -174,6 +226,22 @@ def _cmd_current(args):
return 0
def _cmd_bootstrap(args):
tenants = _select_tenants(args.tenant)
if not tenants:
print(f'No matching tenants for --tenant {args.tenant}.')
return 0
failures = 0
for t in tenants:
try:
applied = bootstrap_tenant(t)
print(f' [ok] {t.slug} (id={t.id}) bootstrapped -> {applied}')
except Exception as e:
failures += 1
print(f' [FAIL] {t.slug} (id={t.id}): {type(e).__name__}: {e}')
return 1 if failures else 0
def _cmd_heads(_args):
print(chain_head())
return 0
@@ -192,6 +260,11 @@ def main(argv=None):
p_cur.add_argument('--tenant', default='all', help="'all', or a tenant id or slug")
p_cur.set_defaults(func=_cmd_current)
p_boot = sub.add_parser('bootstrap',
help='Build a FRESH tenant DB (baseline + stamp head)')
p_boot.add_argument('--tenant', required=True, help="'all', or a tenant id or slug")
p_boot.set_defaults(func=_cmd_bootstrap)
sub.add_parser('heads', help='Show the chain head revision').set_defaults(func=_cmd_heads)
args = parser.parse_args(argv)