Browse Source

fix: migration remote_instance_id via sqlite3 direct avant import app

app.py schedule les jobs au démarrage (Job.query) — si la colonne
n'existe pas encore en DB, l'import explose. On lit DB_PATH depuis
le fichier de config et on fait l'ALTER TABLE via sqlite3 brut
avant tout import de SQLAlchemy/app.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cédric Hansen 1 month ago
parent
commit
6f199a1aa4
1 changed files with 32 additions and 20 deletions
  1. 32 20
      sources/init_db.py

+ 32 - 20
sources/init_db.py

@@ -1,31 +1,43 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
 """Initialise (ou migre) la base de données SQLite. Appelé par les scripts install et upgrade."""
 """Initialise (ou migre) la base de données SQLite. Appelé par les scripts install et upgrade."""
 import os
 import os
+import sqlite3
 import sys
 import sys
 
 
-if len(sys.argv) > 1:
-    os.environ["BACKUPMANAGER_CONFIG"] = sys.argv[1]
+config_path = sys.argv[1] if len(sys.argv) > 1 else None
+if config_path:
+    os.environ["BACKUPMANAGER_CONFIG"] = config_path
 
 
+# Lire DB_PATH directement depuis le fichier de config (sans importer app/SQLAlchemy)
+# pour pouvoir migrer le schéma AVANT que l'import de app ne tente de requêter la DB.
+_cfg = {}
+if config_path and os.path.exists(config_path):
+    with open(config_path) as _f:
+        exec(compile(_f.read(), config_path, "exec"), _cfg)
+db_path = _cfg.get("DB_PATH") or os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), "backupmanager.db"
+)
+
+# Migrations SQLite directes — avant tout import de SQLAlchemy/app
+if os.path.exists(db_path):
+    _conn = sqlite3.connect(db_path)
+    _cur = _conn.execute("PRAGMA table_info(jobs)")
+    existing_cols = {row[1] for row in _cur.fetchall()}
+
+    migrations = [
+        ("destination_id",    "ALTER TABLE jobs ADD COLUMN destination_id INTEGER REFERENCES destinations(id)"),
+        ("remote_instance_id","ALTER TABLE jobs ADD COLUMN remote_instance_id INTEGER REFERENCES remote_instances(id)"),
+    ]
+    for col, sql in migrations:
+        if col not in existing_cols:
+            _conn.execute(sql)
+            _conn.commit()
+            print(f"Migration : colonne {col} ajoutée à jobs.")
+    _conn.close()
+
+# Import de app après les migrations — SQLAlchemy peut désormais requêter la DB
 from app import app, db
 from app import app, db
-from sqlalchemy import text
 
 
 with app.app_context():
 with app.app_context():
     db.create_all()
     db.create_all()
-
-    # Migrations manuelles pour les colonnes ajoutées après la création initiale
-    with db.engine.connect() as conn:
-        existing = [row[1] for row in conn.execute(text("PRAGMA table_info(jobs)"))]
-        if "destination_id" not in existing:
-            conn.execute(text(
-                "ALTER TABLE jobs ADD COLUMN destination_id INTEGER REFERENCES destinations(id)"
-            ))
-            conn.commit()
-            print("Migration : colonne destination_id ajoutée à jobs.")
-        if "remote_instance_id" not in existing:
-            conn.execute(text(
-                "ALTER TABLE jobs ADD COLUMN remote_instance_id INTEGER REFERENCES remote_instances(id)"
-            ))
-            conn.commit()
-            print("Migration : colonne remote_instance_id ajoutée à jobs.")
-
     print("Base de données initialisée.")
     print("Base de données initialisée.")