|
19 | 19 | import sqlalchemy.ext.asyncio |
20 | 20 | import sqlalchemy.orm |
21 | 21 | from alembic.runtime.migration import MigrationContext |
| 22 | +from alembic.script.base import Script |
22 | 23 |
|
23 | 24 | from reflex.base import Base |
24 | 25 | from reflex.config import get_config |
|
34 | 35 | from sqlmodel.ext.asyncio.session import AsyncSession # noqa: E402 |
35 | 36 |
|
36 | 37 |
|
| 38 | +def format_revision( |
| 39 | + rev: Script, |
| 40 | + current_rev: str | None, |
| 41 | + current_reached_ref: list[bool], |
| 42 | +) -> str: |
| 43 | + """Format a single revision for display. |
| 44 | +
|
| 45 | + Args: |
| 46 | + rev: The alembic script object |
| 47 | + current_rev: The currently applied revision ID |
| 48 | + current_reached_ref: Mutable reference to track if we've reached current revision |
| 49 | +
|
| 50 | + Returns: |
| 51 | + Formatted string for display |
| 52 | + """ |
| 53 | + current = rev.revision |
| 54 | + message = rev.doc |
| 55 | + |
| 56 | + # Determine if this migration is applied |
| 57 | + if current_rev is None: |
| 58 | + is_applied = False |
| 59 | + elif current == current_rev: |
| 60 | + is_applied = True |
| 61 | + current_reached_ref[0] = True |
| 62 | + else: |
| 63 | + is_applied = not current_reached_ref[0] |
| 64 | + |
| 65 | + # Show checkmark or X with colors |
| 66 | + status_icon = "[green]✓[/green]" if is_applied else "[red]✗[/red]" |
| 67 | + head_marker = " (head)" if rev.is_head else "" |
| 68 | + |
| 69 | + # Format output with message |
| 70 | + return f" [{status_icon}] {current}{head_marker}, {message}" |
| 71 | + |
| 72 | + |
37 | 73 | def _safe_db_url_for_logging(url: str) -> str: |
38 | 74 | """Remove username and password from the database URL for logging. |
39 | 75 |
|
@@ -361,6 +397,25 @@ def alembic_init(cls): |
361 | 397 | directory=str(environment.ALEMBIC_CONFIG.get().parent / "alembic"), |
362 | 398 | ) |
363 | 399 |
|
| 400 | + @classmethod |
| 401 | + def get_migration_history(cls): |
| 402 | + """Get migration history with current database state. |
| 403 | +
|
| 404 | + Returns: |
| 405 | + tuple: (current_revision, revisions_list) where revisions_list is in chronological order |
| 406 | + """ |
| 407 | + # Get current revision from database |
| 408 | + with cls.get_db_engine().connect() as connection: |
| 409 | + context = MigrationContext.configure(connection) |
| 410 | + current_rev = context.get_current_revision() |
| 411 | + |
| 412 | + # Get all revisions from base to head |
| 413 | + _, script_dir = cls._alembic_config() |
| 414 | + revisions = list(script_dir.walk_revisions()) |
| 415 | + revisions.reverse() # Reverse to get chronological order (base first) |
| 416 | + |
| 417 | + return current_rev, revisions |
| 418 | + |
364 | 419 | @classmethod |
365 | 420 | def alembic_autogenerate( |
366 | 421 | cls, |
|
0 commit comments