db-drifter-cli 0.3.0 → 0.3.1
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 -10
- package/package.json +1 -1
- package/src/index.js +24 -21
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ node cli/src/index.js --help
|
|
|
18
18
|
```sh
|
|
19
19
|
dbd auth # browser sign-in; auto-selects your workspace (prompts if you have several)
|
|
20
20
|
|
|
21
|
-
dbd use staging # pick the source
|
|
21
|
+
dbd use staging # pick the source DB (auto if the workspace has just one)
|
|
22
22
|
|
|
23
23
|
dbd branch pr-42 # create an isolated, writable database and print its DSN
|
|
24
24
|
# postgresql://you@wire.db-drifter.com/ws1.staging.pr-42?sslmode=require
|
|
@@ -28,17 +28,17 @@ dbd branch rm pr-42 # throw it away
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
`dbd auth` auto-selects your workspace when you have one (and prompts a
|
|
31
|
-
radio list when you have several), then remembers a single source
|
|
32
|
-
`dbd branch <name>` creates an isolated overlay on that source and prints its
|
|
31
|
+
radio list when you have several), then remembers a single source DB.
|
|
32
|
+
`dbd branch <name>` creates an isolated overlay on that source DB and prints its
|
|
33
33
|
connection string — hand it straight to your app, tests or an agent. Run
|
|
34
|
-
`dbd add-source <name> <url>` first if you're an admin connecting a database.
|
|
34
|
+
`dbd add-source-db <name> <url>` first if you're an admin connecting a database.
|
|
35
35
|
|
|
36
36
|
```sh
|
|
37
37
|
dbd conn staging # the `main` branch DSN
|
|
38
38
|
dbd conn staging --branch pr-42
|
|
39
39
|
dbd sql staging "select count(*) from orders"
|
|
40
40
|
dbd branch ls
|
|
41
|
-
dbd use staging # default source
|
|
41
|
+
dbd use staging # default source DB for `dbd branch`
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## All commands
|
|
@@ -46,12 +46,12 @@ dbd use staging # default source database for `dbd br
|
|
|
46
46
|
```
|
|
47
47
|
auth [--username U] [--password P] [--token T] log in (device flow by default)
|
|
48
48
|
logout | whoami
|
|
49
|
-
use <source-db> set the default source
|
|
49
|
+
use <source-db> set the default source DB
|
|
50
50
|
|
|
51
|
-
add-source <name> <url> connect a source
|
|
51
|
+
add-source-db <name> <url> connect a source DB (admin)
|
|
52
52
|
source-db ls | add <name> <url> | rm <name> | reset <name>
|
|
53
53
|
|
|
54
|
-
branch <name> [--source S] [--ttl-days N] create a branch, print its DSN
|
|
54
|
+
branch <name> [--source-db S] [--ttl-days N] create a branch, print its DSN
|
|
55
55
|
branch ls | rm <name>
|
|
56
56
|
|
|
57
57
|
conn <source-db> [--branch <name>] print a connection string
|
|
@@ -95,6 +95,37 @@ as `DATABASE_URL`, and deletes it on PR close. Ephemeral branches also carry a
|
|
|
95
95
|
TTL; the server's nightly cleanup removes expired branches and drops their
|
|
96
96
|
overlays.
|
|
97
97
|
|
|
98
|
-
The token can create/delete branches (scope `branch`) but not add source
|
|
99
|
-
databases (scope `admin`) — CI can provision its own database without being able
|
|
98
|
+
The token can create/delete branches (scope `branch`) but not add source DBs (scope `admin`) — CI can provision its own database without being able
|
|
100
99
|
to widen access.
|
|
100
|
+
|
|
101
|
+
## Releasing
|
|
102
|
+
|
|
103
|
+
The package lives in `cli/` and is published as **`db-drifter-cli`** (it installs
|
|
104
|
+
the `dbd` command). Publishing needs your npm 2FA:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
cd cli
|
|
108
|
+
npm version patch # or minor / major — bumps package.json + tags
|
|
109
|
+
npm publish --otp=<6-digit code>
|
|
110
|
+
git push --follow-tags
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`publishConfig` pins the public registry and `access: public`, so no extra flags
|
|
114
|
+
are needed. After publishing, `latest` follows the highest version — check with:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npm view db-drifter-cli dist-tags
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Sanity-check the tarball before releasing (it should include `src/index.js`):
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npm pack --dry-run
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If you ever need to publish from a repo checkout before npm has it:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
npm i -g ./cli # installs `dbd` from the local folder
|
|
130
|
+
node ./cli/src/index.js --help
|
|
131
|
+
```
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { homedir } from "node:os";
|
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
13
|
|
|
14
14
|
const DEFAULT_API = "https://app.db-drifter.com";
|
|
15
|
-
const VERSION = "0.3.
|
|
15
|
+
const VERSION = "0.3.1";
|
|
16
16
|
|
|
17
17
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
18
18
|
|
|
@@ -137,7 +137,7 @@ async function workspaceDetail(api, ws) {
|
|
|
137
137
|
async function findSourceDb(api, ws, name) {
|
|
138
138
|
const v = await workspaceDetail(api, ws);
|
|
139
139
|
const found = (v?.source_dbs ?? []).find((s) => s.name === name);
|
|
140
|
-
if (!found) throw new Error(`source
|
|
140
|
+
if (!found) throw new Error(`source DB "${name}" not found`);
|
|
141
141
|
return found;
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -148,15 +148,15 @@ async function resolveSourceDb(cfg, api, ws, explicit) {
|
|
|
148
148
|
const v = await workspaceDetail(api, ws);
|
|
149
149
|
const names = (v?.source_dbs ?? []).map((s) => s.name);
|
|
150
150
|
if (names.length === 1) return names[0];
|
|
151
|
-
if (names.length === 0) throw new Error("no source
|
|
151
|
+
if (names.length === 0) throw new Error("no source DB yet; run `dbd add-source-db <name> <url>`");
|
|
152
152
|
const sds = v.source_dbs;
|
|
153
|
-
const picked = await chooseRadio("Select a source
|
|
153
|
+
const picked = await chooseRadio("Select a source DB:", sds, (s) => s.name);
|
|
154
154
|
if (picked) {
|
|
155
155
|
cfg.source_db = picked.name;
|
|
156
156
|
saveConfig(cfg);
|
|
157
157
|
return picked.name;
|
|
158
158
|
}
|
|
159
|
-
throw new Error(`which source
|
|
159
|
+
throw new Error(`which source DB? pass --source-db <name> (available: ${names.join(", ")})`);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
async function connFor(api, ws, sourceDb, branch) {
|
|
@@ -169,6 +169,9 @@ async function connFor(api, ws, sourceDb, branch) {
|
|
|
169
169
|
|
|
170
170
|
const enc = (s) => encodeURIComponent(s);
|
|
171
171
|
|
|
172
|
+
/** The source DB flag: `--source-db` (with `--source` accepted for back-compat). */
|
|
173
|
+
const sourceFlag = (args) => str(args.flags["source-db"]) ?? sourceFlag(args);
|
|
174
|
+
|
|
172
175
|
// ---------------------------------------------------------------------------
|
|
173
176
|
// Args
|
|
174
177
|
// ---------------------------------------------------------------------------
|
|
@@ -293,7 +296,7 @@ async function chooseRadio(question, items, label) {
|
|
|
293
296
|
|
|
294
297
|
/**
|
|
295
298
|
* After login, pick a workspace (auto if there is only one, otherwise prompt)
|
|
296
|
-
* and remember a single source
|
|
299
|
+
* and remember a single source DB when the workspace has exactly one.
|
|
297
300
|
*/
|
|
298
301
|
async function settleContext(cfg, api) {
|
|
299
302
|
try {
|
|
@@ -321,7 +324,7 @@ async function settleContext(cfg, api) {
|
|
|
321
324
|
if (sds.length === 1) {
|
|
322
325
|
cfg.source_db = sds[0].name;
|
|
323
326
|
saveConfig(cfg);
|
|
324
|
-
console.log(`using source
|
|
327
|
+
console.log(`using source DB "${sds[0].name}"`);
|
|
325
328
|
}
|
|
326
329
|
} catch {
|
|
327
330
|
/* best effort — never fail a login over this */
|
|
@@ -410,7 +413,7 @@ async function cmdWorkspace(cfg, args) {
|
|
|
410
413
|
throw new Error("usage: dbd workspace ls | use <id>");
|
|
411
414
|
}
|
|
412
415
|
|
|
413
|
-
/** `dbd source-db …` and the `dbd add-source` / `dbd use` aliases. */
|
|
416
|
+
/** `dbd source-db …` and the `dbd add-source-db` / `dbd use` aliases. */
|
|
414
417
|
async function cmdSourceDb(cfg, args, verb) {
|
|
415
418
|
const api = apiFor(cfg);
|
|
416
419
|
const ws = await resolveWorkspace(cfg, api);
|
|
@@ -425,19 +428,19 @@ async function cmdSourceDb(cfg, args, verb) {
|
|
|
425
428
|
return;
|
|
426
429
|
}
|
|
427
430
|
if (sub === "add") {
|
|
428
|
-
// `dbd add-source <name> <url>` or `dbd source-db add <name> <url>`
|
|
431
|
+
// `dbd add-source-db <name> <url>` or `dbd source-db add <name> <url>`
|
|
429
432
|
const name = verb ? args.pos[0] : args.pos[1];
|
|
430
433
|
const url = verb ? args.pos[1] : args.pos[2];
|
|
431
434
|
await api.post(`/api/workspaces/${ws}/source-dbs`, { name, url });
|
|
432
435
|
cfg.source_db = name;
|
|
433
436
|
saveConfig(cfg);
|
|
434
|
-
console.log(`source
|
|
437
|
+
console.log(`source DB "${name}" added (with a \`main\` branch)`);
|
|
435
438
|
return;
|
|
436
439
|
}
|
|
437
440
|
if (sub === "rm") {
|
|
438
441
|
const name = verb ? args.pos[0] : args.pos[1];
|
|
439
442
|
await api.del(`/api/workspaces/${ws}/source-dbs/${enc(name)}`);
|
|
440
|
-
console.log(`source
|
|
443
|
+
console.log(`source DB "${name}" deleted`);
|
|
441
444
|
return;
|
|
442
445
|
}
|
|
443
446
|
if (sub === "reset") {
|
|
@@ -449,7 +452,7 @@ async function cmdSourceDb(cfg, args, verb) {
|
|
|
449
452
|
throw new Error("usage: dbd source-db ls | add <name> <url> | rm <name> | reset <name>");
|
|
450
453
|
}
|
|
451
454
|
|
|
452
|
-
/** `dbd use <source-db>` — set the default source
|
|
455
|
+
/** `dbd use <source-db>` — set the default source DB. */
|
|
453
456
|
async function cmdUse(cfg, args) {
|
|
454
457
|
const api = apiFor(cfg);
|
|
455
458
|
const ws = await resolveWorkspace(cfg, api);
|
|
@@ -458,14 +461,14 @@ async function cmdUse(cfg, args) {
|
|
|
458
461
|
const sds = (await workspaceDetail(api, ws))?.source_dbs ?? [];
|
|
459
462
|
if (sds.length === 1) name = sds[0].name;
|
|
460
463
|
else if (sds.length > 1) {
|
|
461
|
-
name = (await chooseRadio("Select a source
|
|
464
|
+
name = (await chooseRadio("Select a source DB:", sds, (s) => s.name))?.name;
|
|
462
465
|
}
|
|
463
466
|
}
|
|
464
467
|
if (!name) throw new Error("usage: dbd use <source-db>");
|
|
465
468
|
await findSourceDb(api, ws, name); // validate
|
|
466
469
|
cfg.source_db = name;
|
|
467
470
|
saveConfig(cfg);
|
|
468
|
-
console.log(`current source
|
|
471
|
+
console.log(`current source DB: ${name}`);
|
|
469
472
|
}
|
|
470
473
|
|
|
471
474
|
async function cmdBranch(cfg, args) {
|
|
@@ -475,7 +478,7 @@ async function cmdBranch(cfg, args) {
|
|
|
475
478
|
|
|
476
479
|
// `dbd branch ls`
|
|
477
480
|
if (sub === "ls") {
|
|
478
|
-
const src = await resolveSourceDb(cfg, api, ws,
|
|
481
|
+
const src = await resolveSourceDb(cfg, api, ws, sourceFlag(args));
|
|
479
482
|
const sd = await findSourceDb(api, ws, src);
|
|
480
483
|
for (const b of sd.branches ?? []) {
|
|
481
484
|
console.log(
|
|
@@ -486,7 +489,7 @@ async function cmdBranch(cfg, args) {
|
|
|
486
489
|
}
|
|
487
490
|
// `dbd branch rm <name>`
|
|
488
491
|
if (sub === "rm") {
|
|
489
|
-
const src = await resolveSourceDb(cfg, api, ws,
|
|
492
|
+
const src = await resolveSourceDb(cfg, api, ws, sourceFlag(args));
|
|
490
493
|
await api.del(`/api/workspaces/${ws}/source-dbs/${enc(src)}/branches/${enc(args.pos[1])}`);
|
|
491
494
|
console.log(`branch "${args.pos[1]}" deleted`);
|
|
492
495
|
return;
|
|
@@ -495,7 +498,7 @@ async function cmdBranch(cfg, args) {
|
|
|
495
498
|
const explicitCreate = sub === "create";
|
|
496
499
|
const name = explicitCreate ? args.pos[1] : args.pos[0];
|
|
497
500
|
if (!name) throw new Error("usage: dbd branch <name> (or: dbd branch ls | rm <name>)");
|
|
498
|
-
const src = await resolveSourceDb(cfg, api, ws,
|
|
501
|
+
const src = await resolveSourceDb(cfg, api, ws, sourceFlag(args));
|
|
499
502
|
const body = { name };
|
|
500
503
|
const ttl = num(args.flags["ttl-days"]);
|
|
501
504
|
if (ttl !== undefined) body.ttl_days = ttl;
|
|
@@ -560,12 +563,12 @@ Usage: dbd [--api-url <url>] <command> [args]
|
|
|
560
563
|
|
|
561
564
|
auth [--username U] [--password P] [--token T] log in (device flow by default)
|
|
562
565
|
whoami
|
|
563
|
-
use <source-db> set the default source
|
|
566
|
+
use <source-db> set the default source DB
|
|
564
567
|
|
|
565
|
-
add-source <name> <url>
|
|
568
|
+
add-source-db <name> <url> connect a source DB (admin)
|
|
566
569
|
source-db ls | add <name> <url> | rm <name> | reset <name>
|
|
567
570
|
|
|
568
|
-
branch <name> [--source S] [--ttl-days N]
|
|
571
|
+
branch <name> [--source-db S] [--ttl-days N] create a branch, print its DSN
|
|
569
572
|
branch ls | rm <name>
|
|
570
573
|
|
|
571
574
|
conn <source-db> [--branch <name>] print a connection string
|
|
@@ -613,11 +616,11 @@ async function main() {
|
|
|
613
616
|
case "workspace":
|
|
614
617
|
await cmdWorkspace(cfg, rest);
|
|
615
618
|
break;
|
|
619
|
+
case "add-source-db":
|
|
616
620
|
case "add-source":
|
|
617
621
|
await cmdSourceDb(cfg, rest, "add");
|
|
618
622
|
break;
|
|
619
623
|
case "source-db":
|
|
620
|
-
case "source":
|
|
621
624
|
await cmdSourceDb(cfg, rest);
|
|
622
625
|
break;
|
|
623
626
|
case "branch":
|