-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmigrate.py
More file actions
executable file
·175 lines (137 loc) · 4.92 KB
/
Copy pathmigrate.py
File metadata and controls
executable file
·175 lines (137 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/env python
"""
Laravel-style migration commands for Multi-Agent AI System.
Commands:
python migrate.py - Run pending migrations
python migrate.py fresh - Drop all tables and re-run migrations
python migrate.py rollback - Rollback last migration
python migrate.py status - Show migration status
python migrate.py reset - Rollback all migrations
"""
import sys
import subprocess
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from database import engine
from app.models.base import Base
# Import all models to ensure they're registered with Base
from app.models import user, conversation, conversation_message
def run_command(cmd: list, description: str):
"""Run a shell command and print output."""
print(f"\n{'='*60}")
print(f" {description}")
print(f"{'='*60}\n")
result = subprocess.run(cmd, cwd=project_root)
if result.returncode != 0:
print(f"\n❌ Command failed with exit code {result.returncode}")
sys.exit(result.returncode)
print(f"\n✅ {description} completed successfully!\n")
def migrate_fresh():
"""Drop all tables and re-run all migrations (like php artisan migrate:fresh)."""
print("\n⚠️ WARNING: This will DROP ALL TABLES and data!")
confirm = input("Are you sure? Type 'yes' to continue: ")
if confirm.lower() != 'yes':
print("❌ Aborted.")
return
# Drop all tables with CASCADE to handle foreign key dependencies
print("\n🗑️ Dropping all tables...")
try:
from sqlalchemy import text
with engine.connect() as conn:
# Drop PGVector tables first (they have foreign keys)
conn.execute(text("DROP TABLE IF EXISTS memories CASCADE"))
conn.execute(text("DROP TABLE IF EXISTS global_knowledge CASCADE"))
conn.commit()
except Exception as e:
print(f"⚠️ Note: {e}")
# Now drop all remaining tables
Base.metadata.drop_all(bind=engine)
print("✅ All tables dropped")
# Reset alembic version
try:
from sqlalchemy import text
with engine.connect() as conn:
conn.execute(text("DROP TABLE IF EXISTS alembic_version"))
conn.commit()
print("✅ Alembic version table reset")
except Exception as e:
print(f"⚠️ Note: {e}")
# Run all migrations
run_command(
["alembic", "upgrade", "head"],
"Running all migrations"
)
def migrate():
"""Run pending migrations (like php artisan migrate)."""
# Check if there are pending migrations
result = subprocess.run(
["alembic", "current"],
cwd=project_root,
capture_output=True,
text=True
)
# Check head version
head_result = subprocess.run(
["alembic", "heads"],
cwd=project_root,
capture_output=True,
text=True
)
current_version = result.stdout.strip()
head_version = head_result.stdout.strip().split()[0] if head_result.stdout.strip() else ""
# If current matches head, nothing to migrate
if current_version and head_version in current_version:
print("\n" + "="*60)
print(" ✅ Nothing to migrate")
print("="*60)
print(f"\nDatabase is already up to date at revision: {head_version}\n")
return
run_command(
["alembic", "upgrade", "head"],
"Running pending migrations"
)
def migrate_rollback():
"""Rollback last migration (like php artisan migrate:rollback)."""
run_command(
["alembic", "downgrade", "-1"],
"Rolling back last migration"
)
def migrate_reset():
"""Rollback all migrations (like php artisan migrate:reset)."""
print("\n⚠️ WARNING: This will rollback ALL migrations!")
confirm = input("Are you sure? Type 'yes' to continue: ")
if confirm.lower() != 'yes':
print("❌ Aborted.")
return
run_command(
["alembic", "downgrade", "base"],
"Rolling back all migrations"
)
def migrate_status():
"""Show migration status (like php artisan migrate:status)."""
print("\n" + "="*60)
print(" Migration Status")
print("="*60 + "\n")
subprocess.run(["alembic", "current"], cwd=project_root)
print()
subprocess.run(["alembic", "history", "--verbose"], cwd=project_root)
def show_help():
"""Show help message."""
print(__doc__)
if __name__ == "__main__":
command = sys.argv[1] if len(sys.argv) > 1 else "migrate"
commands = {
"fresh": migrate_fresh,
"migrate": migrate,
"rollback": migrate_rollback,
"reset": migrate_reset,
"status": migrate_status,
"help": show_help,
}
if command not in commands:
print(f"❌ Unknown command: {command}")
show_help()
sys.exit(1)
commands[command]()