Skip to content

Commit 62212ac

Browse files
phernandezclaude
andcommitted
fix: run migrations when using CLI entry point
- Add ensure_migrations function to run migrations on startup - Make CLI callback run migrations for every command - Add tests to verify migration behavior - Fixes issue #87 where migrations weren't run when using pip-installed CLI 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 1d31fc0 commit 62212ac

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

src/basic_memory/cli/main.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44

55
import typer
6+
from loguru import logger
67

78
from basic_memory.cli.app import app # pragma: no cover
89

@@ -23,6 +24,18 @@
2324
from basic_memory.db import run_migrations as db_run_migrations
2425

2526

27+
# Helper function to run database migrations
28+
def ensure_migrations(): # pragma: no cover
29+
"""Ensure database migrations are run before executing commands."""
30+
try:
31+
logger.info("Running database migrations on startup...")
32+
asyncio.run(db_run_migrations(config))
33+
except Exception as e:
34+
logger.error(f"Error running migrations: {e}")
35+
# Continue execution even if migrations fail
36+
# The actual command might still work or will fail with a more specific error
37+
38+
2639
# Version command
2740
@app.callback(invoke_without_command=True)
2841
def main(
@@ -58,6 +71,10 @@ def main(
5871

5972
os.environ["BASIC_MEMORY_PROJECT"] = project
6073

74+
# Run migrations for every command unless --version was specified
75+
if not version and ctx.invoked_subcommand is not None:
76+
ensure_migrations()
77+
6178

6279
if __name__ == "__main__": # pragma: no cover
6380
# Run database migrations

tests/cli/test_cli_tools.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
33
These tests use real MCP tools with the test environment instead of mocks.
44
"""
5+
# Import the ensure_migrations function from main.py for testing
6+
import asyncio
7+
from basic_memory.cli.main import ensure_migrations
58

69
import io
710
from datetime import datetime, timedelta
@@ -410,3 +413,36 @@ def test_continue_conversation_no_results(cli_env):
410413
# Check result contains expected content for no results
411414
assert "Continuing conversation on: NonexistentTopic" in result.stdout
412415
assert "The supplied query did not return any information" in result.stdout
416+
417+
418+
@patch("basic_memory.cli.main.db_run_migrations")
419+
def test_ensure_migrations_runs_migrations(mock_run_migrations, test_config, monkeypatch):
420+
"""Test that ensure_migrations runs migrations."""
421+
# Configure mock
422+
async def mock_async_success(*args, **kwargs):
423+
return True
424+
425+
mock_run_migrations.return_value = mock_async_success()
426+
427+
# Call the function
428+
ensure_migrations()
429+
430+
# Check that run_migrations was called
431+
mock_run_migrations.assert_called_once()
432+
433+
434+
@patch("basic_memory.cli.main.db_run_migrations")
435+
@patch("basic_memory.cli.main.logger")
436+
def test_ensure_migrations_handles_errors(mock_logger, mock_run_migrations, test_config, monkeypatch):
437+
"""Test that ensure_migrations handles errors gracefully."""
438+
# Configure mock to raise an exception when awaited
439+
async def mock_async_error(*args, **kwargs):
440+
raise Exception("Test error")
441+
442+
mock_run_migrations.side_effect = mock_async_error
443+
444+
# Call the function
445+
ensure_migrations()
446+
447+
# Check that error was logged
448+
mock_logger.error.assert_called_once()

0 commit comments

Comments
 (0)