Browse Source

Add option to run plugin migrations for the default plugins

Peter Justin 7 years ago
parent
commit
4ab2b792a9
4 changed files with 74 additions and 23 deletions
  1. 5 0
      flaskbb/app.py
  2. 28 20
      flaskbb/cli/main.py
  3. 9 0
      flaskbb/plugins/manager.py
  4. 32 3
      flaskbb/utils/populate.py

+ 5 - 0
flaskbb/app.py

@@ -58,6 +58,7 @@ from flaskbb.utils.search import (ForumWhoosheer, PostWhoosheer,
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.translations import FlaskBBDomain
 from flaskbb.utils.translations import FlaskBBDomain
 
 
+
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
@@ -424,6 +425,10 @@ def load_plugins(app):
     except (OperationalError, ProgrammingError) as exc:
     except (OperationalError, ProgrammingError) as exc:
         logger.debug("Database is not setup correctly or has not been "
         logger.debug("Database is not setup correctly or has not been "
                      "setup yet.", exc_info=exc)
                      "setup yet.", exc_info=exc)
+        # load plugins even though the database isn't setup correctly
+        # i.e. when creating the initial database and wanting to install
+        # the plugins migration as well
+        app.pluggy.load_setuptools_entrypoints('flaskbb_plugins')
         return
         return
 
 
     for plugin in plugins:
     for plugin in plugins:

+ 28 - 20
flaskbb/cli/main.py

@@ -8,35 +8,35 @@
     :copyright: (c) 2016 by the FlaskBB Team.
     :copyright: (c) 2016 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
     :license: BSD, see LICENSE for more details.
 """
 """
-import sys
+import binascii
+import logging
 import os
 import os
+import sys
 import time
 import time
-import requests
-import binascii
 import traceback
 import traceback
-import logging
 from datetime import datetime
 from datetime import datetime
 
 
 import click
 import click
 import click_log
 import click_log
+import requests
 from celery.bin.celery import CeleryCommand
 from celery.bin.celery import CeleryCommand
-from werkzeug.utils import import_string
-from jinja2 import Environment, FileSystemLoader
 from flask import current_app
 from flask import current_app
 from flask.cli import FlaskGroup, ScriptInfo, with_appcontext
 from flask.cli import FlaskGroup, ScriptInfo, with_appcontext
-from sqlalchemy_utils.functions import database_exists
 from flask_alembic import alembic_click
 from flask_alembic import alembic_click
+from jinja2 import Environment, FileSystemLoader
+from sqlalchemy_utils.functions import database_exists
+from werkzeug.utils import import_string
 
 
 from flaskbb import create_app
 from flaskbb import create_app
-from flaskbb.extensions import db, whooshee, celery, alembic
-from flaskbb.cli.utils import (prompt_save_user, prompt_config_path,
-                               write_config, get_version, FlaskBBCLIError,
-                               EmailType)
-from flaskbb.utils.populate import (create_test_data, create_welcome_forum,
-                                    create_default_groups,
-                                    create_default_settings, insert_bulk_data,
-                                    update_settings_from_fixture,
-                                    create_latest_db)
+from flaskbb.cli.utils import (EmailType, FlaskBBCLIError, get_version,
+                               prompt_config_path, prompt_save_user,
+                               write_config)
+from flaskbb.extensions import alembic, celery, db, whooshee
+from flaskbb.utils.populate import (create_default_groups,
+                                    create_default_settings, create_latest_db,
+                                    create_test_data, create_welcome_forum,
+                                    insert_bulk_data, run_plugin_migrations,
+                                    update_settings_from_fixture)
 from flaskbb.utils.translations import compile_translations
 from flaskbb.utils.translations import compile_translations
 
 
 
 
@@ -125,8 +125,10 @@ flaskbb.add_command(alembic_click, "db")
 @click.option("--email", "-e", type=EmailType(),
 @click.option("--email", "-e", type=EmailType(),
               help="The email address of the user.")
               help="The email address of the user.")
 @click.option("--password", "-p", help="The password of the user.")
 @click.option("--password", "-p", help="The password of the user.")
+@click.option("--no-plugins", "-n", default=False, is_flag=True,
+              help="Don't run the migrations for the default plugins.")
 @with_appcontext
 @with_appcontext
-def install(welcome, force, username, email, password):
+def install(welcome, force, username, email, password, no_plugins):
     """Installs flaskbb. If no arguments are used, an interactive setup
     """Installs flaskbb. If no arguments are used, an interactive setup
     will be run.
     will be run.
     """
     """
@@ -154,6 +156,10 @@ def install(welcome, force, username, email, password):
         click.secho("[+] Creating welcome forum...", fg="cyan")
         click.secho("[+] Creating welcome forum...", fg="cyan")
         create_welcome_forum()
         create_welcome_forum()
 
 
+    if not no_plugins:
+        click.secho("[+] Installing default plugins...", fg="cyan")
+        run_plugin_migrations()
+
     click.secho("[+] Compiling translations...", fg="cyan")
     click.secho("[+] Compiling translations...", fg="cyan")
     compile_translations()
     compile_translations()
 
 
@@ -187,6 +193,7 @@ def populate(bulk_data, test_data, posts, topics, force, initdb):
     if initdb:
     if initdb:
         click.secho("[+] Initializing database...", fg="cyan")
         click.secho("[+] Initializing database...", fg="cyan")
         create_latest_db()
         create_latest_db()
+        run_plugin_migrations()
 
 
     if test_data:
     if test_data:
         click.secho("[+] Adding some test data...", fg="cyan")
         click.secho("[+] Adding some test data...", fg="cyan")
@@ -243,9 +250,10 @@ def upgrade(all_latest, fixture, force):
         count = update_settings_from_fixture(
         count = update_settings_from_fixture(
             fixture=settings, overwrite_group=force, overwrite_setting=force
             fixture=settings, overwrite_group=force, overwrite_setting=force
         )
         )
-        click.secho("[+] {settings} settings in {groups} setting groups updated.".format(
-            groups=len(count), settings=sum(len(settings) for settings in count.values())), fg="green"
-        )
+        click.secho("[+] {settings} settings in {groups} setting groups "
+                    "updated.".format(groups=len(count), settings=sum(
+                        len(settings) for settings in count.values())
+                    ), fg="green")
 
 
 
 
 @flaskbb.command("download-emojis")
 @flaskbb.command("download-emojis")

+ 9 - 0
flaskbb/plugins/manager.py

@@ -16,6 +16,7 @@ from pkg_resources import (DistributionNotFound, VersionConflict,
 
 
 from flaskbb.utils.helpers import parse_pkg_metadata
 from flaskbb.utils.helpers import parse_pkg_metadata
 
 
+
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
@@ -149,3 +150,11 @@ class FlaskBBPluginManager(pluggy.PluginManager):
     def get_disabled_plugins(self):
     def get_disabled_plugins(self):
         """Returns a list with disabled plugins."""
         """Returns a list with disabled plugins."""
         return self._disabled_plugins.keys()
         return self._disabled_plugins.keys()
+
+    def get_internal_plugins(self):
+        """Returns a set of registered internal plugins."""
+        return set(self._internal_name2plugin.values())
+
+    def get_external_plugins(self):
+        """Returns a set of registered external plugins."""
+        return set(self.get_plugins() - self.get_internal_plugins())

+ 32 - 3
flaskbb/utils/populate.py

@@ -12,12 +12,17 @@ from __future__ import unicode_literals
 
 
 import collections
 import collections
 import logging
 import logging
+import os
+
+from flask import current_app
+from sqlalchemy_utils.functions import create_database, database_exists
+from alembic.util.exc import CommandError
 
 
 from flaskbb.extensions import alembic, db
 from flaskbb.extensions import alembic, db
 from flaskbb.forum.models import Category, Forum, Post, Topic
 from flaskbb.forum.models import Category, Forum, Post, Topic
 from flaskbb.management.models import Setting, SettingsGroup
 from flaskbb.management.models import Setting, SettingsGroup
 from flaskbb.user.models import Group, User
 from flaskbb.user.models import Group, User
-from sqlalchemy_utils.functions import create_database, database_exists
+
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -382,14 +387,38 @@ def insert_bulk_data(topic_count=10, post_count=100):
     return created_topics, created_posts
     return created_topics, created_posts
 
 
 
 
-def create_latest_db():
+def create_latest_db(target="default@head"):
     """Creates the database including the schema using SQLAlchemy's
     """Creates the database including the schema using SQLAlchemy's
     db.create_all method instead of going through all the database revisions.
     db.create_all method instead of going through all the database revisions.
     The revision will be set to 'head' which indicates the latest alembic
     The revision will be set to 'head' which indicates the latest alembic
     revision.
     revision.
+
+    :param target: The target branch. Defaults to 'default@head'.
     """
     """
     if not database_exists(db.engine.url):
     if not database_exists(db.engine.url):
         create_database(db.engine.url)
         create_database(db.engine.url)
 
 
     db.create_all()
     db.create_all()
-    alembic.stamp()
+    alembic.stamp(target=target)
+
+
+def run_plugin_migrations(plugins=None):
+    """Runs the migrations for a list of plugins.
+
+    :param plugins: A iterable of plugins to run the migrations for. If set
+                    to ``None``, all external plugin migrations will be run.
+    """
+    if plugins is None:
+        plugins = current_app.pluggy.get_external_plugins()
+
+    for plugin in plugins:
+        plugin_name = current_app.pluggy.get_name(plugin)
+        if not os.path.exists(os.path.join(plugin.__path__[0], "migrations")):
+            logger.debug("No migrations found for plugin %s" % plugin_name)
+            continue
+        try:
+            alembic.upgrade(target="{}@head".format(plugin_name))
+        except CommandError as exc:
+            logger.debug("Couldn't run migrations for plugin {} because of "
+                         "following exception: ".format(plugin_name),
+                         exc_info=exc)