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 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
ENTRYPOINT ["/app/.venv/bin/nextcloud-mcp-server", "run", "--host", "0.0.0.0"]
+2 -2
View File
@@ -2,7 +2,7 @@
[alembic]
# Path to migration scripts
script_location = alembic
script_location = nextcloud_mcp_server/alembic
# Template used to generate migration file names
# Default: %%(rev)s_%%(slug)s
@@ -26,7 +26,7 @@ timezone = utc
# Version location specification
# 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)
# 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.
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
# Migrations will be written manually using op.execute() for raw SQL
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.
Works in both development and installed (Docker) modes by using
package location instead of alembic.ini file.
Args:
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:
Alembic Config object configured for the specified database
"""
# Get path to alembic.ini (in project root)
project_root = Path(__file__).parent.parent
alembic_ini_path = project_root / "alembic.ini"
from nextcloud_mcp_server import alembic as alembic_package
if not alembic_ini_path.exists():
raise FileNotFoundError(
f"alembic.ini not found at {alembic_ini_path}. "
"Ensure Alembic is properly initialized."
)
# Use package location (works in both editable and installed modes)
if alembic_package.__file__ is None:
raise RuntimeError("alembic package __file__ is None")
script_location = Path(alembic_package.__file__).parent
# Create Alembic config
config = Config(str(alembic_ini_path))
# Create config programmatically (no alembic.ini needed at runtime)
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:
db_path = Path(database_path).resolve()
# Use sqlite+aiosqlite:// for async support
url = f"sqlite+aiosqlite:///{db_path}"
config.set_main_option("sqlalchemy.url", url)
logger.debug(f"Alembic configured with database: {db_path}")
else:
db_path = Path("/app/data/tokens.db") # Default for Docker
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