refactor(migrations): squash 75 migrations into 12 per-module initial migrations

The old migration chain was broken (downgrade path through vendor->merchant
rename made rollbacks impossible). This squashes everything into fresh
per-module migrations with zero schema drift, verified by autogenerate.

Changes:
- Replace 75 accumulated migrations with 12 per-module initial migrations
  (core, billing, catalog, marketplace, cms, customers, orders, inventory,
  cart, messaging, loyalty, dev_tools) in a linear chain
- Fix make db-reset to use SQL DROP SCHEMA instead of alembic downgrade base
- Enable migration autodiscovery for all modules (migrations_path in definitions)
- Rewrite alembic/env.py to import all 75 model tables across 13 modules
- Fix AdminNotification import (was incorrectly from tenancy, now from messaging)
- Update squash_migrations.py to handle all module migration directories

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 11:51:37 +01:00
parent dad02695f6
commit c3d26e9aa4
111 changed files with 2285 additions and 11723 deletions

View File

@@ -85,6 +85,7 @@ dev_tools_module = ModuleDefinition(
schemas_path="app.modules.dev_tools.schemas",
exceptions_path="app.modules.dev_tools.exceptions",
tasks_path="app.modules.dev_tools.tasks",
migrations_path="migrations",
# Note: Code quality and test tasks are on-demand, not scheduled.
# If scheduled scans are desired, add ScheduledTask entries here.
)

View File

