@xubylele/schema-forge 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -15
- package/dist/api.d.ts +101 -0
- package/dist/api.js +3623 -0
- package/dist/cli.js +696 -54
- package/package.json +19 -5
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A modern CLI tool for database schema management with a clean DSL and automatic
|
|
|
13
13
|
- **Default Change Detection** - Detects added/removed/modified column defaults and generates ALTER COLUMN SET/DROP DEFAULT
|
|
14
14
|
- **Postgres/Supabase** - Currently supports PostgreSQL and Supabase
|
|
15
15
|
- **Constraint Diffing** - Detects UNIQUE and PRIMARY KEY changes with deterministic constraint names
|
|
16
|
+
- **Live PostgreSQL Introspection** - Extract normalized schema directly from `information_schema`
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -28,6 +29,19 @@ Or use directly with npx (no installation required):
|
|
|
28
29
|
npx @xubylele/schema-forge init
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
### Programmatic API
|
|
33
|
+
|
|
34
|
+
Use the programmatic API from Node (e.g. scripts, GitHub Actions) instead of invoking the CLI via shell:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
const { generate, EXIT_CODES } = require('@xubylele/schema-forge/api');
|
|
38
|
+
|
|
39
|
+
const result = await generate({ name: 'MyMigration' });
|
|
40
|
+
if (result.exitCode !== EXIT_CODES.SUCCESS) process.exit(result.exitCode);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Exports:** `init`, `generate`, `diff`, `doctor`, `validate`, `introspect`, `importSchema` (each returns `Promise<RunResult>`), `RunResult` (`{ exitCode: number }`), `EXIT_CODES`, and option types (`GenerateOptions`, `DiffOptions`, etc.). Entrypoint: `@xubylele/schema-forge/api`. Exit code semantics: [docs/exit-codes.json](docs/exit-codes.json).
|
|
44
|
+
|
|
31
45
|
## Development
|
|
32
46
|
|
|
33
47
|
Clone the repository and install dependencies:
|
|
@@ -56,6 +70,17 @@ Run tests:
|
|
|
56
70
|
npm test
|
|
57
71
|
```
|
|
58
72
|
|
|
73
|
+
Run real-db drift integration tests:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run test:integration:drift
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
|
|
81
|
+
- Local explicit run: set `SF_RUN_REAL_DB_TESTS=true` (uses Testcontainers `postgres:16-alpine`, Docker required).
|
|
82
|
+
- CI/service mode: set `SF_USE_CI_POSTGRES=true` and `DATABASE_URL` to reuse an existing Postgres service.
|
|
83
|
+
|
|
59
84
|
## Getting Started
|
|
60
85
|
|
|
61
86
|
Here's a quick walkthrough to get started with SchemaForge:
|
|
@@ -205,9 +230,13 @@ schema-forge diff [--safe] [--force]
|
|
|
205
230
|
|
|
206
231
|
- `--safe` - Block execution if destructive operations are detected (exits with error)
|
|
207
232
|
- `--force` - Bypass safety checks and proceed with displaying destructive SQL (shows warning)
|
|
233
|
+
- `--url` - PostgreSQL connection URL for live database diff (fallback: `DATABASE_URL`)
|
|
234
|
+
- `--schema` - Comma-separated schema names to introspect (default: `public`)
|
|
208
235
|
|
|
209
236
|
Shows what SQL would be generated if you ran `generate`. Useful for previewing changes. Safety behavior is the same as `generate` command. In CI environments, exits with code 3 if destructive operations are detected unless `--force` is used. See [CI Behavior](#ci-behavior) for more details.
|
|
210
237
|
|
|
238
|
+
When `--url` (or `DATABASE_URL`) is provided, `diff` compares your target DSL schema against the live PostgreSQL schema introspected from `information_schema`.
|
|
239
|
+
|
|
211
240
|
### `schema-forge import`
|
|
212
241
|
|
|
213
242
|
Reconstruct `schemaforge/schema.sf` from existing PostgreSQL/Supabase SQL migrations.
|
|
@@ -234,6 +263,36 @@ Detect destructive or risky schema changes before generating/applying migrations
|
|
|
234
263
|
schema-forge validate
|
|
235
264
|
```
|
|
236
265
|
|
|
266
|
+
Live drift validation:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
schema-forge validate --url "$DATABASE_URL" --json
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Live `--json` output returns a structured `DriftReport`:
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"missingTables": ["users_archive"],
|
|
277
|
+
"extraTables": ["audit_log"],
|
|
278
|
+
"columnDifferences": [
|
|
279
|
+
{
|
|
280
|
+
"tableName": "users",
|
|
281
|
+
"missingInLive": ["nickname"],
|
|
282
|
+
"extraInLive": ["last_login"]
|
|
283
|
+
}
|
|
284
|
+
],
|
|
285
|
+
"typeMismatches": [
|
|
286
|
+
{
|
|
287
|
+
"tableName": "users",
|
|
288
|
+
"columnName": "email",
|
|
289
|
+
"expectedType": "varchar",
|
|
290
|
+
"actualType": "text"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
237
296
|
Validation checks include:
|
|
238
297
|
|
|
239
298
|
- Dropped tables (`DROP_TABLE`, error)
|
|
@@ -247,16 +306,49 @@ Use JSON mode for CI and automation:
|
|
|
247
306
|
schema-forge validate --json
|
|
248
307
|
```
|
|
249
308
|
|
|
250
|
-
|
|
309
|
+
Live mode options:
|
|
310
|
+
|
|
311
|
+
- `--url` - PostgreSQL connection URL for live drift validation (fallback: `DATABASE_URL`)
|
|
312
|
+
- `--schema` - Comma-separated schema names to introspect (default: `public`)
|
|
251
313
|
|
|
252
|
-
|
|
253
|
-
- `1` if one or more `error` findings are detected
|
|
254
|
-
- `0` if no `error` findings are detected (warnings alone do not fail)
|
|
314
|
+
In live mode, exit code `2` is used when drift is detected between `state.json` and the live database. For all exit codes used by `validate`, see [Exit code standards](#exit-code-standards).
|
|
255
315
|
|
|
256
|
-
|
|
316
|
+
### `schema-forge doctor`
|
|
257
317
|
|
|
258
|
-
|
|
259
|
-
|
|
318
|
+
Check live database drift against your tracked `state.json`.
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
schema-forge doctor --url "$DATABASE_URL"
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Use JSON mode for CI and automation:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
schema-forge doctor --url "$DATABASE_URL" --json
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Options:
|
|
331
|
+
|
|
332
|
+
- `--url` - PostgreSQL connection URL (fallback: `DATABASE_URL`)
|
|
333
|
+
- `--schema` - Comma-separated schema names to introspect (default: `public`)
|
|
334
|
+
- `--json` - Output structured drift report JSON
|
|
335
|
+
|
|
336
|
+
Exit codes: see [Exit code standards](#exit-code-standards) (doctor uses 0, 2).
|
|
337
|
+
|
|
338
|
+
### `schema-forge introspect`
|
|
339
|
+
|
|
340
|
+
Extract normalized schema directly from a live PostgreSQL database.
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
schema-forge introspect --url "$DATABASE_URL" --json
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Options:**
|
|
347
|
+
|
|
348
|
+
- `--url` - PostgreSQL connection URL (fallback: `DATABASE_URL`)
|
|
349
|
+
- `--schema` - Comma-separated schema names to introspect (default: `public`)
|
|
350
|
+
- `--json` - Output normalized schema as JSON
|
|
351
|
+
- `--out <path>` - Write normalized schema JSON to a file
|
|
260
352
|
|
|
261
353
|
## CI Behavior
|
|
262
354
|
|
|
@@ -269,16 +361,29 @@ CI mode is automatically activated when either environment variable is set:
|
|
|
269
361
|
- `CI=true`
|
|
270
362
|
- `CONTINUOUS_INTEGRATION=true`
|
|
271
363
|
|
|
272
|
-
### Exit
|
|
364
|
+
### Exit code standards
|
|
365
|
+
|
|
366
|
+
SchemaForge uses specific exit codes for deterministic CI and script behavior. The following is the single source of truth.
|
|
367
|
+
|
|
368
|
+
| Exit Code | Name | Meaning |
|
|
369
|
+
| --------- | ---- | ------- |
|
|
370
|
+
| `0` | SUCCESS | No changes or no destructive operations detected |
|
|
371
|
+
| `1` | VALIDATION_ERROR | Invalid DSL, config errors, missing files, or operation declined (e.g. with `--safe`) |
|
|
372
|
+
| `2` | DRIFT_DETECTED | Drift between expected state and live database schema |
|
|
373
|
+
| `3` | CI_DESTRUCTIVE | Destructive operations detected in CI without `--force` |
|
|
374
|
+
|
|
375
|
+
**Per-command exit codes:**
|
|
273
376
|
|
|
274
|
-
|
|
377
|
+
| Command | Possible exit codes |
|
|
378
|
+
| ------- | -------------------- |
|
|
379
|
+
| `validate` | 0, 1, 2, 3 |
|
|
380
|
+
| `doctor` | 0, 2 |
|
|
381
|
+
| `diff`, `generate` | 0, 1, 3 |
|
|
382
|
+
| `init`, `import` | 0 |
|
|
275
383
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
| `1` | General error - validation failed, operation declined, missing files, etc. |
|
|
280
|
-
| `2` | Schema validation error - invalid DSL syntax or structure |
|
|
281
|
-
| `3` | **CI Destructive** - destructive operations detected in CI environment without `--force` |
|
|
384
|
+
(Global CLI errors, e.g. unknown command or missing config, exit with 1.)
|
|
385
|
+
|
|
386
|
+
A machine-readable exit code contract is available at [docs/exit-codes.json](docs/exit-codes.json). It includes a `version` field and an optional `commands` map for tooling.
|
|
282
387
|
|
|
283
388
|
### Destructive Operations in CI
|
|
284
389
|
|
|
@@ -312,6 +417,19 @@ When `CI=true`, SchemaForge will:
|
|
|
312
417
|
- ✅ Allow explicit override with `--force` flag
|
|
313
418
|
- ❌ Not accept user input for confirmation
|
|
314
419
|
|
|
420
|
+
### Drift Integration Tests in CI
|
|
421
|
+
|
|
422
|
+
For drift reliability checks against a real database, run:
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
npm run test:integration:drift
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The integration harness supports two deterministic paths:
|
|
429
|
+
|
|
430
|
+
- `SF_USE_CI_POSTGRES=true` + `DATABASE_URL`: uses the CI Postgres service directly.
|
|
431
|
+
- No CI Postgres env: spins up an isolated Testcontainers Postgres instance.
|
|
432
|
+
|
|
315
433
|
### Using `--safe` in CI
|
|
316
434
|
|
|
317
435
|
The `--safe` flag is compatible with CI and blocks execution of destructive operations:
|
|
@@ -323,6 +441,20 @@ schema-forge generate --safe
|
|
|
323
441
|
|
|
324
442
|
This is useful for strict CI pipelines where all destructive changes must be reviewed and merged separately.
|
|
325
443
|
|
|
444
|
+
### Using exit codes in CI
|
|
445
|
+
|
|
446
|
+
In CI, rely on the process exit code to fail the job when validation or safety checks fail. Example with a shell script:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
schema-forge validate --json
|
|
450
|
+
if [ $? -ne 0 ]; then
|
|
451
|
+
echo "Schema validation failed (exit code: $?)"
|
|
452
|
+
exit 1
|
|
453
|
+
fi
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
When using the [schema-forge-action](https://github.com/xubylele/schema-forge-action), the action passes through the CLI exit code: a non-zero exit from Schema Forge fails the job with that same code, so no extra script is needed.
|
|
457
|
+
|
|
326
458
|
## Constraint Change Detection
|
|
327
459
|
|
|
328
460
|
SchemaForge detects and generates migrations for:
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
interface DiffOptions {
|
|
2
|
+
safe?: boolean;
|
|
3
|
+
force?: boolean;
|
|
4
|
+
url?: string;
|
|
5
|
+
schema?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface DoctorOptions {
|
|
9
|
+
json?: boolean;
|
|
10
|
+
url?: string;
|
|
11
|
+
schema?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface GenerateOptions {
|
|
15
|
+
name?: string;
|
|
16
|
+
safe?: boolean;
|
|
17
|
+
force?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ImportOptions {
|
|
21
|
+
out?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface IntrospectOptions {
|
|
25
|
+
url?: string;
|
|
26
|
+
schema?: string;
|
|
27
|
+
json?: boolean;
|
|
28
|
+
out?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ValidateOptions {
|
|
32
|
+
json?: boolean;
|
|
33
|
+
url?: string;
|
|
34
|
+
schema?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Exit codes used throughout the CLI for deterministic behavior
|
|
39
|
+
*
|
|
40
|
+
* @see SF-106 Standardize Exit Codes
|
|
41
|
+
*/
|
|
42
|
+
declare const EXIT_CODES: {
|
|
43
|
+
/** Successful operation */
|
|
44
|
+
readonly SUCCESS: 0;
|
|
45
|
+
/** Validation error (invalid DSL syntax, config errors, missing files, etc.) */
|
|
46
|
+
readonly VALIDATION_ERROR: 1;
|
|
47
|
+
/** Drift detected - Reserved for future use when comparing actual DB state vs schema */
|
|
48
|
+
readonly DRIFT_DETECTED: 2;
|
|
49
|
+
/** Destructive operation detected in CI environment without --force */
|
|
50
|
+
readonly CI_DESTRUCTIVE: 3;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Programmatic API for Schema Forge.
|
|
55
|
+
* Use this entrypoint when integrating from Node (e.g. scripts, GitHub Actions)
|
|
56
|
+
* instead of invoking the CLI via shell.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const { generate, EXIT_CODES } = require('@xubylele/schema-forge/api');
|
|
60
|
+
* const result = await generate({ name: 'MyMigration' });
|
|
61
|
+
* if (result.exitCode !== EXIT_CODES.SUCCESS) process.exit(result.exitCode);
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Result of a programmatic command run. Exit codes match the CLI contract.
|
|
66
|
+
* @see docs/exit-codes.json
|
|
67
|
+
*/
|
|
68
|
+
interface RunResult {
|
|
69
|
+
exitCode: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Initialize a new schema project in the current directory.
|
|
73
|
+
*/
|
|
74
|
+
declare function init(): Promise<RunResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Generate SQL migration from schema files.
|
|
77
|
+
*/
|
|
78
|
+
declare function generate(options?: GenerateOptions): Promise<RunResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Compare two schema versions and generate migration SQL (optionally against live DB).
|
|
81
|
+
*/
|
|
82
|
+
declare function diff(options?: DiffOptions): Promise<RunResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Check live database drift against state.
|
|
85
|
+
*/
|
|
86
|
+
declare function doctor(options?: DoctorOptions): Promise<RunResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Validate schema and optionally check for destructive changes or live drift.
|
|
89
|
+
*/
|
|
90
|
+
declare function validate(options?: ValidateOptions): Promise<RunResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Extract normalized live schema from PostgreSQL.
|
|
93
|
+
*/
|
|
94
|
+
declare function introspect(options?: IntrospectOptions): Promise<RunResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Import schema from SQL migrations.
|
|
97
|
+
* @param inputPath - Path to .sql file or migrations directory
|
|
98
|
+
*/
|
|
99
|
+
declare function importSchema(inputPath: string, options?: ImportOptions): Promise<RunResult>;
|
|
100
|
+
|
|
101
|
+
export { type DiffOptions, type DoctorOptions, EXIT_CODES, type GenerateOptions, type ImportOptions, type IntrospectOptions, type RunResult, type ValidateOptions, diff, doctor, generate, importSchema, init, introspect, validate };
|