pg-here 0.1.8 → 0.1.9
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 +41 -315
- package/bin/pg-here.mjs +10 -1
- package/package.json +1 -1
- package/scripts/cli-error-help.mjs +122 -2
- package/scripts/pg-dev.mjs +9 -2
package/README.md
CHANGED
|
@@ -1,366 +1,92 @@
|
|
|
1
1
|
# pg-here
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run a local PostgreSQL instance in your project folder with one command.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Project-Local PostgreSQL Setup
|
|
8
|
-
|
|
9
|
-
Each project runs its own isolated PostgreSQL instance. Downloads correct binary automatically for your CPU architecture (x86_64 / arm64). Database lives inside the project folder.
|
|
10
|
-
|
|
11
|
-
### Quick Start
|
|
12
|
-
|
|
13
|
-
Start PostgreSQL:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
bun run db:up
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Output:
|
|
20
|
-
```
|
|
21
|
-
postgresql://postgres:postgres@localhost:55432/postgres
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Connect from your app using that connection string, then Ctrl+C to stop.
|
|
25
|
-
|
|
26
|
-
### One-command start from npm (no local install)
|
|
27
|
-
|
|
28
|
-
If you have Bun installed, you can run the published package directly:
|
|
5
|
+
## 30-second start
|
|
29
6
|
|
|
30
7
|
```bash
|
|
31
8
|
bunx pg-here
|
|
32
9
|
```
|
|
33
10
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
If it still resolves an older release, the package `latest` dist-tag may be behind:
|
|
37
|
-
|
|
38
|
-
To force that version and bypass a stale cache:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
bunx pg-here@0.1.8
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
If maintainers want plain `bunx pg-here` to work for everyone, run once:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
npm dist-tag add pg-here@0.1.8 latest
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
After that, this should work anywhere:
|
|
11
|
+
Default output:
|
|
51
12
|
|
|
52
|
-
```
|
|
53
|
-
|
|
13
|
+
```text
|
|
14
|
+
Launching PostgreSQL 18.0.0 into new pg_local/
|
|
15
|
+
psql postgresql://postgres:postgres@localhost:55432/postgres
|
|
54
16
|
```
|
|
55
17
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`bunx pg-here` uses these defaults if you pass nothing:
|
|
59
|
-
- `username=postgres`
|
|
60
|
-
- `password=postgres`
|
|
61
|
-
- `database=postgres`
|
|
62
|
-
- `port=55432`
|
|
63
|
-
|
|
64
|
-
All args are optional.
|
|
18
|
+
If a data folder already exists:
|
|
65
19
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
bunx pg-here --username postgres --password postgres --database my_app --port 55432
|
|
20
|
+
```text
|
|
21
|
+
Reusing existing pg_local/data/ with PostgreSQL 18.0.0
|
|
22
|
+
psql postgresql://postgres:postgres@localhost:55432/postgres
|
|
70
23
|
```
|
|
71
24
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
If `bunx pg-here` fails with `error while loading shared libraries`:
|
|
75
|
-
- install missing system packages on the host (not in npm), then retry
|
|
76
|
-
- restart the command
|
|
77
|
-
|
|
78
|
-
Most commonly:
|
|
25
|
+
If the cached folder version differs:
|
|
79
26
|
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# Fedora/RHEL
|
|
85
|
-
sudo dnf install -y libxml2
|
|
86
|
-
|
|
87
|
-
# Alpine
|
|
88
|
-
sudo apk add libxml2
|
|
27
|
+
```text
|
|
28
|
+
Reusing existing pg_local/data/ (pg_local/bin has 18.0.0, running PostgreSQL is 18.0)
|
|
29
|
+
psql postgresql://postgres:postgres@localhost:55432/postgres
|
|
89
30
|
```
|
|
90
31
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
If apt says `Package 'libxml2' has no installation candidate`, your apt sources are likely missing standard repos.
|
|
32
|
+
The process stays alive until you stop it.
|
|
33
|
+
Ctrl+C → exits and stops Postgres.
|
|
94
34
|
|
|
95
|
-
|
|
96
|
-
cat /etc/os-release
|
|
97
|
-
apt-cache search '^libxml2$'
|
|
98
|
-
```
|
|
35
|
+
## Defaults (all args optional)
|
|
99
36
|
|
|
100
|
-
|
|
37
|
+
`bunx pg-here`
|
|
101
38
|
|
|
102
|
-
|
|
39
|
+
- `username = postgres`
|
|
40
|
+
- `password = postgres`
|
|
41
|
+
- `database = postgres`
|
|
42
|
+
- `port = 55432`
|
|
43
|
+
- `pg-version` = auto
|
|
103
44
|
|
|
104
|
-
|
|
105
|
-
- If a failure mentions `/pg_local/bin/bin/postgres`, clear and redownload:
|
|
45
|
+
## Custom run
|
|
106
46
|
|
|
107
47
|
```bash
|
|
108
|
-
|
|
109
|
-
bunx pg-here@0.1.8
|
|
48
|
+
bunx pg-here --username me --password secret --database my_app --port 55433 --pg-version 17.0.0
|
|
110
49
|
```
|
|
111
50
|
|
|
112
|
-
|
|
113
|
-
`libxml2.so.16`-only hosts:
|
|
114
|
-
- it creates a local runtime symlink in `./pg_local/runtime-libs/libxml2.so.2` pointing to the discovered `libxml2.so.16`
|
|
115
|
-
- sets `LD_LIBRARY_PATH` for the retry so `postgres`/`initdb` can launch
|
|
116
|
-
|
|
117
|
-
If your system is locked down and this still fails, create a global compatibility symlink (where permitted):
|
|
51
|
+
You can also run locally in this repo:
|
|
118
52
|
|
|
119
53
|
```bash
|
|
120
|
-
|
|
121
|
-
sudo ldconfig
|
|
54
|
+
bun run db:up
|
|
122
55
|
```
|
|
123
56
|
|
|
124
|
-
|
|
57
|
+
Same CLI flags are supported.
|
|
125
58
|
|
|
126
|
-
|
|
59
|
+
## Programmatic
|
|
127
60
|
|
|
128
61
|
```ts
|
|
129
62
|
import { startPgHere } from "pg-here";
|
|
130
63
|
|
|
131
|
-
const
|
|
64
|
+
const pg = await startPgHere({
|
|
132
65
|
projectDir: process.cwd(),
|
|
133
|
-
port: 55432,
|
|
134
66
|
database: "my_app",
|
|
135
67
|
createDatabaseIfMissing: true,
|
|
136
|
-
postgresVersion: "18.0.0",
|
|
137
68
|
});
|
|
138
69
|
|
|
139
|
-
console.log(
|
|
140
|
-
|
|
141
|
-
// On shutdown:
|
|
142
|
-
await pgHere.stop();
|
|
70
|
+
console.log(pg.databaseConnectionString); // psql-ready URL
|
|
71
|
+
await pg.stop();
|
|
143
72
|
```
|
|
144
73
|
|
|
145
|
-
|
|
146
|
-
If the DB does not exist yet, `createDatabaseIfMissing: true` creates it on startup.
|
|
147
|
-
Set `postgresVersion` if you want to pin/select a specific PostgreSQL version.
|
|
148
|
-
By default, `startPgHere()` installs SIGINT/SIGTERM shutdown hooks that stop Postgres when
|
|
149
|
-
your process exits, and `stop()` preserves data (no cluster cleanup/delete).
|
|
150
|
-
Use `await pgHere.cleanup()` only when you explicitly want full resource cleanup.
|
|
151
|
-
`pg_stat_statements` is enabled automatically (`shared_preload_libraries` + extension creation).
|
|
152
|
-
Set `enablePgStatStatements: false` to opt out.
|
|
74
|
+
## Linux runtime error (quick fix)
|
|
153
75
|
|
|
154
|
-
|
|
76
|
+
If startup fails with missing `libxml2` libraries, install runtime packages and retry:
|
|
155
77
|
|
|
156
78
|
```bash
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
# Short flags
|
|
161
|
-
bun run db:up -u postgres -p postgres
|
|
162
|
-
|
|
163
|
-
# Custom port
|
|
164
|
-
bun run db:up --port 55433
|
|
165
|
-
|
|
166
|
-
# All together
|
|
167
|
-
bun run db:up -u postgres -p postgres --database postgres --port 55433
|
|
168
|
-
|
|
169
|
-
# Pin postgres version
|
|
170
|
-
bun run db:up --pg-version 18.0.0
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
**Defaults**: username=`postgres`, password=`postgres`, port=`55432`, pg-version=`PG_VERSION` or pg-embedded default
|
|
174
|
-
|
|
175
|
-
### Project Structure
|
|
176
|
-
|
|
177
|
-
```
|
|
178
|
-
project/
|
|
179
|
-
pg_local/
|
|
180
|
-
data/ # PostgreSQL data cluster (persists between runs)
|
|
181
|
-
bin/ # Downloaded PostgreSQL binaries
|
|
182
|
-
scripts/
|
|
183
|
-
pg-dev.mjs # Runner script
|
|
184
|
-
package.json
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### How It Works
|
|
188
|
-
|
|
189
|
-
- PostgreSQL only runs when you execute `bun run db:up`
|
|
190
|
-
- Correct architecture binary downloads automatically on first run
|
|
191
|
-
- Data persists in `pg_local/data/` across restarts
|
|
192
|
-
- Process stops completely on exit (Ctrl+C)
|
|
193
|
-
- One instance per project, no system PostgreSQL dependency
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Use this from another project (recommended)
|
|
198
|
-
|
|
199
|
-
Keep this repo in one place, and point it at any other project directory when you want a dedicated Postgres instance there.
|
|
200
|
-
|
|
201
|
-
Example: your app lives at `/path/to/my-app`, but this repo lives elsewhere.
|
|
202
|
-
|
|
203
|
-
```
|
|
204
|
-
# start postgres for that project (one-time init happens automatically)
|
|
205
|
-
bun run snapshot snapshot /path/to/my-app/.pg-here
|
|
206
|
-
|
|
207
|
-
# list snapshots for that project
|
|
208
|
-
bun run snapshot list /path/to/my-app/.pg-here
|
|
209
|
-
|
|
210
|
-
# revert that project to a snapshot
|
|
211
|
-
bun run revert /path/to/my-app/.pg-here snap_YYYYMMDD_HHMMSS
|
|
79
|
+
sudo apt-get update && sudo apt-get install -y libxml2 libxml2-utils
|
|
80
|
+
sudo dnf install -y libxml2
|
|
81
|
+
sudo apk add libxml2
|
|
212
82
|
```
|
|
213
83
|
|
|
214
|
-
|
|
215
|
-
- Pick a per-project folder (e.g. `.pg-here`) and reuse it.
|
|
216
|
-
- The project directory just needs to be on the same APFS volume for clones to be fast.
|
|
217
|
-
- You can also pass the directory with `--project/-p` instead of positional.
|
|
218
|
-
|
|
219
|
-
To install dependencies:
|
|
84
|
+
This release already retries startup with a project-local `libxml2` compatibility fallback when needed.
|
|
220
85
|
|
|
221
|
-
|
|
222
|
-
bun install
|
|
223
|
-
```
|
|
86
|
+
## Version pin / stale cache
|
|
224
87
|
|
|
225
|
-
|
|
88
|
+
If your environment keeps resolving an older release, force a specific version:
|
|
226
89
|
|
|
227
90
|
```bash
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
|
232
|
-
|
|
233
|
-
## APFS clone snapshots (macOS) — insanely simple
|
|
234
|
-
|
|
235
|
-
### The 30‑second version
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
# take a snapshot for the default project directory
|
|
239
|
-
bun run snapshot snapshot
|
|
240
|
-
|
|
241
|
-
# list snapshots
|
|
242
|
-
bun run snapshot list
|
|
243
|
-
|
|
244
|
-
# revert to a snapshot (copy from list output)
|
|
245
|
-
bun run snapshot revert snap_YYYYMMDD_HHMMSS
|
|
246
|
-
|
|
247
|
-
# or use the alias
|
|
248
|
-
bun run revert snap_YYYYMMDD_HHMMSS
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
By default, snapshots live under `./pg_projects/default`. You can pass a project directory explicitly if you want.
|
|
252
|
-
|
|
253
|
-
### What this does
|
|
254
|
-
|
|
255
|
-
- Uses APFS copy‑on‑write clones (`cp -cR` / `ditto --clone`)
|
|
256
|
-
- Stops Postgres during snapshot/revert (cold stop/start)
|
|
257
|
-
- Keeps snapshots immutable and restores into new instances
|
|
258
|
-
|
|
259
|
-
### Why it's great
|
|
260
|
-
|
|
261
|
-
- Near‑instant snapshot and revert via copy‑on‑write
|
|
262
|
-
- Space grows only with changed blocks
|
|
263
|
-
- No volume‑wide rollback
|
|
264
|
-
- No WAL/PITR complexity
|
|
265
|
-
- macOS‑native, zero extra services
|
|
266
|
-
- Deterministic failure modes
|
|
267
|
-
|
|
268
|
-
### Operational constraints
|
|
269
|
-
|
|
270
|
-
- PostgreSQL must be stopped for snapshot/revert
|
|
271
|
-
- Source and destination must be on the same APFS volume
|
|
272
|
-
- One cluster per project
|
|
273
|
-
|
|
274
|
-
### Under the hood layout
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
~/pg/proj/
|
|
278
|
-
current -> instances/inst_active
|
|
279
|
-
instances/
|
|
280
|
-
snaps/
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Full commands (helper script)
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
# snapshot current cluster
|
|
287
|
-
bun run snapshot snapshot /path/to/project
|
|
288
|
-
|
|
289
|
-
# list snapshots
|
|
290
|
-
bun run snapshot list /path/to/project
|
|
291
|
-
|
|
292
|
-
# revert to a snapshot
|
|
293
|
-
bun run snapshot revert /path/to/project snap_YYYYMMDD_HHMMSS
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
Flags (optional):
|
|
297
|
-
|
|
298
|
-
```
|
|
299
|
-
--project/-p project directory (same as positional projectDir)
|
|
300
|
-
--snap/-s snapshot name (for revert)
|
|
301
|
-
--pg-ctl path to pg_ctl (overrides PG_CTL)
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
If `pg_ctl` isn't on your `PATH`, set `PG_CTL`. By default the script looks in `./pg_local/bin/*/bin/pg_ctl`.
|
|
305
|
-
|
|
306
|
-
### One‑shot test (does everything end‑to‑end)
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
bun run snapshot:test
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
This:
|
|
313
|
-
1) Starts a temporary cluster
|
|
314
|
-
2) Writes sample data
|
|
315
|
-
3) Snapshots
|
|
316
|
-
4) Mutates data
|
|
317
|
-
5) Restores
|
|
318
|
-
6) Verifies the original data returns
|
|
319
|
-
|
|
320
|
-
Optional flags:
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
--project/-p project directory (default: ./pg_projects/apfs_test_TIMESTAMP)
|
|
324
|
-
--port postgres port (default: 55433 or PGPORT_SNAPSHOT_TEST)
|
|
325
|
-
--pg-version postgres version (default: PG_VERSION or pg-embedded default)
|
|
326
|
-
--keep keep the project directory after the test
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Bun test integration
|
|
330
|
-
|
|
331
|
-
```
|
|
332
|
-
bun test
|
|
333
|
-
bun run test:apfs
|
|
91
|
+
bunx pg-here@0.1.9
|
|
334
92
|
```
|
|
335
|
-
|
|
336
|
-
Set `SKIP_APFS_TEST=1` to skip the APFS snapshot test.
|
|
337
|
-
|
|
338
|
-
## APFS clone speed benchmark
|
|
339
|
-
|
|
340
|
-
Compares clone time between a small and large dataset by seeding a table and cloning the data directory.
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
bun run bench:apfs
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
Optional flags:
|
|
347
|
-
|
|
348
|
-
```
|
|
349
|
-
--project/-p project directory (default: ./pg_projects/bench)
|
|
350
|
-
--port postgres port (default: 55434 or PGPORT_BENCH)
|
|
351
|
-
--small-rows rows for small dataset (default: 50_000)
|
|
352
|
-
--large-rows rows for large dataset (default: 2_000_000)
|
|
353
|
-
--row-bytes payload bytes per row (default: 256)
|
|
354
|
-
--pg-version postgres version (default: PG_VERSION or pg-embedded default)
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
Example:
|
|
358
|
-
|
|
359
|
-
```
|
|
360
|
-
bun run bench:apfs --small-rows 100000 --large-rows 5000000 --row-bytes 512
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
### When to choose something else
|
|
364
|
-
|
|
365
|
-
- Need online "rewind to 5 minutes ago" repeatedly → base backup + WAL/PITR
|
|
366
|
-
- Dataset ≥ ~50 GB with heavy churn → dedicated APFS volume + volume snapshots, or move DB off the laptop
|
package/bin/pg-here.mjs
CHANGED
|
@@ -5,6 +5,8 @@ import { hideBin } from "yargs/helpers";
|
|
|
5
5
|
import { startPgHere } from "../dist/index.js";
|
|
6
6
|
import {
|
|
7
7
|
maybePrintLinuxRuntimeHelp,
|
|
8
|
+
getPreStartPgHereState,
|
|
9
|
+
printPgHereStartupInfo,
|
|
8
10
|
startPgHereWithLibxml2Compat,
|
|
9
11
|
} from "../scripts/cli-error-help.mjs";
|
|
10
12
|
|
|
@@ -36,6 +38,7 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
36
38
|
.parse();
|
|
37
39
|
|
|
38
40
|
let pg;
|
|
41
|
+
const preStartState = getPreStartPgHereState(process.cwd());
|
|
39
42
|
const startInstance = () =>
|
|
40
43
|
startPgHere({
|
|
41
44
|
projectDir: process.cwd(),
|
|
@@ -55,5 +58,11 @@ try {
|
|
|
55
58
|
throw error;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
printPgHereStartupInfo({
|
|
62
|
+
connectionString: pg.databaseConnectionString,
|
|
63
|
+
instance: pg.instance,
|
|
64
|
+
preStartState,
|
|
65
|
+
requestedVersion: argv["pg-version"],
|
|
66
|
+
});
|
|
67
|
+
|
|
59
68
|
setInterval(() => {}, 1 << 30);
|
package/package.json
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
accessSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
lstatSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readlinkSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
symlinkSync,
|
|
11
|
+
F_OK,
|
|
12
|
+
} from "node:fs";
|
|
3
13
|
import { join } from "node:path";
|
|
4
14
|
|
|
5
15
|
const LIBXML2_SONAME = "libxml2.so.2";
|
|
@@ -16,6 +26,12 @@ const LIB_PATHS = [
|
|
|
16
26
|
"/usr/local/lib",
|
|
17
27
|
];
|
|
18
28
|
|
|
29
|
+
const PG_LOCAL_DIR = "pg_local";
|
|
30
|
+
const PG_LOCAL_DATA_DIR = "data";
|
|
31
|
+
const PG_LOCAL_BIN_DIR = "bin";
|
|
32
|
+
const PG_VERSION_FILE = "PG_VERSION";
|
|
33
|
+
const VERSION_DIR_RE = /^\d+\.\d+(?:\.\d+)?$/;
|
|
34
|
+
|
|
19
35
|
export function maybePrintLinuxRuntimeHelp(error) {
|
|
20
36
|
const message = String(error?.message ?? error);
|
|
21
37
|
const missingLibsFromError = extractMissingLibrariesFromMessage(message);
|
|
@@ -54,7 +70,7 @@ export function maybePrintLinuxRuntimeHelp(error) {
|
|
|
54
70
|
if (binPath.includes("/bin/bin/postgres")) {
|
|
55
71
|
console.error(" Your local pg_local cache looks partially provisioned (bin/bin/postgres path).");
|
|
56
72
|
console.error(" Try removing it and retrying:");
|
|
57
|
-
console.error(" rm -rf pg_local && bunx pg-here@0.1.
|
|
73
|
+
console.error(" rm -rf pg_local && bunx pg-here@0.1.9");
|
|
58
74
|
}
|
|
59
75
|
console.error(
|
|
60
76
|
`If your distro requires different package names, install packages that provide: ${missingLibs.join(", ")}`
|
|
@@ -94,6 +110,91 @@ export function hasLibxml2CompatibilityNeed(error) {
|
|
|
94
110
|
return missing.includes(LIBXML2_SONAME) || message.includes("libxml2.so.2");
|
|
95
111
|
}
|
|
96
112
|
|
|
113
|
+
export function getPreStartPgHereState(projectDir) {
|
|
114
|
+
const normalizedProjectDir = typeof projectDir === "string" && projectDir ? projectDir : process.cwd();
|
|
115
|
+
const dataDir = join(normalizedProjectDir, PG_LOCAL_DIR, PG_LOCAL_DATA_DIR);
|
|
116
|
+
const binDir = join(normalizedProjectDir, PG_LOCAL_DIR, PG_LOCAL_BIN_DIR);
|
|
117
|
+
const hasData = existsSync(dataDir);
|
|
118
|
+
const hasPgVersionFile = existsSync(join(dataDir, PG_VERSION_FILE));
|
|
119
|
+
const installedVersions = getInstalledPostgresVersions(binDir);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
dataDir,
|
|
123
|
+
hasData,
|
|
124
|
+
hasPgVersionFile,
|
|
125
|
+
installedVersions,
|
|
126
|
+
installedVersion: installedVersions[0] ?? "",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function printPgHereStartupInfo({
|
|
131
|
+
connectionString,
|
|
132
|
+
instance,
|
|
133
|
+
preStartState,
|
|
134
|
+
requestedVersion,
|
|
135
|
+
}) {
|
|
136
|
+
const { hasData, installedVersion } = preStartState ?? {};
|
|
137
|
+
const startedVersion = getPostgresInstanceVersion(instance);
|
|
138
|
+
|
|
139
|
+
const displayVersion =
|
|
140
|
+
startedVersion || requestedVersion || "default";
|
|
141
|
+
const dataPath = `${PG_LOCAL_DIR}/${PG_LOCAL_DATA_DIR}/`;
|
|
142
|
+
const firstLine = hasData
|
|
143
|
+
? getExistingDataStatusLine({ installedVersion, startedVersion, requestedVersion, dataPath })
|
|
144
|
+
: `Launching PostgreSQL ${displayVersion} into new ${PG_LOCAL_DIR}/`;
|
|
145
|
+
|
|
146
|
+
console.log(firstLine);
|
|
147
|
+
if (typeof connectionString === "string" && connectionString.length > 0) {
|
|
148
|
+
console.log(`psql ${connectionString}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getExistingDataStatusLine({
|
|
153
|
+
installedVersion,
|
|
154
|
+
startedVersion,
|
|
155
|
+
requestedVersion,
|
|
156
|
+
dataPath,
|
|
157
|
+
}) {
|
|
158
|
+
const runVersion = startedVersion || requestedVersion || "default";
|
|
159
|
+
if (installedVersion && startedVersion && installedVersion !== startedVersion) {
|
|
160
|
+
return `Reusing existing ${dataPath} (pg_local/bin has ${installedVersion}, running PostgreSQL is ${startedVersion})`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (installedVersion && startedVersion && installedVersion === startedVersion) {
|
|
164
|
+
return `Reusing existing ${dataPath} with PostgreSQL ${runVersion}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return `Reusing existing ${dataPath} with PostgreSQL ${runVersion}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getPostgresInstanceVersion(instance) {
|
|
171
|
+
try {
|
|
172
|
+
if (typeof instance?.getPostgreSqlVersion === "function") {
|
|
173
|
+
return instance.getPostgreSqlVersion();
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
return "";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return "";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function compareSemVerDesc(left, right) {
|
|
183
|
+
const leftParts = left.split(".").map((segment) => Number.parseInt(segment, 10) || 0);
|
|
184
|
+
const rightParts = right.split(".").map((segment) => Number.parseInt(segment, 10) || 0);
|
|
185
|
+
const maxLength = Math.max(leftParts.length, rightParts.length);
|
|
186
|
+
for (let i = 0; i < maxLength; i += 1) {
|
|
187
|
+
const leftValue = leftParts[i] ?? 0;
|
|
188
|
+
const rightValue = rightParts[i] ?? 0;
|
|
189
|
+
if (leftValue === rightValue) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
return rightValue - leftValue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
97
198
|
export async function startPgHereWithLibxml2Compat(start, workingDir) {
|
|
98
199
|
try {
|
|
99
200
|
return await start();
|
|
@@ -111,6 +212,25 @@ export async function startPgHereWithLibxml2Compat(start, workingDir) {
|
|
|
111
212
|
}
|
|
112
213
|
}
|
|
113
214
|
|
|
215
|
+
function getInstalledPostgresVersions(baseDir) {
|
|
216
|
+
if (!existsSync(baseDir)) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let entries = [];
|
|
221
|
+
try {
|
|
222
|
+
entries = readdirSync(baseDir, { withFileTypes: true });
|
|
223
|
+
} catch {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return entries
|
|
228
|
+
.filter((entry) => entry.isDirectory() && VERSION_DIR_RE.test(entry.name))
|
|
229
|
+
.map((entry) => entry.name)
|
|
230
|
+
.sort(compareSemVerDesc);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
114
234
|
export function ensureLibxml2Compatibility(workingDir) {
|
|
115
235
|
if (process.platform !== "linux") {
|
|
116
236
|
return false;
|
package/scripts/pg-dev.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import { hideBin } from "yargs/helpers";
|
|
|
3
3
|
import { startPgHere } from "../index.ts";
|
|
4
4
|
import {
|
|
5
5
|
maybePrintLinuxRuntimeHelp,
|
|
6
|
+
getPreStartPgHereState,
|
|
7
|
+
printPgHereStartupInfo,
|
|
6
8
|
startPgHereWithLibxml2Compat,
|
|
7
9
|
} from "./cli-error-help.mjs";
|
|
8
10
|
|
|
@@ -34,6 +36,7 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
34
36
|
.parse();
|
|
35
37
|
|
|
36
38
|
let pg;
|
|
39
|
+
const preStartState = getPreStartPgHereState(process.cwd());
|
|
37
40
|
const startInstance = () =>
|
|
38
41
|
startPgHere({
|
|
39
42
|
projectDir: process.cwd(),
|
|
@@ -53,8 +56,12 @@ try {
|
|
|
53
56
|
throw error;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
printPgHereStartupInfo({
|
|
60
|
+
connectionString: pg.databaseConnectionString,
|
|
61
|
+
instance: pg.instance,
|
|
62
|
+
preStartState,
|
|
63
|
+
requestedVersion: argv["pg-version"],
|
|
64
|
+
});
|
|
58
65
|
|
|
59
66
|
// keep this process alive; Ctrl-C stops postgres
|
|
60
67
|
setInterval(() => {}, 1 << 30);
|