Skip to content

Commit

Permalink
upgrade-modules-db: skip existing modules
Browse files Browse the repository at this point in the history
Modules without up-to-date or partially up-to-date
Alembic branch, but already in gn_commons.t_modules table
are skipped. There are probably legacy modules migrating
to Alembic and requiring a manual stamp.
  • Loading branch information
bouttier committed Jan 18, 2023
1 parent 194440b commit aedd240
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 9 deletions.
14 changes: 11 additions & 3 deletions backend/geonature/core/command/create_gn_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ def install_gn_module(x_arg, module_path, module_code, build, upgrade_db):
click.secho("Rebuild du frontend terminé.", fg="green")

if upgrade_db:
click.echo("Installation de la base de données…")
module_db_upgrade(module_dist, x_arg=x_arg)
click.echo("Installation / mise à jour de la base de données…")
if not module_db_upgrade(module_dist, x_arg=x_arg):
click.echo(
"Le module est déjà déclaré en base. "
"Installation de la base de données ignorée."
)


@main.command()
Expand Down Expand Up @@ -100,4 +104,8 @@ def upgrade_modules_db(directory, sql, tag, x_arg, module_codes):
continue
click.echo(f"Mise-à-jour du module {module_code}…")
module_dist = module_code_entry.dist
module_db_upgrade(module_dist, directory, sql, tag, x_arg)
if not module_db_upgrade(module_dist, directory, sql, tag, x_arg):
click.echo(
"Le module est déjà déclaré en base. "
"Installation de la base de données ignorée."
)
58 changes: 52 additions & 6 deletions backend/geonature/utils/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from pathlib import Path
from pkg_resources import load_entry_point, get_entry_info, iter_entry_points

from alembic.script import ScriptDirectory
from alembic.migration import MigrationContext
from flask import current_app
from flask_migrate import upgrade as db_upgrade

from geonature.utils.utilstoml import load_and_validate_toml
Expand Down Expand Up @@ -42,8 +45,47 @@ def get_dist_from_code(module_code):
raise Exception(f"Module with code {module_code} not installed in venv")


def iterate_revisions(script, base_revision):
"""
Iterate revisions without following depends_on directive.
Useful to find all revisions of a given branch.
"""
yelded = set()
todo = {base_revision}
while todo:
rev = todo.pop()
yield rev
yelded.add(rev)
rev = script.get_revision(rev)
todo |= rev.nextrev - yelded


def alembic_branch_in_use(branch_name, directory, x_arg):
"""
Return true if at least one revision of the given branch is applied.
"""
db = current_app.extensions["sqlalchemy"].db
migrate = current_app.extensions["migrate"].migrate
config = migrate.get_config(directory, x_arg)
script = ScriptDirectory.from_config(config)
base_revision = script.get_revision(script.as_revision_number(branch_name))
branch_revisions = set(iterate_revisions(script, base_revision.revision))
migration_context = MigrationContext.configure(db.session.connection())
current_heads = migration_context.get_current_heads()
# get_current_heads does not return implicit revision through dependencies, get_all_current does
current_heads = set(map(lambda rev: rev.revision, script.get_all_current(current_heads)))
return not branch_revisions.isdisjoint(current_heads)


def module_db_upgrade(module_dist, directory=None, sql=False, tag=None, x_arg=[]):
module_code = module_dist.load_entry_point("gn_module", "code")
if "migrations" in module_dist.get_entry_map("gn_module"):
try:
alembic_branch = module_dist.load_entry_point("gn_module", "alembic_branch")
except ImportError:
alembic_branch = module_code.lower()
else:
alembic_branch = None
module = TModules.query.filter_by(module_code=module_code).one_or_none()
if module is None:
# add module to database
Expand All @@ -68,11 +110,15 @@ def module_db_upgrade(module_dist, directory=None, sql=False, tag=None, x_arg=[]
)
db.session.add(module)
db.session.commit()

if "migrations" in module_dist.get_entry_map("gn_module"):
try:
alembic_branch = module_dist.load_entry_point("gn_module", "alembic_branch")
except ImportError:
alembic_branch = module_code.lower()
elif alembic_branch and not alembic_branch_in_use(alembic_branch, directory, x_arg):
"""
The module branch is not known to be applied by Alembic,
but the module is present in gn_commons.t_modules table.
Refusing to upgrade the Alembic branch.
Upgrading of old module requiring manual stamp?
"""
return False
if alembic_branch:
revision = alembic_branch + "@head"
db_upgrade(directory, revision, sql, tag, x_arg)
return True

0 comments on commit aedd240

Please sign in to comment.