Skip to content

Commit 44086d5

Browse files
#53 Build portfolio migration tool for schema/version upgrades
1 parent f49b65d commit 44086d5

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

.github/workflows/backend-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ on:
44
pull_request:
55
paths:
66
- 'backend/**'
7+
- 'docs/MIGRATION.md'
78
- '.github/workflows/backend-tests.yml'
89
push:
910
branches:
1011
- main
1112
- develop
1213
paths:
1314
- 'backend/**'
15+
- 'docs/MIGRATION.md'
1416

1517
jobs:
1618
test:
@@ -35,6 +37,10 @@ jobs:
3537
working-directory: backend
3638
run: npm ci
3739

40+
- name: Verify migrations (dry-run)
41+
working-directory: backend
42+
run: npm run db:migrate:dry-run
43+
3844
- name: Run tests
3945
working-directory: backend
4046
run: npm run test

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ cp backend/.env.example backend/.env
6767
cp frontend/.env.example frontend/.env
6868
# Edit with contract addresses
6969

70+
**Database migrations** (when using PostgreSQL with `DATABASE_URL`): see [docs/MIGRATION.md](docs/MIGRATION.md). Apply with `cd backend && npm run db:migrate`; use `--dry-run` to preview.
71+
7072
Configure SMTP for Email Notifications (Optional)
7173

7274
To enable email notifications for rebalancing events:

docs/MIGRATION.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Database migrations
2+
3+
This project uses a **versioned migration framework** for PostgreSQL. Schema changes are applied deterministically and can be rolled back when needed.
4+
5+
## Quick reference
6+
7+
| Task | Command |
8+
|------|--------|
9+
| Apply pending migrations | `cd backend && npm run db:migrate` |
10+
| Preview (dry-run) | `cd backend && npm run db:migrate -- --dry-run` |
11+
| Roll back last migration | `cd backend && npm run db:migrate -- --rollback` |
12+
| Roll back last N migrations | `cd backend && npm run db:migrate -- --rollback 2` |
13+
| Show status | `cd backend && npm run db:migrate -- --status` |
14+
15+
Requires `DATABASE_URL` in the environment (e.g. in `.env` or CI).
16+
17+
---
18+
19+
## Backup and rollback
20+
21+
### Before running migrations
22+
23+
1. **Back up the database** (recommended for production):
24+
- **PostgreSQL:** `pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql`
25+
- Or use your provider’s snapshot/backup (e.g. RDS snapshot, Heroku pg:backups).
26+
27+
2. **Dry-run** to see what will run:
28+
```bash
29+
npm run db:migrate -- --dry-run
30+
```
31+
32+
3. Run migrations:
33+
```bash
34+
npm run db:migrate
35+
```
36+
37+
### If a migration fails
38+
39+
1. Fix the failure (e.g. fix SQL, fix data, or fix environment).
40+
2. If you need to **undo the last migration**:
41+
```bash
42+
npm run db:migrate -- --rollback
43+
```
44+
This runs the corresponding `.down.sql` and removes the row from `schema_migrations`.
45+
46+
3. Restore from backup if you need to restore data:
47+
```bash
48+
psql $DATABASE_URL < backup_YYYYMMDD_HHMMSS.sql
49+
```
50+
51+
### Rollback by migration
52+
53+
Each migration has a **down** file (e.g. `001_initial_schema.down.sql`) that reverses the **up** migration. The runner applies down migrations in reverse order when you use `--rollback [n]`. Documented rollback behavior:
54+
55+
| Migration | Rollback (down) |
56+
|-----------|------------------|
57+
| `001_initial_schema` | Drops `notification_preferences`, `analytics_snapshots`, `rebalance_events`, `portfolios` (in that order). |
58+
| `002_seed_demo_data` | Deletes demo portfolio `demo-portfolio-1` and its rebalance events. |
59+
60+
---
61+
62+
## Seed / demo data migration path
63+
64+
- **Optional migration:** `002_seed_demo_data` inserts a demo portfolio and sample rebalance events. It is **idempotent** (safe to run multiple times; uses `ON CONFLICT DO NOTHING`).
65+
- **When to use:** Development, staging, or demo environments. You can **skip** this migration in production by not running it, or run it once for a demo instance.
66+
- **To apply only schema (no demo data):** Ensure `002_seed_demo_data` is not applied (e.g. use a separate DB for prod and run only `001_initial_schema`, or roll back `002` after seeding a staging DB if you prefer).
67+
- **SQLite (local):** The backend also supports SQLite via `DB_PATH`. Demo data is seeded automatically by `DatabaseService` when the DB is empty; there is no separate migration runner for SQLite. For schema changes that affect both PostgreSQL and SQLite, update:
68+
- `backend/src/db/migrations/` (PostgreSQL)
69+
- `backend/src/services/databaseService.ts` `SCHEMA_SQL` (SQLite)
70+
71+
---
72+
73+
## Version history and CI
74+
75+
- **Version history** is stored in the `schema_migrations` table (`version`, `name`, `applied_at`).
76+
- **Migrations live in the repo** under `backend/src/db/migrations/` with naming:
77+
- `NNN_description.up.sql` – forward migration
78+
- `NNN_description.down.sql` – rollback for that version
79+
- **Deterministic order:** Migrations run in ascending order of `NNN`. The same list of files produces the same order in every environment.
80+
- **CI:** The backend workflow can run `npm run db:migrate -- --dry-run` to verify migration files and that the runner works. For full reproducibility, run real migrations in CI against a Postgres service container and then run tests (see workflow example below).
81+
82+
---
83+
84+
## Adding a new migration
85+
86+
1. Add two files in `backend/src/db/migrations/`:
87+
- `003_short_description.up.sql` – forward SQL
88+
- `003_short_description.down.sql` – rollback SQL
89+
2. Use the next sequential number; do not renumber existing migrations.
90+
3. Document rollback behavior in this file if it’s non-obvious.
91+
4. Run `npm run db:migrate -- --dry-run` to confirm, then apply with `npm run db:migrate`.

0 commit comments

Comments
 (0)