feat: add Alembic database migration system
Implements Alembic for managing token storage database schema versions. Migrations run automatically on startup with full backward compatibility. **Changes:** - Add Alembic dependency (1.14.0+) and SQLAlchemy (auto-installed) - Create migration infrastructure in alembic/ directory - Add initial migration (001) capturing current schema - Modify RefreshTokenStorage.initialize() to run migrations via anyio - Add CLI commands: db upgrade, current, history, downgrade, migrate - Add comprehensive migration documentation **Backward Compatibility:** - Pre-Alembic databases automatically stamped with revision 001 - No schema changes for existing databases - Automatic upgrade on first startup after update **Migration Strategy:** Three scenarios handled: 1. New database → Run migrations from scratch 2. Pre-Alembic database → Stamp with 001 (no changes) 3. Alembic-managed → Upgrade to latest **Architecture:** - Uses anyio.to_thread.run_sync() for structured concurrency - Alembic env.py runs with anyio.run() in worker thread - SQLite-friendly migration patterns documented - No ThreadPoolExecutor needed (anyio handles it) **CLI Usage:** ```bash nextcloud-mcp-server db upgrade # Upgrade to latest nextcloud-mcp-server db current # Show version nextcloud-mcp-server db history # View changelog nextcloud-mcp-server db downgrade # Rollback (with confirmation) nextcloud-mcp-server db migrate "description" # Create migration ``` **Testing:** - All 13 webhook storage tests pass - New/pre-Alembic database scenarios validated - anyio integration tested 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
a4a34e46a8
commit
3fa376905c
@@ -0,0 +1,71 @@
|
||||
Database Migrations for nextcloud-mcp-server
|
||||
============================================
|
||||
|
||||
This directory contains Alembic database migrations for the token storage database.
|
||||
|
||||
Structure
|
||||
---------
|
||||
- env.py: Alembic environment configuration
|
||||
- script.py.mako: Template for generating new migration files
|
||||
- versions/: Directory containing migration scripts
|
||||
|
||||
Usage
|
||||
-----
|
||||
Migrations are managed via the CLI:
|
||||
|
||||
# Upgrade database to latest version
|
||||
uv run nextcloud-mcp-server db upgrade
|
||||
|
||||
# Show current database version
|
||||
uv run nextcloud-mcp-server db current
|
||||
|
||||
# Show migration history
|
||||
uv run nextcloud-mcp-server db history
|
||||
|
||||
# Create a new migration (developers only)
|
||||
uv run nextcloud-mcp-server db migrate "description of changes"
|
||||
|
||||
# Downgrade database by one version (emergency use only)
|
||||
uv run nextcloud-mcp-server db downgrade
|
||||
|
||||
Direct Alembic Usage
|
||||
--------------------
|
||||
You can also use Alembic commands directly:
|
||||
|
||||
# Specify database URL via -x flag
|
||||
uv run alembic -x database_url=sqlite+aiosqlite:////path/to/tokens.db upgrade head
|
||||
|
||||
# Or set in alembic.ini and run
|
||||
uv run alembic upgrade head
|
||||
uv run alembic current
|
||||
uv run alembic history
|
||||
|
||||
Writing Migrations
|
||||
------------------
|
||||
Since we don't use SQLAlchemy models, migrations are written with raw SQL:
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("""
|
||||
ALTER TABLE refresh_tokens
|
||||
ADD COLUMN new_field TEXT
|
||||
""")
|
||||
|
||||
def downgrade() -> None:
|
||||
# SQLite doesn't support DROP COLUMN, use table recreation
|
||||
op.execute("""
|
||||
CREATE TABLE refresh_tokens_new AS
|
||||
SELECT user_id, encrypted_token, ... FROM refresh_tokens
|
||||
""")
|
||||
op.execute("DROP TABLE refresh_tokens")
|
||||
op.execute("ALTER TABLE refresh_tokens_new RENAME TO refresh_tokens")
|
||||
|
||||
Migration File Naming
|
||||
---------------------
|
||||
Format: YYYYMMDD_HHMM_<revision>_<slug>.py
|
||||
Example: 20251217_2200_001_initial_schema.py
|
||||
|
||||
Notes
|
||||
-----
|
||||
- Migrations run automatically when RefreshTokenStorage.initialize() is called
|
||||
- Existing databases are automatically stamped with the initial version
|
||||
- SQLite has limited ALTER TABLE support - complex changes require table recreation
|
||||
Reference in New Issue
Block a user