1818from rich .table import Table
1919
2020from infrahub import config
21+ from infrahub .auth import AccountSession , AuthType
22+ from infrahub .context import InfrahubContext
2123from infrahub .core import registry
24+ from infrahub .core .branch import Branch
25+ from infrahub .core .branch .tasks import rebase_branch
26+ from infrahub .core .constants import GLOBAL_BRANCH_NAME
2227from infrahub .core .graph import GRAPH_VERSION
2328from infrahub .core .graph .constraints import ConstraintManagerBase , ConstraintManagerMemgraph , ConstraintManagerNeo4j
2429from infrahub .core .graph .index import node_indexes , rel_indexes
3035 GraphRelationshipIsPartOf ,
3136 GraphRelationshipProperties ,
3237)
33- from infrahub .core .initialization import (
34- get_root_node ,
35- initialize_registry ,
36- )
38+ from infrahub .core .initialization import get_root_node , initialize_registry
39+ from infrahub .core .migrations .exceptions import MigrationFailureError
3740from infrahub .core .migrations .graph import get_graph_migrations , get_migration_by_number
3841from infrahub .core .migrations .schema .models import SchemaApplyMigrationData
3942from infrahub .core .migrations .schema .tasks import schema_apply_migrations
4548from infrahub .database import DatabaseType
4649from infrahub .database .memgraph import IndexManagerMemgraph
4750from infrahub .database .neo4j import IndexManagerNeo4j
51+ from infrahub .exceptions import ValidationError
4852
4953from .constants import ERROR_BADGE , FAILED_BADGE , SUCCESS_BADGE
5054from .db_commands .check_inheritance import check_inheritance
@@ -59,7 +63,7 @@ def get_timestamp_string() -> str:
5963
6064if TYPE_CHECKING :
6165 from infrahub .cli .context import CliContext
62- from infrahub .core .migrations .shared import ArbitraryMigration , GraphMigration , InternalSchemaMigration
66+ from infrahub .core .migrations .shared import MigrationTypes
6367 from infrahub .database import InfrahubDatabase
6468 from infrahub .database .index import IndexManagerBase
6569
@@ -105,7 +109,15 @@ async def migrate_cmd(
105109 context : CliContext = ctx .obj
106110 dbdriver = await context .init_db (retry = 1 )
107111
108- await migrate_database (db = dbdriver , initialize = True , check = check , migration_number = migration_number )
112+ root_node = await get_root_node (db = dbdriver )
113+ migrations = await detect_migration_to_run (
114+ current_graph_version = root_node .graph_version , migration_number = migration_number
115+ )
116+
117+ if check or not migrations :
118+ return
119+
120+ await migrate_database (db = dbdriver , migrations = migrations , initialize = True )
109121
110122 await dbdriver .close ()
111123
@@ -268,49 +280,55 @@ async def index(
268280 await dbdriver .close ()
269281
270282
283+ async def detect_migration_to_run (
284+ current_graph_version : int , migration_number : int | str | None = None
285+ ) -> Sequence [MigrationTypes ]:
286+ """Return a sequence of migrations to apply to upgrade the database."""
287+ rprint ("Checking current state of the database" )
288+ migrations : list [MigrationTypes ] = []
289+
290+ if migration_number :
291+ migration = get_migration_by_number (migration_number )
292+ migrations .append (migration )
293+ if current_graph_version > migration .minimum_version :
294+ rprint (
295+ f"Migration { migration_number } already applied. To apply again, run the command without the --check flag."
296+ )
297+ return []
298+ rprint (
299+ f"Migration { migration_number } needs to be applied. Run `infrahub db migrate` to apply all outstanding migrations."
300+ )
301+ else :
302+ migrations .extend (await get_graph_migrations (current_graph_version = current_graph_version ))
303+ if not migrations :
304+ rprint (f"Database up-to-date (v{ current_graph_version } ), no migration to execute." )
305+ return []
306+
307+ rprint (
308+ f"Database needs to be updated (v{ current_graph_version } -> v{ GRAPH_VERSION } ), { len (migrations )} migrations pending"
309+ )
310+ return migrations
311+
312+
271313async def migrate_database (
272- db : InfrahubDatabase , initialize : bool = False , check : bool = False , migration_number : int | str | None = None
314+ db : InfrahubDatabase , migrations : Sequence [ MigrationTypes ], initialize : bool = False
273315) -> bool :
274316 """Apply the latest migrations to the database, this function will print the status directly in the console.
275317
276318 Returns a boolean indicating whether a migration failed or if all migrations succeeded.
277319
278320 Args:
279321 db: The database object.
280- check: If True, the function will only check the status of the database and not apply the migrations. Defaults to False .
281- migration_number: If provided, the function will only apply the migration with the given number. Defaults to None .
322+ migrations: Sequence of migrations to apply .
323+ initialize: Whether to initialize the registry before running migrations .
282324 """
283- rprint ("Checking current state of the Database" )
325+ if not migrations :
326+ return True
284327
285328 if initialize :
286329 await initialize_registry (db = db )
287330
288331 root_node = await get_root_node (db = db )
289- if migration_number :
290- migration = get_migration_by_number (migration_number )
291- migrations : Sequence [GraphMigration | InternalSchemaMigration | ArbitraryMigration ] = [migration ]
292- if check :
293- if root_node .graph_version > migration .minimum_version :
294- rprint (
295- f"Migration { migration_number } already applied. To apply again, run the command without the --check flag."
296- )
297- return True
298- rprint (
299- f"Migration { migration_number } needs to be applied. Run `infrahub db migrate` to apply all outstanding migrations."
300- )
301- return False
302- else :
303- migrations = await get_graph_migrations (root = root_node )
304- if not migrations :
305- rprint (f"Database up-to-date (v{ root_node .graph_version } ), no migration to execute." )
306- return True
307-
308- rprint (
309- f"Database needs to be updated (v{ root_node .graph_version } -> v{ GRAPH_VERSION } ), { len (migrations )} migrations pending"
310- )
311-
312- if check :
313- return True
314332
315333 for migration in migrations :
316334 execution_result = await migration .execute (db = db )
@@ -335,6 +353,36 @@ async def migrate_database(
335353 return True
336354
337355
356+ async def trigger_rebase_branches (db : InfrahubDatabase ) -> None :
357+ """Trigger rebase of non-default branches, also triggering migrations in the process."""
358+ branches = [b for b in await Branch .get_list (db = db ) if b .name not in [registry .default_branch , GLOBAL_BRANCH_NAME ]]
359+ if not branches :
360+ return
361+
362+ rprint (f"Planning rebase and migrations for { len (branches )} branches: { ', ' .join ([b .name for b in branches ])} " )
363+
364+ for branch in branches :
365+ if branch .graph_version == GRAPH_VERSION :
366+ rprint (
367+ f"Ignoring branch rebase and migrations for '{ branch .name } ' (ID: { branch .uuid } ), it is already up-to-date"
368+ )
369+ continue
370+
371+ rprint (f"Rebasing branch '{ branch .name } ' (ID: { branch .uuid } )..." , end = "" )
372+ try :
373+ await registry .schema .load_schema (db = db , branch = branch )
374+ await rebase_branch (
375+ branch = branch .name ,
376+ context = InfrahubContext .init (
377+ branch = branch , account = AccountSession (auth_type = AuthType .NONE , authenticated = False , account_id = "" )
378+ ),
379+ send_events = False ,
380+ )
381+ rprint (SUCCESS_BADGE )
382+ except (ValidationError , MigrationFailureError ):
383+ rprint (FAILED_BADGE )
384+
385+
338386async def initialize_internal_schema () -> None :
339387 registry .schema = SchemaManager ()
340388 schema = SchemaRoot (** internal_schema )
0 commit comments