fix: move Alembic to package submodule for Docker compatibility

- Move alembic/ directory to nextcloud_mcp_server/alembic/ subpackage
- Update migrations.py to use package location instead of alembic.ini
- Update env.py to set script_location dynamically
- Update alembic.ini for development CLI usage
- Fix Dockerfile typo: .vnev -> .venv

This fixes FileNotFoundError when running in Docker with non-editable
install. The alembic package is now installed with the main package,
making it work in both development and production environments.

Resolves: Docker startup error 'alembic.ini not found at
/app/.venv/lib/python3.12/site-packages/alembic.ini'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2025-12-18 00:42:59 +01:00
parent c4b3df04a0
commit 54b69f0d68
7 changed files with 30 additions and 19 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ RUN uv sync --locked --no-dev --no-editable --no-cache
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/app/.venv ENV VIRTUAL_ENV=/app/.venv
ENV PATH=/app/.vnev/bin:$PATH ENV PATH=/app/.venv/bin:$PATH
ENV TESSDATA_PREFIX=/usr/share/tesseract-ocr/5/tessdata ENV TESSDATA_PREFIX=/usr/share/tesseract-ocr/5/tessdata
ENTRYPOINT ["/app/.venv/bin/nextcloud-mcp-server", "run", "--host", "0.0.0.0"] ENTRYPOINT ["/app/.venv/bin/nextcloud-mcp-server", "run", "--host", "0.0.0.0"]
+2 -2
View File
@@ -2,7 +2,7 @@
[alembic] [alembic]
# Path to migration scripts # Path to migration scripts
script_location = alembic script_location = nextcloud_mcp_server/alembic
# Template used to generate migration file names # Template used to generate migration file names
# Default: %%(rev)s_%%(slug)s # Default: %%(rev)s_%%(slug)s
@@ -26,7 +26,7 @@ timezone = utc
# Version location specification # Version location specification
# Supports single or multiple directories # Supports single or multiple directories
version_locations = alembic/versions version_locations = nextcloud_mcp_server/alembic/versions
# Path separator for version locations (required to suppress deprecation warning) # Path separator for version locations (required to suppress deprecation warning)
# Use os (for cross-platform compatibility) # Use os (for cross-platform compatibility)
@@ -23,6 +23,11 @@ logger = logging.getLogger("alembic.env")
# access to the values within the .ini file in use. # access to the values within the .ini file in use.
config = context.config config = context.config
# Update script location to point to package location
# This allows alembic to find migrations when installed in site-packages
script_location = Path(__file__).parent
config.set_main_option("script_location", str(script_location))
# We don't use SQLAlchemy models, so target_metadata is None # We don't use SQLAlchemy models, so target_metadata is None
# Migrations will be written manually using op.execute() for raw SQL # Migrations will be written manually using op.execute() for raw SQL
target_metadata = None target_metadata = None
+22 -16
View File
@@ -19,33 +19,39 @@ def get_alembic_config(database_path: str | Path | None = None) -> Config:
""" """
Get Alembic configuration for programmatic use. Get Alembic configuration for programmatic use.
Works in both development and installed (Docker) modes by using
package location instead of alembic.ini file.
Args: Args:
database_path: Path to SQLite database file. If None, uses default database_path: Path to SQLite database file. If None, uses default
from alembic.ini (/app/data/tokens.db) (/app/data/tokens.db for Docker)
Returns: Returns:
Alembic Config object configured for the specified database Alembic Config object configured for the specified database
""" """
# Get path to alembic.ini (in project root) from nextcloud_mcp_server import alembic as alembic_package
project_root = Path(__file__).parent.parent
alembic_ini_path = project_root / "alembic.ini"
if not alembic_ini_path.exists(): # Use package location (works in both editable and installed modes)
raise FileNotFoundError( if alembic_package.__file__ is None:
f"alembic.ini not found at {alembic_ini_path}. " raise RuntimeError("alembic package __file__ is None")
"Ensure Alembic is properly initialized." script_location = Path(alembic_package.__file__).parent
)
# Create Alembic config # Create config programmatically (no alembic.ini needed at runtime)
config = Config(str(alembic_ini_path)) config = Config()
config.set_main_option("script_location", str(script_location))
config.set_main_option("path_separator", "os") # Suppress deprecation warning
# Override database URL if provided # Set database URL
if database_path: if database_path:
db_path = Path(database_path).resolve() db_path = Path(database_path).resolve()
# Use sqlite+aiosqlite:// for async support else:
url = f"sqlite+aiosqlite:///{db_path}" db_path = Path("/app/data/tokens.db") # Default for Docker
config.set_main_option("sqlalchemy.url", url)
logger.debug(f"Alembic configured with database: {db_path}") url = f"sqlite+aiosqlite:///{db_path}"
config.set_main_option("sqlalchemy.url", url)
logger.debug(f"Alembic script location: {script_location}")
logger.debug(f"Database: {db_path}")
return config return config