Skip to content

Commit f277ba1

Browse files
committed
Add dev infrastructure: observability, migrations CLI, and archive proposal
- Add Sentry observability module with init_sentry(), capture_exception(), breadcrumbs - Add Alembic database migration CLI commands (jdo db status/upgrade/downgrade/revision) - Add settings for sentry_dsn, sentry_traces_sample_rate, environment - Archive add-dev-infrastructure change proposal after completion - Update AGENTS.md with migration workflow documentation
1 parent b0a018e commit f277ba1

File tree

23 files changed

+1033
-80
lines changed

23 files changed

+1033
-80
lines changed

AGENTS.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,56 @@ class MyWidget(Widget):
8484

8585
When pyrefly reports errors on working code, verify at runtime then document here.
8686

87+
## Pre-commit Hooks
88+
89+
Pre-commit hooks are configured to run automatically before each commit:
90+
91+
```bash
92+
# Install hooks (one-time setup)
93+
uv run pre-commit install
94+
95+
# Run manually on all files
96+
uv run pre-commit run --all-files
97+
98+
# Skip hooks if needed (not recommended)
99+
git commit --no-verify
100+
```
101+
102+
Hooks configured:
103+
- `trailing-whitespace` - Remove trailing whitespace
104+
- `end-of-file-fixer` - Ensure files end with newline
105+
- `check-yaml` - Validate YAML syntax
106+
- `check-toml` - Validate TOML syntax
107+
- `check-added-large-files` - Block files > 500KB
108+
- `ruff` - Lint with auto-fix
109+
- `ruff-format` - Format code
110+
- `pyrefly` - Type checking on src/
111+
112+
## Database Migrations
113+
114+
Database migrations are managed with Alembic:
115+
116+
```bash
117+
# Check current migration status
118+
jdo db status
119+
120+
# Apply all pending migrations
121+
jdo db upgrade
122+
123+
# Create a new migration (auto-detects model changes)
124+
jdo db revision -m "Add new field to model"
125+
126+
# Rollback one migration
127+
jdo db downgrade
128+
```
129+
130+
**Workflow for schema changes**:
131+
1. Modify SQLModel model in `src/jdo/models/`
132+
2. Create migration: `jdo db revision -m "Description"`
133+
3. Review generated migration in `migrations/versions/`
134+
4. Apply migration: `jdo db upgrade`
135+
5. Run tests: `uv run pytest`
136+
87137
## Context7 Docs (check before coding)
88138

89139
| Tech | ID | Topics |

migrations/alembic.ini

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Alembic configuration for JDO database migrations
2+
3+
[alembic]
4+
# Path to migration scripts
5+
script_location = .
6+
7+
# Template for migration file names
8+
file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s_%%(slug)s
9+
10+
# Truncate slug field to 40 characters
11+
truncate_slug_length = 40
12+
13+
# Prepend sys.path with the project root
14+
prepend_sys_path = ..
15+
16+
# Timezone to use when rendering dates
17+
# Leave blank for localtime
18+
timezone =
19+
20+
# Max revision name length
21+
# truncate_slug_length = 40
22+
23+
# SQLAlchemy URL - dynamically loaded from settings
24+
# sqlalchemy.url = driver://user:pass@localhost/dbname
25+
26+
# Logging configuration
27+
[loggers]
28+
keys = root,sqlalchemy,alembic
29+
30+
[handlers]
31+
keys = console
32+
33+
[formatters]
34+
keys = generic
35+
36+
[logger_root]
37+
level = WARN
38+
handlers = console
39+
qualname =
40+
41+
[logger_sqlalchemy]
42+
level = WARN
43+
handlers =
44+
qualname = sqlalchemy.engine
45+
46+
[logger_alembic]
47+
level = INFO
48+
handlers =
49+
qualname = alembic
50+
51+
[handler_console]
52+
class = StreamHandler
53+
args = (sys.stderr,)
54+
level = NOTSET
55+
formatter = generic
56+
57+
[formatter_generic]
58+
format = %(levelname)-5.5s [%(name)s] %(message)s
59+
datefmt = %H:%M:%S

