Skip to content

Commit 3d70821

Browse files
authored
[#438] Add DB CI Migration to remove Dependency on Dev Lead (#439)
1 parent 8984aef commit 3d70821

15 files changed

Lines changed: 6133 additions & 25 deletions

.github/PULL_REQUEST_TEMPLATE

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ This could include:
3333
## Checklist
3434

3535
<!--
36-
IMPORTANT: Non-automated step. Make sure to confirm this, or Prod will go down.
36+
IMPORTANT: Schema changes must include committed migration files.
37+
If production auto-migrations are still disabled, coordinate the rollout plan before merge.
3738
-->
3839

39-
- [ ] Database: No schema changes, OR I have contacted the Development Lead to run `db:push` before merging
40+
- [ ] Database: No schema changes, OR I ran `pnpm db:generate` and committed the generated files in `packages/db/drizzle/`
4041
- [ ] Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

.github/workflows/ci.yml

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,115 @@ concurrency:
1515
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
1616

1717
jobs:
18+
migration_check:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Setup
24+
uses: ./tooling/github/setup
25+
26+
- name: Generate migrations
27+
env:
28+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
29+
run: pnpm db:generate
30+
31+
- name: Verify migrations are committed
32+
run: |
33+
if [ -n "$(git status --porcelain -- packages/db/drizzle)" ]; then
34+
echo "Detected uncommitted migration changes in packages/db/drizzle."
35+
echo "Run 'pnpm db:generate' and commit the generated files."
36+
git status --short -- packages/db/drizzle
37+
exit 1
38+
fi
39+
40+
migration_apply_fresh:
41+
runs-on: ubuntu-latest
42+
services:
43+
postgres:
44+
image: postgres:16
45+
env:
46+
POSTGRES_USER: postgres
47+
POSTGRES_PASSWORD: postgres
48+
POSTGRES_DB: forge
49+
options: >-
50+
--health-cmd="pg_isready -U postgres -d forge"
51+
--health-interval=10s
52+
--health-timeout=5s
53+
--health-retries=5
54+
ports:
55+
- 5432:5432
56+
steps:
57+
- uses: actions/checkout@v4
58+
59+
- name: Setup
60+
uses: ./tooling/github/setup
61+
62+
- name: Apply migrations on a fresh database
63+
env:
64+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/forge
65+
run: |
66+
pnpm db:migrate
67+
pnpm db:migrate
68+
69+
migration_upgrade_smoke:
70+
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && vars.ENABLE_DB_UPGRADE_SMOKE == 'true'
71+
runs-on: ubuntu-latest
72+
services:
73+
postgres:
74+
image: postgres:16
75+
env:
76+
POSTGRES_USER: postgres
77+
POSTGRES_PASSWORD: postgres
78+
POSTGRES_DB: local
79+
options: >-
80+
--health-cmd="pg_isready -U postgres -d forge"
81+
--health-interval=10s
82+
--health-timeout=5s
83+
--health-retries=5
84+
ports:
85+
- 5432:5432
86+
steps:
87+
- uses: actions/checkout@v4
88+
with:
89+
fetch-depth: 0
90+
91+
- name: Setup
92+
uses: ./tooling/github/setup
93+
94+
- name: Create base branch worktree
95+
run: git worktree add /tmp/forge-base "${{ github.event.pull_request.base.sha }}"
96+
97+
- name: Install base branch dependencies
98+
working-directory: /tmp/forge-base
99+
run: pnpm install --ignore-scripts
100+
101+
- name: Materialize base branch schema
102+
working-directory: /tmp/forge-base
103+
env:
104+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/local
105+
run: |
106+
if node -e "const pkg = require('./package.json'); process.exit(pkg.scripts['db:migrate'] ? 0 : 1)"; then
107+
pnpm db:migrate
108+
else
109+
pnpm --filter=@forge/db push
110+
fi
111+
112+
- name: Restore filtered prod-like data
113+
env:
114+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/local
115+
MINIO_ENDPOINT: ${{ secrets.MINIO_ENDPOINT }}
116+
MINIO_ACCESS_KEY: ${{ secrets.MINIO_ACCESS_KEY }}
117+
MINIO_SECRET_KEY: ${{ secrets.MINIO_SECRET_KEY }}
118+
run: pnpm db:pull --truncate
119+
120+
- name: Apply branch migrations on prod-like data
121+
env:
122+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/local
123+
run: |
124+
pnpm db:migrate
125+
pnpm db:migrate
126+
18127
lint:
19128
runs-on: ubuntu-latest
20129
steps:
@@ -50,7 +159,7 @@ jobs:
50159

51160
build:
52161
runs-on: ubuntu-latest
53-
needs: [lint, format, typecheck]
162+
needs: [migration_check, migration_apply_fresh, lint, format, typecheck]
54163
permissions:
55164
contents: read
56165
steps:
@@ -64,3 +173,23 @@ jobs:
64173

65174
- name: Build
66175
run: pnpm build
176+
177+
migrate_prod:
178+
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.ENABLE_PROD_DB_MIGRATIONS == 'true'
179+
runs-on: ubuntu-latest
180+
needs: [build]
181+
concurrency:
182+
group: db-migrate-prod
183+
cancel-in-progress: false
184+
permissions:
185+
contents: read
186+
steps:
187+
- uses: actions/checkout@v4
188+
189+
- name: Setup
190+
uses: ./tooling/github/setup
191+
192+
- name: Apply database migrations
193+
env:
194+
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
195+
run: pnpm db:migrate

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ If you are not a Knight Hacks dev team member, you can still contribute to Forge
2323

2424
Install Docker Desktop if you don't already have it.
2525

26-
To create a Postgres database locally with docker, you can run `docker compose up`. You will then need to push the schema to the database by running `pnpm db:push`.
26+
To create a Postgres database locally with Docker, run `docker compose up`. Then apply the committed schema migrations by running `pnpm db:migrate`.
27+
28+
If you change any schema files in `packages/db/src/schemas`, generate and commit a migration with `pnpm db:generate`.
2729

2830
To stop the Postgres container, run `docker compose stop`. To completely reset your database, run `docker compose down --volumes`.
2931

docs/ARCHITECTURE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,11 @@ Database layer using Drizzle ORM with PostgreSQL.
7878

7979
- Contains all database schemas
8080
- Exports the database client
81-
- Includes migration scripts
81+
- Includes committed SQL migrations and migration helper scripts
8282
- Located in `packages/db/src/schemas/`
8383

84+
Local development applies schema changes with `pnpm db:migrate`. Schema edits should be followed by `pnpm db:generate`, and the generated files in `packages/db/drizzle/` are part of the reviewed source of truth.
85+
8486
### `@forge/auth`
8587

8688
Authentication setup using Better Auth with Discord OAuth.

docs/GETTING-STARTED.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,20 @@ docker compose up
7575

7676
> IMPORTANT!
7777
78-
You must push the database schema to your local database before running the project. This is a common source of errors for new contributors. The most common error for this will be a "Failed to get current session" error on any page within Blade.
78+
You must apply the committed migrations to your local database before running the project. This is a common source of errors for new contributors. The most common symptom is a "Failed to get current session" error on Blade pages.
7979

8080
```bash
81-
pnpm db:push
81+
pnpm db:migrate
8282
```
8383

84+
When you change files in `packages/db/src/schemas`, generate a new migration and commit it:
85+
86+
```bash
87+
pnpm db:generate
88+
```
89+
90+
Pull requests with schema changes are expected to include the generated files in `packages/db/drizzle/`. CI verifies that migrations are committed, that they apply on a fresh database, and that they can upgrade a prod-like sanitized dataset.
91+
8492
**Optional:** View the database contents with Drizzle Studio:
8593

8694
```bash
@@ -119,7 +127,7 @@ After running this, you'll have full superadmin permissions and can manage other
119127

120128
### 6. (Optional) Populate Test Data from Production
121129

122-
**Dev Team Members Only:** If you have access to the shared MinIO instance, you can pull a sanitized copy of production data to test with realistic data locally.
130+
**Dev Team Members Only:** If you have access to the shared MinIO instance, you can pull a sanitized copy of production data to test with realistic data locally. This restores data into the database currently pointed to by `DATABASE_URL`, so make sure you have already run `pnpm db:migrate` first.
123131

124132
```bash
125133
pnpm db:pull
@@ -191,11 +199,13 @@ Look for issues labeled [`Onboarding`](https://github.com/KnightHacks/forge/labe
191199
3. Test your changes locally
192200
4. Run checks before submitting:
193201
```bash
202+
pnpm db:migrate
194203
pnpm format
195204
pnpm lint
196205
pnpm typecheck
197206
pnpm build
198207
```
208+
If you changed any schema files, also run `pnpm db:generate` and commit the generated migration files.
199209
5. Commit your changes (use lowercase, descriptive commit messages)
200210
6. Push your branch and open a pull request
201211

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"start": "turbo run start",
1212
"clean": "git clean -xdf node_modules",
1313
"clean:workspaces": "turbo run clean",
14+
"db:generate": "pnpm --filter=@forge/db generate",
15+
"db:migrate": "pnpm --filter=@forge/db migrate",
1416
"db:push": "turbo -F @forge/db push",
1517
"db:studio": "turbo -F @forge/db studio",
1618
"db:pull": "pnpm --filter=@forge/db with-env tsx scripts/get_prod_db.ts",

packages/db/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Database Migrations (Drizzle)
2+
3+
This package uses migration files committed to the repository.
4+
5+
## Standard workflow
6+
7+
1. Start a local Postgres database.
8+
2. Apply committed migrations:
9+
10+
```bash
11+
pnpm db:migrate
12+
```
13+
14+
3. Update schema files in `src/schemas/`.
15+
4. Generate migration SQL:
16+
17+
```bash
18+
pnpm db:generate
19+
```
20+
21+
5. Commit the generated files in `packages/db/drizzle/`.
22+
6. Open a PR. CI will fail if schema and migration files are out of sync and will smoke test applying migrations.
23+
7. After merge to `main`, CI can apply pending migrations with `pnpm db:migrate` once production is baselined and the deploy gate is enabled.
24+
25+
## Commands
26+
27+
- `pnpm db:generate`: Generate SQL migration files from schema changes.
28+
- `pnpm db:migrate`: Apply pending migrations using `DATABASE_URL`.
29+
- `pnpm db:pull`: Restore the filtered prod-like backup into the database at `DATABASE_URL`.
30+
- `pnpm db:push`: Directly push schema changes (emergency/non-standard only).
31+
32+
## Notes
33+
34+
- Migrations are the reviewable source of truth for DB changes.
35+
- Local development should use `pnpm db:migrate`, not `pnpm db:push`.
36+
- `pnpm db:pull` is intended for maintainers/CI jobs that have MinIO credentials.
37+
- Production migration apply job expects `PROD_DATABASE_URL` in GitHub secrets and maps it to `DATABASE_URL`.

packages/db/drizzle.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { env } from "./src/env";
44

55
export default {
66
schema: "./src/schemas",
7+
out: "./drizzle",
78
dialect: "postgresql",
89
dbCredentials: { url: env.DATABASE_URL },
910
casing: "snake_case",

0 commit comments

Comments
 (0)