pgroll 0.0.9 → 0.0.10
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 +17 -13
- package/dist/src/cli.js +5 -4
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +17 -20
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -94,12 +94,13 @@ pgroll [global options] <command>
|
|
|
94
94
|
|
|
95
95
|
### Global options
|
|
96
96
|
|
|
97
|
-
| Option | Description
|
|
98
|
-
| --------------------------- |
|
|
99
|
-
| `-d, --migrationDir <path>` | Directory holding the migration files (default `./migrations`).
|
|
100
|
-
| `-u, --url <url>` | PostgreSQL connection URL (overrides `PG*` env vars).
|
|
101
|
-
| `-
|
|
102
|
-
| `-
|
|
97
|
+
| Option | Description |
|
|
98
|
+
| --------------------------- | ------------------------------------------------------------------ |
|
|
99
|
+
| `-d, --migrationDir <path>` | Directory holding the migration files (default: `./migrations`). |
|
|
100
|
+
| `-u, --url <url>` | PostgreSQL connection URL (overrides `PG*` env vars). |
|
|
101
|
+
| `-s, --schema <schema>` | Schema for pgroll's internal migrations table (default: `public`). |
|
|
102
|
+
| `-V, --version` | Print the `pgroll` version. |
|
|
103
|
+
| `-h, --help` | Show help. |
|
|
103
104
|
|
|
104
105
|
### Commands
|
|
105
106
|
|
|
@@ -159,12 +160,13 @@ await migrator.go(0, { eventHandler: info => console.log(info) });
|
|
|
159
160
|
await sql.end();
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
### `new Migrator(dbClient, migrationsDir?)`
|
|
163
|
+
### `new Migrator(dbClient, migrationsDir?, schema?)`
|
|
163
164
|
|
|
164
|
-
| Parameter | Type | Description
|
|
165
|
-
| --------------- | ------------------- |
|
|
166
|
-
| `dbClient` | `Sql` (PostgresJS) | A `postgres` client instance.
|
|
167
|
-
| `migrationsDir` | `string` (optional) | Directory of migration files. Defaults to `<cwd>/migrations`.
|
|
165
|
+
| Parameter | Type | Description |
|
|
166
|
+
| --------------- | ------------------- | -------------------------------------------------------------------- |
|
|
167
|
+
| `dbClient` | `Sql` (PostgresJS) | A `postgres` client instance. |
|
|
168
|
+
| `migrationsDir` | `string` (optional) | Directory of migration files. Defaults to `<cwd>/migrations`. |
|
|
169
|
+
| `schema` | `string` (optional) | Schema for pgroll's internal migrations table. Defaults to `public`. |
|
|
168
170
|
|
|
169
171
|
### Methods
|
|
170
172
|
|
|
@@ -180,10 +182,12 @@ human-readable message as each migration is applied.
|
|
|
180
182
|
|
|
181
183
|
## How it works
|
|
182
184
|
|
|
183
|
-
On the first run, `pgroll` creates a bookkeeping table
|
|
185
|
+
On the first run, `pgroll` creates a bookkeeping table in the configured schema (default
|
|
186
|
+
`public`, override with `-s, --schema`). All references to it are schema-qualified, so it is
|
|
187
|
+
unaffected by any `search_path` a migration sets:
|
|
184
188
|
|
|
185
189
|
```sql
|
|
186
|
-
CREATE TABLE IF NOT EXISTS migrations (
|
|
190
|
+
CREATE TABLE IF NOT EXISTS <schema>.migrations (
|
|
187
191
|
name varchar(500) PRIMARY KEY,
|
|
188
192
|
version smallint NOT NULL,
|
|
189
193
|
applied_at timestamp DEFAULT CURRENT_TIMESTAMP
|
package/dist/src/cli.js
CHANGED
|
@@ -10,8 +10,9 @@ let migrator;
|
|
|
10
10
|
program
|
|
11
11
|
.version('0.0.9')
|
|
12
12
|
.description('Database migration tool')
|
|
13
|
-
.option('-d, --migrationDir <filepath>', 'Specify migration directory(Default: ./migrations)')
|
|
13
|
+
.option('-d, --migrationDir <filepath>', 'Specify migration directory (Default: ./migrations)')
|
|
14
14
|
.option('-u, --url <url>', 'PostgreSQL connection URL (overrides PG* env vars)')
|
|
15
|
+
.option('-s, --schema <schema>', 'Specify schema (Default: public)')
|
|
15
16
|
.hook('preAction', cmd => {
|
|
16
17
|
const opts = cmd.opts();
|
|
17
18
|
const pgOptions = {
|
|
@@ -19,7 +20,7 @@ program
|
|
|
19
20
|
// do nothing
|
|
20
21
|
}
|
|
21
22
|
};
|
|
22
|
-
migrator = new Migrator(opts.url ? postgres(opts.url, pgOptions) : postgres(pgOptions), opts.migrationDir);
|
|
23
|
+
migrator = new Migrator(opts.url ? postgres(opts.url, pgOptions) : postgres(pgOptions), opts.migrationDir, opts.schema);
|
|
23
24
|
});
|
|
24
25
|
program
|
|
25
26
|
.command('up')
|
|
@@ -64,8 +65,8 @@ program
|
|
|
64
65
|
.description('Navigate to a specific version; version 0 performs a rollback, reverting all migrations.')
|
|
65
66
|
.argument('<version>', 'version to migrate to')
|
|
66
67
|
.action(async (version) => {
|
|
67
|
-
const parsedVersion = Number
|
|
68
|
-
if (Number.
|
|
68
|
+
const parsedVersion = Number(version);
|
|
69
|
+
if (!Number.isSafeInteger(parsedVersion) || parsedVersion < 0) {
|
|
69
70
|
console.error('Invalid version number.');
|
|
70
71
|
process.exit(1);
|
|
71
72
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface Option {
|
|
|
5
5
|
}
|
|
6
6
|
export interface IMigrator {
|
|
7
7
|
migrationsDir: string;
|
|
8
|
+
schema: string;
|
|
8
9
|
up: (opts?: Option) => Promise<void>;
|
|
9
10
|
down: (opts?: Option) => Promise<void>;
|
|
10
11
|
go: (version: number, opts?: Option) => Promise<void>;
|
|
@@ -13,7 +14,8 @@ export interface IMigrator {
|
|
|
13
14
|
export declare class Migrator implements IMigrator {
|
|
14
15
|
private readonly dbClient;
|
|
15
16
|
readonly migrationsDir: string;
|
|
16
|
-
|
|
17
|
+
readonly schema: string;
|
|
18
|
+
constructor(dbClient: Sql, migrationsDir?: string, schema?: string);
|
|
17
19
|
ensureMigrationTable(tx: ReservedSql): Promise<void>;
|
|
18
20
|
up(): Promise<void>;
|
|
19
21
|
down(): Promise<void>;
|
package/dist/src/index.js
CHANGED
|
@@ -4,12 +4,15 @@ import { getMigrationFiles } from "./utils.js";
|
|
|
4
4
|
export class Migrator {
|
|
5
5
|
dbClient;
|
|
6
6
|
migrationsDir;
|
|
7
|
-
|
|
7
|
+
schema;
|
|
8
|
+
constructor(dbClient, migrationsDir = '', schema = 'public') {
|
|
8
9
|
this.dbClient = dbClient;
|
|
9
10
|
this.migrationsDir = migrationsDir || `${process.cwd()}/migrations`;
|
|
11
|
+
this.schema = schema;
|
|
10
12
|
}
|
|
11
13
|
async ensureMigrationTable(tx) {
|
|
12
|
-
await tx `CREATE
|
|
14
|
+
await tx `CREATE SCHEMA IF NOT EXISTS ${tx(this.schema)}`;
|
|
15
|
+
await tx `CREATE TABLE IF NOT EXISTS ${tx(this.schema)}.migrations(
|
|
13
16
|
name varchar(500) PRIMARY KEY,
|
|
14
17
|
version smallint NOT NULL,
|
|
15
18
|
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`;
|
|
@@ -29,6 +32,7 @@ export class Migrator {
|
|
|
29
32
|
const currentVersion = await this.getCurrentVersionWithTx(tx);
|
|
30
33
|
if (currentVersion === version) {
|
|
31
34
|
opts?.eventHandler(`Already at version ${version}`);
|
|
35
|
+
await this.commit(tx);
|
|
32
36
|
return;
|
|
33
37
|
}
|
|
34
38
|
const direction = version > currentVersion ? 'up' : 'down';
|
|
@@ -37,10 +41,8 @@ export class Migrator {
|
|
|
37
41
|
const fileVersion = Math.min(fileNames.length, version);
|
|
38
42
|
for (let i = currentVersion; i < fileVersion; i++) {
|
|
39
43
|
const file = fileNames[i] ?? '';
|
|
40
|
-
await
|
|
41
|
-
|
|
42
|
-
tx `INSERT INTO migrations(name, version) VALUES (${file}, ${i} + 1)`
|
|
43
|
-
]);
|
|
44
|
+
await tx.file(path.join(this.migrationsDir, file)).execute();
|
|
45
|
+
await tx `INSERT INTO ${tx(this.schema)}.migrations(name, version) VALUES (${file}, ${i} + 1)`;
|
|
44
46
|
opts?.eventHandler(`Successfully migrated: ${file}`);
|
|
45
47
|
}
|
|
46
48
|
if (version > fileNames.length) {
|
|
@@ -54,10 +56,8 @@ export class Migrator {
|
|
|
54
56
|
const end = start + (currentVersion - version);
|
|
55
57
|
for (let i = start; i < end; i++) {
|
|
56
58
|
const file = fileNames[i] ?? '';
|
|
57
|
-
await
|
|
58
|
-
|
|
59
|
-
tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
|
|
60
|
-
]);
|
|
59
|
+
await tx.file(path.join(this.migrationsDir, file)).execute();
|
|
60
|
+
await tx `DELETE FROM ${tx(this.schema)}.migrations WHERE version = ${fileNames.length - i}`;
|
|
61
61
|
opts?.eventHandler(`Successfully migrated: ${file}`);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -84,10 +84,8 @@ export class Migrator {
|
|
|
84
84
|
for (const fileName of fileNames) {
|
|
85
85
|
const id = fileNames.indexOf(fileName);
|
|
86
86
|
if (id >= currentVersion) {
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
tx `INSERT INTO migrations(name, version) VALUES (${fileName}, ${id} + 1)`
|
|
90
|
-
]);
|
|
87
|
+
await tx.file(path.join(this.migrationsDir, fileName)).execute();
|
|
88
|
+
await tx `INSERT INTO ${tx(this.schema)}.migrations(name, version) VALUES (${fileName}, ${id} + 1)`;
|
|
91
89
|
opts?.eventHandler(`Successfully migrated: ${fileName}`);
|
|
92
90
|
}
|
|
93
91
|
}
|
|
@@ -97,10 +95,8 @@ export class Migrator {
|
|
|
97
95
|
const start = fileNames.length - currentVersion;
|
|
98
96
|
for (let i = start; i < fileNames.length; i++) {
|
|
99
97
|
const file = fileNames[i] ?? '';
|
|
100
|
-
await
|
|
101
|
-
|
|
102
|
-
tx `DELETE FROM migrations WHERE version = ${fileNames.length - i}`
|
|
103
|
-
]);
|
|
98
|
+
await tx.file(path.join(this.migrationsDir, file)).execute();
|
|
99
|
+
await tx `DELETE FROM ${tx(this.schema)}.migrations WHERE version = ${fileNames.length - i}`;
|
|
104
100
|
opts?.eventHandler(`Successfully migrated: ${file}`);
|
|
105
101
|
}
|
|
106
102
|
}
|
|
@@ -116,11 +112,12 @@ export class Migrator {
|
|
|
116
112
|
}
|
|
117
113
|
}
|
|
118
114
|
async getCurrentVersion() {
|
|
119
|
-
const result = await this
|
|
115
|
+
const result = await this
|
|
116
|
+
.dbClient `SELECT version FROM ${this.dbClient(this.schema)}.migrations ORDER BY version DESC LIMIT 1`;
|
|
120
117
|
return result.length > 0 ? result[0]?.['version'] : 0;
|
|
121
118
|
}
|
|
122
119
|
async getCurrentVersionWithTx(tx) {
|
|
123
|
-
const result = await tx `SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
|
|
120
|
+
const result = await tx `SELECT version FROM ${tx(this.schema)}.migrations ORDER BY version DESC LIMIT 1`;
|
|
124
121
|
return result.length > 0 ? result[0]?.['version'] : 0;
|
|
125
122
|
}
|
|
126
123
|
async acquireLock(tx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgroll",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Postgres migration tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -50,24 +50,24 @@
|
|
|
50
50
|
"license": "ISC",
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@eslint/js": "^10.0.1",
|
|
53
|
-
"@types/node": "^
|
|
54
|
-
"@vitest/coverage-v8": "^4.1.
|
|
55
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
56
|
-
"eslint": "^10.
|
|
53
|
+
"@types/node": "^26.0.1",
|
|
54
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
55
|
+
"@vitest/eslint-plugin": "^1.6.20",
|
|
56
|
+
"eslint": "^10.5.0",
|
|
57
57
|
"eslint-config-prettier": "^10.1.8",
|
|
58
|
-
"eslint-import-resolver-typescript": "^4.4.
|
|
59
|
-
"eslint-plugin-import-x": "^4.
|
|
58
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
59
|
+
"eslint-plugin-import-x": "^4.17.0",
|
|
60
60
|
"eslint-plugin-promise": "^7.3.0",
|
|
61
|
-
"eslint-plugin-sonarjs": "^4.0
|
|
62
|
-
"eslint-plugin-unicorn": "^
|
|
63
|
-
"globals": "^17.
|
|
64
|
-
"prettier": "^3.8.
|
|
61
|
+
"eslint-plugin-sonarjs": "^4.1.0",
|
|
62
|
+
"eslint-plugin-unicorn": "^69.0.0",
|
|
63
|
+
"globals": "^17.7.0",
|
|
64
|
+
"prettier": "^3.8.4",
|
|
65
65
|
"typescript": "^6.0.3",
|
|
66
|
-
"typescript-eslint": "^8.
|
|
67
|
-
"vitest": "^4.1.
|
|
66
|
+
"typescript-eslint": "^8.62.0",
|
|
67
|
+
"vitest": "^4.1.9"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"commander": "^
|
|
70
|
+
"commander": "^15.0.0",
|
|
71
71
|
"postgres": "^3.4.9"
|
|
72
72
|
}
|
|
73
73
|
}
|