DB-GPT/dbgpt/app/_cli.py
2024-01-10 10:39:04 +08:00

291 lines
8.5 KiB
Python

import functools
import os
from typing import Optional
import click
from dbgpt.app.base import WebServerParameters
from dbgpt.configs.model_config import LOGDIR
from dbgpt.util.command_utils import _run_current_with_daemon, _stop_service
from dbgpt.util.parameter_utils import EnvArgumentParser
@click.command(name="webserver")
@EnvArgumentParser.create_click_option(WebServerParameters)
def start_webserver(**kwargs):
"""Start webserver(dbgpt_server.py)"""
if kwargs["daemon"]:
log_file = os.path.join(LOGDIR, "webserver_uvicorn.log")
_run_current_with_daemon("WebServer", log_file)
else:
from dbgpt.app.dbgpt_server import run_webserver
run_webserver(WebServerParameters(**kwargs))
@click.command(name="webserver")
@click.option(
"--port",
type=int,
default=None,
required=False,
help=("The port to stop"),
)
def stop_webserver(port: int):
"""Stop webserver(dbgpt_server.py)"""
_stop_service("webserver", "WebServer", port=port)
def _stop_all_dbgpt_server():
_stop_service("webserver", "WebServer")
@click.group("migration")
def migration():
"""Manage database migration"""
pass
def add_migration_options(func):
@click.option(
"--alembic_ini_path",
required=False,
type=str,
default=None,
show_default=True,
help="Alembic ini path, if not set, use 'pilot/meta_data/alembic.ini'",
)
@click.option(
"--script_location",
required=False,
type=str,
default=None,
show_default=True,
help="Alembic script location, if not set, use 'pilot/meta_data/alembic'",
)
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@migration.command()
@add_migration_options
@click.option(
"-m",
"--message",
required=False,
type=str,
default="Init migration",
show_default=True,
help="The message for create migration repository",
)
def init(alembic_ini_path: str, script_location: str, message: str):
"""Initialize database migration repository"""
from dbgpt.util._db_migration_utils import create_migration_script
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
create_migration_script(alembic_cfg, db_manager.engine, message)
@migration.command()
@add_migration_options
@click.option(
"-m",
"--message",
required=False,
type=str,
default="New migration",
show_default=True,
help="The message for migration script",
)
def migrate(alembic_ini_path: str, script_location: str, message: str):
"""Create migration script"""
from dbgpt.util._db_migration_utils import create_migration_script
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
create_migration_script(alembic_cfg, db_manager.engine, message)
@migration.command()
@add_migration_options
@click.option(
"--sql-output",
type=str,
default=None,
help="Generate SQL script for migration instead of applying it. ex: --sql-output=upgrade.sql",
)
def upgrade(alembic_ini_path: str, script_location: str, sql_output: str):
"""Upgrade database to target version"""
from dbgpt.util._db_migration_utils import (
generate_sql_for_upgrade,
upgrade_database,
)
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
if sql_output:
generate_sql_for_upgrade(alembic_cfg, db_manager.engine, output_file=sql_output)
else:
upgrade_database(alembic_cfg, db_manager.engine)
@migration.command()
@add_migration_options
@click.option(
"-y",
required=False,
type=bool,
default=False,
is_flag=True,
help="Confirm to downgrade database",
)
@click.option(
"-r",
"--revision",
default="-1",
show_default=True,
help="Revision to downgrade to",
)
def downgrade(alembic_ini_path: str, script_location: str, y: bool, revision: str):
"""Downgrade database to target version"""
from dbgpt.util._db_migration_utils import downgrade_database
if not y:
click.confirm("Are you sure you want to downgrade the database?", abort=True)
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
downgrade_database(alembic_cfg, db_manager.engine, revision)
@migration.command()
@add_migration_options
@click.option(
"--drop_all_tables",
required=False,
type=bool,
default=False,
is_flag=True,
help="Drop all tables",
)
@click.option(
"-y",
required=False,
type=bool,
default=False,
is_flag=True,
help="Confirm to clean migration data",
)
@click.option(
"--confirm_drop_all_tables",
required=False,
type=bool,
default=False,
is_flag=True,
help="Confirm to drop all tables",
)
def clean(
alembic_ini_path: str,
script_location: str,
drop_all_tables: bool,
y: bool,
confirm_drop_all_tables: bool,
):
"""Clean Alembic migration scripts and history"""
from dbgpt.util._db_migration_utils import clean_alembic_migration
if not y:
click.confirm(
"Are you sure clean alembic migration scripts and history?", abort=True
)
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
clean_alembic_migration(alembic_cfg, db_manager.engine)
if drop_all_tables:
if not confirm_drop_all_tables:
click.confirm("\nAre you sure drop all tables?", abort=True)
with db_manager.engine.connect() as connection:
for tbl in reversed(db_manager.Model.metadata.sorted_tables):
print(f"Drop table {tbl.name}")
connection.execute(tbl.delete())
@migration.command()
@add_migration_options
def list(alembic_ini_path: str, script_location: str):
"""List all versions in the migration history, marking the current one"""
from alembic.runtime.migration import MigrationContext
from alembic.script import ScriptDirectory
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
# Set up Alembic environment and script directory
script = ScriptDirectory.from_config(alembic_cfg)
# Get current revision
def get_current_revision():
with db_manager.engine.connect() as connection:
context = MigrationContext.configure(connection)
return context.get_current_revision()
current_rev = get_current_revision()
# List all revisions and mark the current one
for revision in script.walk_revisions():
current_marker = "(current)" if revision.revision == current_rev else ""
print(f"{revision.revision} {current_marker}: {revision.doc}")
@migration.command()
@add_migration_options
@click.argument("revision", required=True)
def show(alembic_ini_path: str, script_location: str, revision: str):
"""Show the migration script for a specific version."""
from alembic.script import ScriptDirectory
alembic_cfg, db_manager = _get_migration_config(alembic_ini_path, script_location)
script = ScriptDirectory.from_config(alembic_cfg)
rev = script.get_revision(revision)
if rev is None:
print(f"Revision {revision} not found.")
return
# Find the migration script file
script_files = os.listdir(os.path.join(script.dir, "versions"))
script_file = next((f for f in script_files if f.startswith(revision)), None)
if script_file is None:
print(f"Migration script for revision {revision} not found.")
return
# Print the migration script
script_file_path = os.path.join(script.dir, "versions", script_file)
print(f"Migration script for revision {revision}: {script_file_path}")
try:
with open(script_file_path, "r") as file:
print(file.read())
except FileNotFoundError:
print(f"Migration script {script_file_path} not found.")
def _get_migration_config(
alembic_ini_path: Optional[str] = None, script_location: Optional[str] = None
):
from dbgpt.app.base import _initialize_db
# Import all models to make sure they are registered with SQLAlchemy.
from dbgpt.app.initialization.db_model_initialization import _MODELS
from dbgpt.storage.metadata.db_manager import db as db_manager
from dbgpt.util._db_migration_utils import create_alembic_config
# initialize db
default_meta_data_path = _initialize_db()
alembic_cfg = create_alembic_config(
default_meta_data_path,
db_manager.engine,
db_manager.Model,
db_manager.session(),
alembic_ini_path,
script_location,
)
return alembic_cfg, db_manager