@@ -0,0 +1,163 @@
"""dev_tools initial - test runs, architecture scans, violations
Revision ID: dev_tools_001
Revises: loyalty_001
Create Date: 2026-02-07
"""
from alembic import op
import sqlalchemy as sa
revision = "dev_tools_001"
down_revision = "loyalty_001"
branch_labels = None
depends_on = None
def upgrade() -> None:
# --- test_runs ---
op.create_table(
"test_runs",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("timestamp", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, index=True),
sa.Column("total_tests", sa.Integer(), nullable=True),
sa.Column("passed", sa.Integer(), nullable=True),
sa.Column("failed", sa.Integer(), nullable=True),
sa.Column("errors", sa.Integer(), nullable=True),
sa.Column("skipped", sa.Integer(), nullable=True),
sa.Column("xfailed", sa.Integer(), nullable=True),
sa.Column("xpassed", sa.Integer(), nullable=True),
sa.Column("coverage_percent", sa.Float(), nullable=True),
sa.Column("duration_seconds", sa.Float(), nullable=True),
sa.Column("triggered_by", sa.String(100), nullable=True),
sa.Column("git_commit_hash", sa.String(40), nullable=True),
sa.Column("git_branch", sa.String(100), nullable=True),
sa.Column("test_path", sa.String(500), nullable=True),
sa.Column("pytest_args", sa.String(500), nullable=True),
sa.Column("status", sa.String(20), nullable=True, server_default="running", index=True),
sa.Column("celery_task_id", sa.String(255), nullable=True, index=True),
)
# --- test_results ---
op.create_table(
"test_results",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("run_id", sa.Integer(), sa.ForeignKey("test_runs.id"), nullable=False, index=True),
sa.Column("node_id", sa.String(500), nullable=False, index=True),
sa.Column("test_name", sa.String(200), nullable=False),
sa.Column("test_file", sa.String(300), nullable=False),
sa.Column("test_class", sa.String(200), nullable=True),
sa.Column("outcome", sa.String(20), nullable=False, index=True),
sa.Column("duration_seconds", sa.Float(), nullable=True),
sa.Column("error_message", sa.Text(), nullable=True),
sa.Column("traceback", sa.Text(), nullable=True),
sa.Column("markers", sa.JSON(), nullable=True),
sa.Column("parameters", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
# --- test_collections ---
op.create_table(
"test_collections",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("total_tests", sa.Integer(), nullable=True),
sa.Column("total_files", sa.Integer(), nullable=True),
sa.Column("total_classes", sa.Integer(), nullable=True),
sa.Column("unit_tests", sa.Integer(), nullable=True),
sa.Column("integration_tests", sa.Integer(), nullable=True),
sa.Column("performance_tests", sa.Integer(), nullable=True),
sa.Column("system_tests", sa.Integer(), nullable=True),
sa.Column("test_files", sa.JSON(), nullable=True),
sa.Column("collected_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
# --- architecture_scans ---
op.create_table(
"architecture_scans",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("timestamp", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, index=True),
sa.Column("validator_type", sa.String(20), nullable=False, server_default="architecture", index=True),
sa.Column("status", sa.String(30), nullable=False, server_default="pending", index=True),
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("error_message", sa.Text(), nullable=True),
sa.Column("progress_message", sa.String(255), nullable=True),
sa.Column("total_files", sa.Integer(), nullable=True),
sa.Column("total_violations", sa.Integer(), nullable=True),
sa.Column("errors", sa.Integer(), nullable=True),
sa.Column("warnings", sa.Integer(), nullable=True),
sa.Column("duration_seconds", sa.Float(), nullable=True),
sa.Column("triggered_by", sa.String(100), nullable=True),
sa.Column("git_commit_hash", sa.String(40), nullable=True),
sa.Column("celery_task_id", sa.String(255), nullable=True, index=True),
)
# --- architecture_violations ---
op.create_table(
"architecture_violations",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("scan_id", sa.Integer(), sa.ForeignKey("architecture_scans.id"), nullable=False, index=True),
sa.Column("validator_type", sa.String(20), nullable=False, server_default="architecture", index=True),
sa.Column("rule_id", sa.String(20), nullable=False, index=True),
sa.Column("rule_name", sa.String(200), nullable=False),
sa.Column("severity", sa.String(10), nullable=False, index=True),
sa.Column("file_path", sa.String(500), nullable=False, index=True),
sa.Column("line_number", sa.Integer(), nullable=False),
sa.Column("message", sa.Text(), nullable=False),
sa.Column("context", sa.Text(), nullable=True),
sa.Column("suggestion", sa.Text(), nullable=True),
sa.Column("status", sa.String(20), nullable=True, server_default="open", index=True),
sa.Column("assigned_to", sa.Integer(), sa.ForeignKey("users.id"), nullable=True),
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("resolved_by", sa.Integer(), sa.ForeignKey("users.id"), nullable=True),
sa.Column("resolution_note", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
# --- architecture_rules ---
op.create_table(
"architecture_rules",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("rule_id", sa.String(20), unique=True, nullable=False, index=True),
sa.Column("validator_type", sa.String(20), nullable=False, server_default="architecture", index=True),
sa.Column("category", sa.String(50), nullable=False),
sa.Column("name", sa.String(200), nullable=False),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("severity", sa.String(10), nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False, server_default="true"),
sa.Column("custom_config", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
# --- violation_assignments ---
op.create_table(
"violation_assignments",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("violation_id", sa.Integer(), sa.ForeignKey("architecture_violations.id"), nullable=False, index=True),
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=False),
sa.Column("assigned_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.Column("assigned_by", sa.Integer(), sa.ForeignKey("users.id"), nullable=True),
sa.Column("due_date", sa.DateTime(timezone=True), nullable=True),
sa.Column("priority", sa.String(10), nullable=True),
)
# --- violation_comments ---
op.create_table(
"violation_comments",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("violation_id", sa.Integer(), sa.ForeignKey("architecture_violations.id"), nullable=False, index=True),
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=False),
sa.Column("comment", sa.Text(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
def downgrade() -> None:
op.drop_table("violation_comments")
op.drop_table("violation_assignments")
op.drop_table("architecture_rules")
op.drop_table("architecture_violations")
op.drop_table("architecture_scans")
op.drop_table("test_collections")
op.drop_table("test_results")
op.drop_table("test_runs")