Files
VoiceStudio/backend/migrations/env.py
T
Palash Debnath 5cab8e0149 feat: rename the product to VoiceStudio (previously OmniVoice-Studio)
Renames what users see. The app, the installers, the window title, the
docs and all 21 locales now say VoiceStudio, with "(previously
OmniVoice-Studio)" noted near the title of each doc surface so people
recognise it.

Deliberately NOT renamed, because renaming any of them silently breaks
an existing install — there is no legacy-path fallback anywhere in this
codebase:

  - bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode,
    macOS TCC grants, managed venv, WebView localStorage, the
    single-instance lock)
  - data directories OmniVoice / .omnivoice and omnivoice.db
  - the ~150 OMNIVOICE_* environment variables
  - the X-OmniVoice-* HTTP headers (a wire protocol)
  - the published Docker image paths
  - the OmniVoice ENGINE, which is a model name and not this product

tests/test_identity_paths_survive_the_rename.py pins every one of those
so a future well-meaning sweep cannot orphan a user's library.

Linux .deb users install a new package name and should apt remove
omnivoice-studio; that note is in the changelog.
2026-08-07 01:30:58 +05:30

83 lines
3.5 KiB
Python

"""Alembic environment for VoiceStudio.
DB URL is computed from `core.config.DB_PATH` at runtime so Alembic honours
the same `OMNIVOICE_DATA_DIR` override the app does. We use SQLite, so both
offline (SQL-scripted) and online (live-connection) paths are supported.
We do NOT use SQLAlchemy models — the schema lives in `core/db.py`'s
`_BASE_SCHEMA`. Migrations are hand-written using raw `op.execute(...)`
or the typed helpers (`op.add_column`, etc.). No autogeneration.
"""
from logging.config import fileConfig
from alembic import context
from sqlalchemy import engine_from_config, pool
from core.config import DB_PATH # noqa: E402 — backend/ is on sys.path via alembic.ini
config = context.config
if config.config_file_name is not None and config.attributes.get("configure_logger", True):
# `disable_existing_loggers=False` is deliberate: this env runs *inside* the
# live app (startup `alembic upgrade head`), so the default (True) would
# disable every already-created application logger — e.g. silence
# `omnivoice.db.backup`'s "Skipping pre-migration DB backup" line and the
# rest of the app's logging for the remainder of the process. A migration
# must never mute the app (or leak that mute across a test session).
#
# The `configure_logger` attribute gate exists for the same reason (#1174):
# even with disable_existing_loggers=False, fileConfig() REPLACES the root
# logger's handlers with alembic.ini's console handler and applies its
# `[logger_root] level=WARN` — so any boot that actually ran migrations
# (every FIRST RUN, every upgrade) lost the rolling omnivoice.log file
# handler and every subsequent INFO line for the rest of the process,
# including the entire graceful-shutdown trace: a SIGTERM'd clean quit
# looked like a silent crash. The in-app runner (core/db.py
# `_run_alembic_upgrade`) sets configure_logger=False; the standalone
# `alembic` CLI doesn't, and keeps this logging config.
fileConfig(config.config_file_name, disable_existing_loggers=False)
# SQLite file URL. Honour an externally-set URL (tests pass one via
# `cfg.set_main_option("sqlalchemy.url", ...)` to point at a fixture DB),
# otherwise resolve from `core.config.DB_PATH` so production runs respect
# the `OMNIVOICE_DATA_DIR` override.
if not config.get_main_option("sqlalchemy.url"):
config.set_main_option("sqlalchemy.url", f"sqlite:///{DB_PATH}")
target_metadata = None # no SQLAlchemy models — hand-written migrations only.
def run_migrations_offline() -> None:
"""Emit SQL to stdout without connecting to the DB."""
context.configure(
url=config.get_main_option("sqlalchemy.url"),
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
render_as_batch=True, # SQLite-safe ALTER
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Apply migrations against a live SQLite connection."""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True, # SQLite-safe ALTER
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()