migrations/env.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""Alembic migration environment configuration.
2+
3+
This module configures Alembic for SQLModel migrations with SQLite support.
4+
"""
5+
6+
import sys
7+
from logging.config import fileConfig
8+
from pathlib import Path
9+
10+
from alembic import context
11+
from sqlalchemy import engine_from_config, pool
12+
from sqlmodel import SQLModel
13+
14+
# Add the project root to sys.path for model imports
15+
project_root = Path(__file__).parent.parent
16+
sys.path.insert(0, str(project_root / "src"))
17+
18+
# Import all models to ensure they're registered with SQLModel.metadata
19+
# This is required for autogenerate to detect model changes
20+
from jdo.models import ( # noqa: E402, F401
21+
Commitment,
22+
Draft,
23+
Goal,
24+
Milestone,
25+
RecurringCommitment,
26+
Stakeholder,
27+
Task,
28+
Vision,
29+
)
30+
31+
# Get settings for database path
32+
from jdo.config.settings import get_settings # noqa: E402
33+
34+
# Alembic Config object for access to .ini file values
35+
config = context.config
36+
37+
# Configure Python logging from alembic.ini
38+
if config.config_file_name is not None:
39+
fileConfig(config.config_file_name)
40+
41+
# Set target metadata for autogenerate support
42+
target_metadata = SQLModel.metadata
43+
44+
45+
def get_url() -> str:
46+
"""Get the database URL from settings.
47+
48+
Returns:
49+
SQLite database URL.
50+
"""
51+
settings = get_settings()
52+
return f"sqlite:///{settings.database_path}"
53+
54+
55+
def run_migrations_offline() -> None:
56+
"""Run migrations in 'offline' mode.
57+
58+
This configures the context with just a URL and not an Engine,
59+
useful for generating SQL scripts without connecting to a database.
60+
"""
61+
url = get_url()
62+
context.configure(
63+
url=url,
64+
target_metadata=target_metadata,
65+
literal_binds=True,
66+
dialect_opts={"paramstyle": "named"},
67+
render_as_batch=True, # Required for SQLite ALTER TABLE support
68+
)
69+
70+
with context.begin_transaction():
71+
context.run_migrations()
72+
73+
74+
def run_migrations_online() -> None:
75+
"""Run migrations in 'online' mode.
76+
77+
Creates an Engine and associates a connection with the context.
78+
"""
79+
# Build configuration dictionary
80+
configuration = {
81+
"sqlalchemy.url": get_url(),
82+
}
83+
84+
connectable = engine_from_config(
85+
configuration,
86+
prefix="sqlalchemy.",
87+
poolclass=pool.NullPool,
88+
)
89+
90+
with connectable.connect() as connection:
91+
context.configure(
92+
connection=connection,
93+
target_metadata=target_metadata,
94+
render_as_batch=True, # Required for SQLite ALTER TABLE support
95+
)
96+
97+
with context.begin_transaction():
98+
context.run_migrations()
99+
100+
101+
if context.is_offline_mode():
102+
run_migrations_offline()
103+
else:
104+
run_migrations_online()

migrations/script.py.mako

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from collections.abc import Sequence
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
${imports if imports else ""}
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = ${repr(up_revision)}
16+
down_revision: str | None = ${repr(down_revision)}
17+
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
18+
depends_on: str | Sequence[str] | None = ${repr(depends_on)}
19+
20+
21+
def upgrade() -> None:
22+
"""Apply migration changes."""
23+
${upgrades if upgrades else "pass"}
24+
25+
26+
def downgrade() -> None:
27+
"""Revert migration changes."""
28+
${downgrades if downgrades else "pass"}

openspec/changes/add-dev-infrastructure/tasks.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

openspec/changes/add-dev-infrastructure/design.md renamed to openspec/changes/archive/2025-12-18-add-dev-infrastructure/design.md

File renamed without changes.

openspec/changes/add-dev-infrastructure/proposal.md renamed to openspec/changes/archive/2025-12-18-add-dev-infrastructure/proposal.md

File renamed without changes.

openspec/changes/add-dev-infrastructure/specs/database-migrations/spec.md renamed to openspec/changes/archive/2025-12-18-add-dev-infrastructure/specs/database-migrations/spec.md

File renamed without changes.

openspec/changes/add-dev-infrastructure/specs/dev-tooling/spec.md renamed to openspec/changes/archive/2025-12-18-add-dev-infrastructure/specs/dev-tooling/spec.md

File renamed without changes.

openspec/changes/add-dev-infrastructure/specs/error-handling/spec.md renamed to openspec/changes/archive/2025-12-18-add-dev-infrastructure/specs/error-handling/spec.md

File renamed without changes.

0 commit comments

Comments
 (0)