From f7d79eae7068aeb3c928cbf0c037e9bf36b3e97a Mon Sep 17 00:00:00 2001 From: Erik Vroon Date: Sat, 10 Feb 2024 16:36:58 +0100 Subject: [PATCH] Autorun alembics (#455) Runs alembic migration automatically on fastapi startup. Can be disabled using config setting. --- .github/workflows/backend.yml | 2 +- backend/Pipfile | 2 +- backend/alembic/__init__.py | 0 ...e04993dd7_tournaments_dashboard_endpoint_unique.py | 0 backend/bracket/app.py | 11 +++++++++++ backend/bracket/config.py | 1 + backend/cli.py | 5 +++-- backend/precommit.sh | 2 +- docs/docs/running-bracket/configuration.md | 3 +++ 9 files changed, 21 insertions(+), 5 deletions(-) delete mode 100644 backend/alembic/__init__.py rename backend/{alembics => alembic}/versions/c08e04993dd7_tournaments_dashboard_endpoint_unique.py (100%) diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 0f065fbb..e94de283 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -50,7 +50,7 @@ jobs: working-directory: backend - name: Run pylint - run: pipenv run pylint alembic bracket tests + run: pipenv run pylint bracket tests cli.py working-directory: backend - name: Run ruff format diff --git a/backend/Pipfile b/backend/Pipfile index f982f63b..1c08e7fb 100644 --- a/backend/Pipfile +++ b/backend/Pipfile @@ -6,7 +6,7 @@ name = "pypi" [packages] aiohttp = ">=3.8.3" aiopg = ">=1.4.0" -alembic = ">=1.9.1" +alembic = ">=1.13.1" bcrypt = "4.1.2" click = ">=8.1.3" databases = {extras = ["asyncpg"], version = "<=0.7.0"} diff --git a/backend/alembic/__init__.py b/backend/alembic/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/alembics/versions/c08e04993dd7_tournaments_dashboard_endpoint_unique.py b/backend/alembic/versions/c08e04993dd7_tournaments_dashboard_endpoint_unique.py similarity index 100% rename from backend/alembics/versions/c08e04993dd7_tournaments_dashboard_endpoint_unique.py rename to backend/alembic/versions/c08e04993dd7_tournaments_dashboard_endpoint_unique.py diff --git a/backend/bracket/app.py b/backend/bracket/app.py index 96de8a75..07e0c446 100644 --- a/backend/bracket/app.py +++ b/backend/bracket/app.py @@ -9,6 +9,8 @@ from starlette.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse, Response from starlette.staticfiles import StaticFiles +from alembic import command +from alembic.config import Config from bracket.config import Environment, config, environment, init_sentry from bracket.cronjobs.scheduling import start_cronjobs from bracket.database import database @@ -34,11 +36,20 @@ from bracket.utils.logging import logger init_sentry() +def run_migrations() -> None: + logger.info("Running migrations") + alembic_cfg = Config("alembic.ini") + command.upgrade(alembic_cfg, "head") + + @asynccontextmanager async def lifespan(_: FastAPI) -> AsyncIterator[None]: await database.connect() await init_db_when_empty() + if config.auto_run_migrations and environment is not Environment.CI: + run_migrations() + if environment is Environment.PRODUCTION: start_cronjobs() diff --git a/backend/bracket/config.py b/backend/bracket/config.py index 9b98bb73..1856fece 100644 --- a/backend/bracket/config.py +++ b/backend/bracket/config.py @@ -36,6 +36,7 @@ class Config(BaseSettings): cors_origin_regex: str = "" cors_origins: str = "*" jwt_secret: str + auto_run_migrations: bool = True pg_dsn: PostgresDsn = "postgresql://user:pass@localhost:5432/db" # type: ignore[assignment] sentry_dsn: str | None = None diff --git a/backend/cli.py b/backend/cli.py index a80edd2b..da6d428c 100755 --- a/backend/cli.py +++ b/backend/cli.py @@ -9,6 +9,7 @@ from bracket.config import config from bracket.database import database from bracket.logger import get_logger from bracket.utils.db_init import sql_create_dev_db +from bracket.utils.security import hash_password logger = get_logger("cli") @@ -42,7 +43,7 @@ def cli() -> None: @click.command() -def hash_password() -> None: +def hash_password_cmd() -> None: if config.admin_password is None: logger.error("No admin password is given") else: @@ -59,5 +60,5 @@ async def create_dev_db() -> None: if __name__ == "__main__": cli.add_command(create_dev_db) - cli.add_command(hash_password) + cli.add_command(hash_password_cmd) cli() diff --git a/backend/precommit.sh b/backend/precommit.sh index b20667dd..fbd7d163 100755 --- a/backend/precommit.sh +++ b/backend/precommit.sh @@ -6,4 +6,4 @@ ruff --fix . ! vulture | grep "unused function\|unused class\|unused method" dmypy run -- --follow-imports=normal --junit-xml= . ENVIRONMENT=CI pytest --cov --cov-report=xml . -vvv -pylint alembic bracket tests +pylint cli.py bracket tests diff --git a/docs/docs/running-bracket/configuration.md b/docs/docs/running-bracket/configuration.md index 60b49592..ac070e94 100644 --- a/docs/docs/running-bracket/configuration.md +++ b/docs/docs/running-bracket/configuration.md @@ -20,6 +20,8 @@ Copy `ci.env` to `prod.env` and fill in the values: used for production while bracket is still in beta - `ALLOW_INSECURE_HTTP_SSO`: Should not be used in production. Allows use of INSECURE requests for SSO auth. +- `AUTO_RUN_MIGRATIONS`: Whether to run (alembic) migrations automatically on startup or not. + Migrations can be applied manually using `pipenv run alembic upgrade head`. ### Backend: Example configuration file @@ -36,6 +38,7 @@ SENTRY_DSN='my sentry dsn' ALLOW_USER_REGISTRATION=false ALLOW_INSECURE_HTTP_SSO=false CAPTCHA_SECRET='xxx' +AUTO_RUN_MIGRATIONS=true ``` ## Frontend