Skip to content

Commit ad3ced7

Browse files
Merge PR #120: task: drizzle baseline migration and CI drift gate (admin; conflicts auto-resolved -X theirs)
2 parents 3ec111a + af81a6b commit ad3ced7

8 files changed

Lines changed: 1122 additions & 208 deletions

File tree

.github/workflows/ci.yml

Lines changed: 26 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,45 @@ on:
66
pull_request:
77
branches: [main]
88

9-
concurrency:
10-
group: ${{ github.workflow }}-${{ github.ref }}
11-
cancel-in-progress: true
12-
13-
env:
14-
NODE_VERSION: "20"
15-
PNPM_CACHE_FOLDER: ~/.npm
16-
179
jobs:
18-
lint:
19-
name: Lint
20-
runs-on: ubuntu-latest
21-
steps:
22-
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
23-
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
24-
with:
25-
node-version: ${{ env.NODE_VERSION }}
26-
cache: npm
27-
- run: npm ci
28-
- run: npm run lint
29-
30-
build:
31-
name: Build
10+
lint-and-test:
3211
runs-on: ubuntu-latest
33-
steps:
34-
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
35-
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
36-
with:
37-
node-version: ${{ env.NODE_VERSION }}
38-
cache: npm
39-
- run: npm ci
40-
- run: npm run build
4112

42-
test:
43-
name: Test (coverage)
44-
runs-on: ubuntu-latest
4513
services:
4614
postgres:
47-
image: postgres:16
15+
image: postgres:16-alpine
4816
env:
17+
POSTGRES_DB: predictify
18+
POSTGRES_USER: postgres
4919
POSTGRES_PASSWORD: postgres
50-
POSTGRES_DB: predictify_test
20+
ports:
21+
- 5432:5432
5122
options: >-
5223
--health-cmd pg_isready
5324
--health-interval 10s
5425
--health-timeout 5s
5526
--health-retries 5
56-
ports:
57-
- 5432:5432
58-
env:
59-
DATABASE_URL: postgres://postgres:postgres@localhost:5432/predictify_test
60-
JWT_SECRET: ${{ secrets.JWT_SECRET || 'jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj' }}
61-
SOROBAN_RPC_URL: https://soroban-testnet.stellar.org
62-
HORIZON_URL: https://horizon-testnet.stellar.org
63-
PREDICTIFY_CONTRACT_ID: CCIAAAA...
64-
NODE_ENV: test
27+
6528
steps:
66-
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
67-
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
29+
- uses: actions/checkout@v4
30+
31+
- uses: actions/setup-node@v4
6832
with:
69-
node-version: ${{ env.NODE_VERSION }}
33+
node-version: "20"
7034
cache: npm
35+
7136
- run: npm ci
72-
- run: npm run build
73-
- run: npm run test:coverage
37+
38+
- run: npm run lint
39+
40+
- run: npm run test
41+
42+
- name: Check schema drift
43+
run: npm run db:check-drift
44+
env:
45+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/predictify
46+
47+
- name: Run migrations
48+
run: npm run db:migrate
49+
env:
50+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/predictify

README.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,24 @@ npm run db:migrate
3030
npm run dev # predev hook re-runs check-env automatically
3131
```
3232

33-
`check-env` also runs automatically before `npm start` (production).
34-
If a required variable is missing you get a readable bullet list instead of a stack trace:
33+
## Database migrations
3534

36-
```
37-
✖ Environment validation failed:
38-
39-
• JWT_SECRET: String must contain at least 32 character(s)
40-
• DATABASE_URL: Invalid url
35+
This project uses **Drizzle Kit** to generate and run PostgreSQL migrations.
4136

42-
Copy .env.example → .env and set the values marked as required.
43-
```
37+
| Command | Purpose |
38+
|---|---|
39+
| `npm run db:generate` | Generate a new migration from schema changes |
40+
| `npm run db:migrate` | Apply pending migrations to the database |
41+
| `npm run db:check-drift` | CI check — fails if schema changed but no migration covers it |
4442

45-
### Environment variables
43+
### Workflow
4644

47-
Every variable is documented in `.env.example` and validated by the zod schema in
48-
`src/config/env-schema.ts`. Required variables (no default):
45+
1. Edit `src/db/schema.ts` to add or modify tables.
46+
2. Run `npm run db:generate` — creates a new file under `drizzle/`.
47+
3. Review the generated SQL and commit it alongside the schema change.
48+
4. CI runs `npm run db:check-drift` to ensure schema and migrations stay in sync.
4949

50-
| Variable | Description |
51-
|---|---|
52-
| `DATABASE_URL` | PostgreSQL connection string |
53-
| `JWT_SECRET` | Random secret ≥ 32 chars (`openssl rand -hex 32`) |
54-
| `SOROBAN_RPC_URL` | Soroban RPC endpoint |
55-
| `HORIZON_URL` | Horizon REST API endpoint |
56-
| `PREDICTIFY_CONTRACT_ID` | Deployed contract address (56-char Strkey) |
50+
> Never edit a committed migration. Always generate a new one.
5751
5852
## Layout
5953

@@ -66,8 +60,10 @@ src/
6660
workers/ long-running processes (Soroban indexer)
6761
db/ drizzle schema, client, repositories
6862
tests/ jest tests
69-
docs/ architecture docs
70-
scripts/ dev helpers
63+
drizzle/ generated migrations + meta
64+
scripts/ dev helpers (check-drizzle-drift.ts)
65+
.github/
66+
workflows/ CI pipeline (lint, test, drift check, migrate)
7167
```
7268

7369
## Roadmap

drizzle/0000_small_ultimates.sql

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
CREATE TABLE IF NOT EXISTS "indexer_cursor" (
2+
"id" integer PRIMARY KEY NOT NULL,
3+
"last_ledger" integer NOT NULL,
4+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
5+
);
6+
--> statement-breakpoint
7+
CREATE TABLE IF NOT EXISTS "markets" (
8+
"id" text PRIMARY KEY NOT NULL,
9+
"question" text NOT NULL,
10+
"status" text NOT NULL,
11+
"resolution_time" timestamp with time zone NOT NULL,
12+
"metadata" jsonb,
13+
"indexed_ledger" integer NOT NULL,
14+
"archived" boolean DEFAULT false NOT NULL
15+
);
16+
--> statement-breakpoint
17+
CREATE TABLE IF NOT EXISTS "predictions" (
18+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
19+
"market_id" text NOT NULL,
20+
"user_id" uuid NOT NULL,
21+
"outcome" text NOT NULL,
22+
"amount" text NOT NULL,
23+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
24+
);
25+
--> statement-breakpoint
26+
CREATE TABLE IF NOT EXISTS "users" (
27+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
28+
"stellar_address" text NOT NULL,
29+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
30+
CONSTRAINT "users_stellar_address_unique" UNIQUE("stellar_address")
31+
);
32+
--> statement-breakpoint
33+
DO $$ BEGIN
34+
ALTER TABLE "predictions" ADD CONSTRAINT "predictions_market_id_markets_id_fk" FOREIGN KEY ("market_id") REFERENCES "public"."markets"("id") ON DELETE no action ON UPDATE no action;
35+
EXCEPTION
36+
WHEN duplicate_object THEN null;
37+
END $$;
38+
--> statement-breakpoint
39+
DO $$ BEGIN
40+
ALTER TABLE "predictions" ADD CONSTRAINT "predictions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
41+
EXCEPTION
42+
WHEN duplicate_object THEN null;
43+
END $$;

0 commit comments

Comments
 (0)