Files
TimeTrack/migrations/old_migrations/run_code_migrations.py
Jens Luedicke 9a79778ad6 Squashed commit of the following:
commit 1eeea9f83ad9230a5c1f7a75662770eaab0df837
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 21:15:41 2025 +0200

    Disable resuming of old time entries.

commit 3e3ec2f01cb7943622b819a19179388078ae1315
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 20:59:19 2025 +0200

    Refactor db migrations.

commit 15a51a569da36c6b7c9e01ab17b6fdbdee6ad994
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 19:58:04 2025 +0200

    Apply new style for Time Tracking view.

commit 77e5278b303e060d2b03853b06277f8aa567ae68
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 18:06:04 2025 +0200

    Allow direct registrations as a Company.

commit 188a8772757cbef374243d3a5f29e4440ddecabe
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 18:04:45 2025 +0200

    Add email invitation feature.

commit d9ebaa02aa01b518960a20dccdd5a327d82f30c6
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 17:12:32 2025 +0200

    Apply common style for Company, User, Team management pages.

commit 81149caf4d8fc6317e2ab1b4f022b32fc5aa6d22
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 16:44:32 2025 +0200

    Move export functions to own module.

commit 1a26e19338e73f8849c671471dd15cc3c1b1fe82
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 15:51:15 2025 +0200

    Split up models.py.

commit 61f1ccd10f721b0ff4dc1eccf30c7a1ee13f204d
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 12:05:28 2025 +0200

    Move utility function into own modules.

commit 84b341ed35e2c5387819a8b9f9d41eca900ae79f
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 11:44:24 2025 +0200

    Refactor auth functions use.

commit 923e311e3da5b26d85845c2832b73b7b17c48adb
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 11:35:52 2025 +0200

    Refactor route nameing and fix bugs along the way.

commit f0a5c4419c340e62a2615c60b2a9de28204d2995
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 10:34:33 2025 +0200

    Fix URL endpoints in announcement template.

commit b74d74542a1c8dc350749e4788a9464d067a88b5
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 09:25:53 2025 +0200

    Move announcements to own module.

commit 9563a28021ac46c82c04fe4649b394dbf96f92c7
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 09:16:30 2025 +0200

    Combine Company view and edit templates.

commit 6687c373e681d54e4deab6b2582fed5cea9aadf6
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 08:17:42 2025 +0200

    Move Users, Company and System Administration to own modules.

commit 8b7894a2e3eb84bb059f546648b6b9536fea724e
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 07:40:57 2025 +0200

    Move Teams and Projects to own modules.

commit d11bf059d99839ecf1f5d7020b8c8c8a2454c00b
Author: Jens Luedicke <jens@luedicke.me>
Date:   Mon Jul 7 07:09:33 2025 +0200

    Move Tasks and Sprints to own modules.
2025-07-07 21:16:36 +02:00

166 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Run code migrations during startup - updates code to match model changes
"""
import os
import sys
import subprocess
from pathlib import Path
import hashlib
import json
from datetime import datetime
MIGRATION_STATE_FILE = '/data/code_migrations_state.json'
def get_migration_hash(script_path):
"""Get hash of migration script to detect changes"""
with open(script_path, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def load_migration_state():
"""Load state of previously run migrations"""
if os.path.exists(MIGRATION_STATE_FILE):
try:
with open(MIGRATION_STATE_FILE, 'r') as f:
return json.load(f)
except:
return {}
return {}
def save_migration_state(state):
"""Save migration state"""
os.makedirs(os.path.dirname(MIGRATION_STATE_FILE), exist_ok=True)
with open(MIGRATION_STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
def should_run_migration(script_path, state):
"""Check if migration should run based on state"""
script_name = os.path.basename(script_path)
current_hash = get_migration_hash(script_path)
if script_name not in state:
return True
# Re-run if script has changed
if state[script_name].get('hash') != current_hash:
return True
# Skip if already run successfully
if state[script_name].get('status') == 'success':
return False
return True
def run_migration(script_path, state):
"""Run a single migration script"""
script_name = os.path.basename(script_path)
print(f"\n{'='*60}")
print(f"Running code migration: {script_name}")
print('='*60)
try:
result = subprocess.run(
[sys.executable, script_path],
capture_output=True,
text=True,
check=True,
timeout=300 # 5 minute timeout
)
print(result.stdout)
if result.stderr:
print("Warnings:", result.stderr)
# Update state
state[script_name] = {
'hash': get_migration_hash(script_path),
'status': 'success',
'last_run': str(datetime.now()),
'output': result.stdout[-1000:] if result.stdout else '' # Last 1000 chars
}
save_migration_state(state)
return True
except subprocess.CalledProcessError as e:
print(f"❌ Error running {script_name}:")
print(e.stdout)
print(e.stderr)
# Update state with failure
state[script_name] = {
'hash': get_migration_hash(script_path),
'status': 'failed',
'last_run': str(datetime.now()),
'error': str(e)
}
save_migration_state(state)
return False
except subprocess.TimeoutExpired:
print(f"❌ Migration {script_name} timed out!")
state[script_name] = {
'hash': get_migration_hash(script_path),
'status': 'timeout',
'last_run': str(datetime.now())
}
save_migration_state(state)
return False
def main():
"""Run all code migrations that need to be run"""
print("🔄 Checking for code migrations...")
# Get migration state
state = load_migration_state()
# Get all migration scripts
migrations_dir = Path(__file__).parent
migration_scripts = sorted([
str(p) for p in migrations_dir.glob('*.py')
if p.name.startswith(('11_', '12_', '13_', '14_', '15_'))
and 'template' not in p.name.lower()
])
if not migration_scripts:
print("No code migration scripts found.")
return 0
# Check which migrations need to run
to_run = []
for script in migration_scripts:
if should_run_migration(script, state):
to_run.append(script)
if not to_run:
print("✅ All code migrations are up to date.")
return 0
print(f"\n📋 Found {len(to_run)} code migrations to run:")
for script in to_run:
print(f" - {Path(script).name}")
# Run migrations
failed = []
for script in to_run:
if not run_migration(script, state):
failed.append(script)
# Continue with other migrations even if one fails
print(f"\n⚠️ Migration {Path(script).name} failed, continuing with others...")
# Summary
print("\n" + "="*60)
if failed:
print(f"⚠️ {len(failed)} code migrations failed:")
for script in failed:
print(f" - {Path(script).name}")
print("\nThe application may not work correctly.")
print("Check the logs and fix the issues.")
# Don't exit with error - let the app start anyway
return 0
else:
print("✅ All code migrations completed successfully!")
return 0
if __name__ == "__main__":
sys.exit(main())