@codemarc/blt 1.6.5 → 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 +192 -0
- package/dist/blt +4 -0
- package/dist/blt.d.ts.map +1 -1
- package/dist/blt.js.map +1 -1
- package/dist/commands/data/apply.d.ts +6 -0
- package/dist/commands/data/apply.d.ts.map +1 -0
- package/dist/commands/data/apply.js +363 -0
- package/dist/commands/data/apply.js.map +1 -0
- package/dist/commands/data/helpers.d.ts +15 -0
- package/dist/commands/data/helpers.d.ts.map +1 -0
- package/dist/commands/data/helpers.js +53 -0
- package/dist/commands/data/helpers.js.map +1 -0
- package/dist/commands/data/pull.d.ts +11 -0
- package/dist/commands/data/pull.d.ts.map +1 -0
- package/dist/commands/data/pull.js +101 -0
- package/dist/commands/data/pull.js.map +1 -0
- package/dist/commands/data/remove.d.ts +7 -0
- package/dist/commands/data/remove.d.ts.map +1 -0
- package/dist/commands/data/remove.js +29 -0
- package/dist/commands/data/remove.js.map +1 -0
- package/dist/commands/data.d.ts +3 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +115 -0
- package/dist/commands/data.js.map +1 -0
- package/dist/commands/spin/dns.d.ts +7 -0
- package/dist/commands/spin/dns.d.ts.map +1 -0
- package/dist/commands/spin/dns.js +42 -0
- package/dist/commands/spin/dns.js.map +1 -0
- package/dist/commands/spin/down.d.ts +3 -0
- package/dist/commands/spin/down.d.ts.map +1 -0
- package/dist/commands/spin/down.js +17 -0
- package/dist/commands/spin/down.js.map +1 -0
- package/dist/commands/spin/helpers.d.ts +7 -0
- package/dist/commands/spin/helpers.d.ts.map +1 -0
- package/dist/commands/spin/helpers.js +38 -0
- package/dist/commands/spin/helpers.js.map +1 -0
- package/dist/commands/spin/list.d.ts +3 -0
- package/dist/commands/spin/list.d.ts.map +1 -0
- package/dist/commands/spin/list.js +20 -0
- package/dist/commands/spin/list.js.map +1 -0
- package/dist/commands/spin/setup.d.ts +3 -0
- package/dist/commands/spin/setup.d.ts.map +1 -0
- package/dist/commands/spin/setup.js +32 -0
- package/dist/commands/spin/setup.js.map +1 -0
- package/dist/commands/spin/ssh.d.ts +3 -0
- package/dist/commands/spin/ssh.d.ts.map +1 -0
- package/dist/commands/spin/ssh.js +14 -0
- package/dist/commands/spin/ssh.js.map +1 -0
- package/dist/commands/spin/status.d.ts +3 -0
- package/dist/commands/spin/status.d.ts.map +1 -0
- package/dist/commands/spin/status.js +21 -0
- package/dist/commands/spin/status.js.map +1 -0
- package/dist/commands/spin/up.d.ts +11 -0
- package/dist/commands/spin/up.d.ts.map +1 -0
- package/dist/commands/spin/up.js +34 -0
- package/dist/commands/spin/up.js.map +1 -0
- package/dist/commands/spin.d.ts +3 -0
- package/dist/commands/spin.d.ts.map +1 -0
- package/dist/commands/spin.js +166 -0
- package/dist/commands/spin.js.map +1 -0
- package/dist/lib/data-pull-extra-tables.d.ts +26 -0
- package/dist/lib/data-pull-extra-tables.d.ts.map +1 -0
- package/dist/lib/data-pull-extra-tables.js +212 -0
- package/dist/lib/data-pull-extra-tables.js.map +1 -0
- package/dist/lib/data-pull-manifest.d.ts +97 -0
- package/dist/lib/data-pull-manifest.d.ts.map +1 -0
- package/dist/lib/data-pull-manifest.js +603 -0
- package/dist/lib/data-pull-manifest.js.map +1 -0
- package/dist/lib/database-runner.d.ts +28 -2
- package/dist/lib/database-runner.d.ts.map +1 -1
- package/dist/lib/database-runner.js +112 -49
- package/dist/lib/database-runner.js.map +1 -1
- package/dist/lib/digitalocean.d.ts +60 -0
- package/dist/lib/digitalocean.d.ts.map +1 -0
- package/dist/lib/digitalocean.js +108 -0
- package/dist/lib/digitalocean.js.map +1 -0
- package/dist/lib/yaml-converter.d.ts +25 -1
- package/dist/lib/yaml-converter.d.ts.map +1 -1
- package/dist/lib/yaml-converter.js +98 -15
- package/dist/lib/yaml-converter.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Redact credentials from a Postgres URL for manifest logging. */
|
|
2
|
+
export declare function redactedDbUrl(connectionString: string): string;
|
|
3
|
+
/** ISO-ish UTC timestamp for snapshot folder names. */
|
|
4
|
+
export declare function snapshotTimestamp(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `"latest"` to the most recent snapshot directory.
|
|
7
|
+
*
|
|
8
|
+
* Scans `<baseDir>/<instance?>/<timestamp>/manifest.json` and returns the
|
|
9
|
+
* full path to the newest stamped snapshot folder. When no instance prefix is
|
|
10
|
+
* given the search covers every instance sub-directory.
|
|
11
|
+
*
|
|
12
|
+
* @returns absolute path to the snapshot directory, or `null` if none found.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveLatestSnapshot(baseDir: string): string | null;
|
|
15
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/data/helpers.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/** Redact credentials from a Postgres URL for manifest logging. */
|
|
4
|
+
export function redactedDbUrl(connectionString) {
|
|
5
|
+
try {
|
|
6
|
+
const u = new URL(connectionString);
|
|
7
|
+
if (u.password)
|
|
8
|
+
u.password = "***";
|
|
9
|
+
if (u.username)
|
|
10
|
+
u.username = "***";
|
|
11
|
+
return u.toString();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return "(unparseable-connection-string)";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** ISO-ish UTC timestamp for snapshot folder names. */
|
|
18
|
+
export function snapshotTimestamp() {
|
|
19
|
+
return new Date().toISOString().replace(/:/g, "-").replace(/\.\d{3}Z$/, "Z");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve `"latest"` to the most recent snapshot directory.
|
|
23
|
+
*
|
|
24
|
+
* Scans `<baseDir>/<instance?>/<timestamp>/manifest.json` and returns the
|
|
25
|
+
* full path to the newest stamped snapshot folder. When no instance prefix is
|
|
26
|
+
* given the search covers every instance sub-directory.
|
|
27
|
+
*
|
|
28
|
+
* @returns absolute path to the snapshot directory, or `null` if none found.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveLatestSnapshot(baseDir) {
|
|
31
|
+
if (!existsSync(baseDir))
|
|
32
|
+
return null;
|
|
33
|
+
let best = null;
|
|
34
|
+
const instanceDirs = readdirSync(baseDir, { withFileTypes: true })
|
|
35
|
+
.filter((d) => d.isDirectory());
|
|
36
|
+
for (const instDir of instanceDirs) {
|
|
37
|
+
const instPath = join(baseDir, instDir.name);
|
|
38
|
+
const stamps = readdirSync(instPath, { withFileTypes: true })
|
|
39
|
+
.filter((d) => d.isDirectory());
|
|
40
|
+
for (const stamp of stamps) {
|
|
41
|
+
const snapPath = join(instPath, stamp.name);
|
|
42
|
+
const manifestPath = join(snapPath, "manifest.json");
|
|
43
|
+
if (!existsSync(manifestPath))
|
|
44
|
+
continue;
|
|
45
|
+
const mtime = statSync(manifestPath).mtimeMs;
|
|
46
|
+
if (!best || mtime > best.mtime) {
|
|
47
|
+
best = { path: snapPath, mtime };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return best?.path ?? null;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/data/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,mEAAmE;AACnE,MAAM,UAAU,aAAa,CAAC,gBAAwB;IACrD,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACpC,IAAI,CAAC,CAAC,QAAQ;YAAE,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC;QACnC,IAAI,CAAC,CAAC,QAAQ;YAAE,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC;QACnC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,iCAAiC,CAAC;IAC1C,CAAC;AACF,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,iBAAiB;IAChC,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACpD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,IAAI,IAAI,GAA2C,IAAI,CAAC;IAExD,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAChE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAEjC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aAC3D,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAEjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;YACrD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;gBAAE,SAAS;YACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAC7C,IAAI,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBACjC,IAAI,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAClC,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Logger } from "@caporal/core";
|
|
2
|
+
export type DataPullOpts = {
|
|
3
|
+
outBaseDir: string;
|
|
4
|
+
format: "json" | "yaml" | "toon";
|
|
5
|
+
includeUsers: boolean;
|
|
6
|
+
includeSecrets: boolean;
|
|
7
|
+
/** When true (default), dump every other `public` base table not covered by the curated manifest. */
|
|
8
|
+
includeExtraTables: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function dataPull(instanceLabel: string, opts: DataPullOpts, logger: Logger): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/data/pull.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAe5C,MAAM,MAAM,YAAY,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,qGAAqG;IACrG,kBAAkB,EAAE,OAAO,CAAC;CAC5B,CAAC;AA4CF,wBAAsB,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiEvG"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { encode } from "@toon-format/toon";
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
import { getPullConnectionString, withReadOnlyClient } from "../../lib/database-runner";
|
|
6
|
+
import { discoverAndSortExtraPublicTables, pullExtraPublicTablePartition } from "../../lib/data-pull-extra-tables";
|
|
7
|
+
import { getFullPullManifest } from "../../lib/data-pull-manifest";
|
|
8
|
+
import { redactedDbUrl, snapshotTimestamp } from "./helpers";
|
|
9
|
+
function writeSnapshotAsset(snapDir, format, part, functionName, sqlType, logger, assetsManifest) {
|
|
10
|
+
const rows = part.rows;
|
|
11
|
+
const ext = format === "yaml" ? "yaml" : format === "toon" ? "toon" : "json";
|
|
12
|
+
const fileName = `${part.snapshotStem}.${ext}`;
|
|
13
|
+
const filePath = join(snapDir, fileName);
|
|
14
|
+
const payload = {
|
|
15
|
+
assetId: part.assetId,
|
|
16
|
+
function: functionName,
|
|
17
|
+
sqlType,
|
|
18
|
+
rows,
|
|
19
|
+
};
|
|
20
|
+
if (part.primaryKey?.length)
|
|
21
|
+
payload.primaryKey = part.primaryKey;
|
|
22
|
+
if (part.conflictUpdateExclude?.length)
|
|
23
|
+
payload.conflictUpdateExclude = part.conflictUpdateExclude;
|
|
24
|
+
if (part.conflictWhere)
|
|
25
|
+
payload.conflictWhere = part.conflictWhere;
|
|
26
|
+
if (part.columnUdts && Object.keys(part.columnUdts).length > 0) {
|
|
27
|
+
payload.columnUdts = part.columnUdts;
|
|
28
|
+
}
|
|
29
|
+
const body = format === "yaml"
|
|
30
|
+
? yaml.dump(payload, { lineWidth: 120, noRefs: true })
|
|
31
|
+
: format === "toon"
|
|
32
|
+
? `${encode(payload)}\n`
|
|
33
|
+
: `${JSON.stringify(payload, null, 2)}\n`;
|
|
34
|
+
writeFileSync(filePath, body, "utf8");
|
|
35
|
+
assetsManifest.push({
|
|
36
|
+
id: part.assetId,
|
|
37
|
+
file: fileName,
|
|
38
|
+
function: functionName,
|
|
39
|
+
sqlType,
|
|
40
|
+
rowCount: rows.length,
|
|
41
|
+
});
|
|
42
|
+
logger.info(` Wrote ${fileName} (${rows.length} rows)`);
|
|
43
|
+
}
|
|
44
|
+
export async function dataPull(instanceLabel, opts, logger) {
|
|
45
|
+
const url = getPullConnectionString();
|
|
46
|
+
const stamped = snapshotTimestamp();
|
|
47
|
+
const snapDir = join(process.cwd(), opts.outBaseDir, instanceLabel, stamped);
|
|
48
|
+
mkdirSync(snapDir, { recursive: true });
|
|
49
|
+
logger.info(`Snapshot directory: ${snapDir}`);
|
|
50
|
+
logger.info(`Source (redacted): ${redactedDbUrl(url)}`);
|
|
51
|
+
const ctx = {
|
|
52
|
+
includeUsers: opts.includeUsers,
|
|
53
|
+
includeSecrets: opts.includeSecrets,
|
|
54
|
+
};
|
|
55
|
+
if (!ctx.includeUsers) {
|
|
56
|
+
logger.info("Pull: skipping users and profile_roles_extra (--skip-users)");
|
|
57
|
+
}
|
|
58
|
+
if (!ctx.includeSecrets) {
|
|
59
|
+
logger.info("Pull: redacting payment_providers secrets (use --include-secrets to include)");
|
|
60
|
+
}
|
|
61
|
+
if (!opts.includeExtraTables) {
|
|
62
|
+
logger.info("Pull: skipping extra public.* tables (--skip-extra-tables)");
|
|
63
|
+
}
|
|
64
|
+
let pgVersion = "unknown";
|
|
65
|
+
const assetsManifest = [];
|
|
66
|
+
await withReadOnlyClient(url, async (client) => {
|
|
67
|
+
const v = await client.query("SELECT version() AS version");
|
|
68
|
+
pgVersion = v.rows[0]?.version ?? "unknown";
|
|
69
|
+
const seq = getFullPullManifest();
|
|
70
|
+
for (const entry of seq) {
|
|
71
|
+
if (entry.skip?.(ctx)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const partitions = await entry.fetchPartitions(client, ctx);
|
|
75
|
+
for (const part of partitions) {
|
|
76
|
+
logger.info(`Pulling ${part.assetId}…`);
|
|
77
|
+
writeSnapshotAsset(snapDir, opts.format, part, entry.targetName, entry.sqlType, logger, assetsManifest);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (opts.includeExtraTables) {
|
|
81
|
+
const extras = await discoverAndSortExtraPublicTables(client, ctx);
|
|
82
|
+
logger.info(`Pulling ${extras.length} additional public table(s) (not in curated manifest)…`);
|
|
83
|
+
for (const table of extras) {
|
|
84
|
+
const part = await pullExtraPublicTablePartition(client, table);
|
|
85
|
+
logger.info(`Pulling ${part.assetId}…`);
|
|
86
|
+
writeSnapshotAsset(snapDir, opts.format, part, `public.${table}`, "table", logger, assetsManifest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const manifest = {
|
|
91
|
+
instance: instanceLabel,
|
|
92
|
+
pulledAt: new Date().toISOString(),
|
|
93
|
+
sourceHostRedacted: redactedDbUrl(url),
|
|
94
|
+
pgVersion,
|
|
95
|
+
assets: assetsManifest,
|
|
96
|
+
};
|
|
97
|
+
const mfPath = join(snapDir, "manifest.json");
|
|
98
|
+
writeFileSync(mfPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
99
|
+
logger.info(`Wrote manifest: ${mfPath}`);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/commands/data/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/commands/data/remove.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAgBF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAevF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync, rmSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
function resolveInstanceSnapshotRoot(outBaseDir, instance) {
|
|
4
|
+
const inst = instance.trim();
|
|
5
|
+
if (!inst || inst === "." || inst === ".." || inst.includes("/") || inst.includes("\\")) {
|
|
6
|
+
throw new Error(`Instance must be a simple label (e.g. laf), not a path: "${instance}"`);
|
|
7
|
+
}
|
|
8
|
+
const base = resolve(process.cwd(), outBaseDir);
|
|
9
|
+
const target = resolve(base, inst);
|
|
10
|
+
const rel = relative(base, target);
|
|
11
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
12
|
+
throw new Error(`Resolved path escapes snapshot base: ${target}`);
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
}
|
|
16
|
+
export function dataRemove(instance, opts, logger) {
|
|
17
|
+
const target = resolveInstanceSnapshotRoot(opts.outBaseDir, instance);
|
|
18
|
+
if (!existsSync(target)) {
|
|
19
|
+
logger.info(`Nothing to remove (missing): ${target}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (opts.dryRun) {
|
|
23
|
+
logger.info(`Dry-run: would remove recursively: ${target}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
rmSync(target, { recursive: true, force: true });
|
|
27
|
+
logger.info(`Removed: ${target}`);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=remove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../src/commands/data/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQ1D,SAAS,2BAA2B,CAAC,UAAkB,EAAE,QAAgB;IACxE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACzF,MAAM,IAAI,KAAK,CAAC,4DAA4D,QAAQ,GAAG,CAAC,CAAC;IAC1F,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,IAAoB,EAAE,MAAc;IAChF,MAAM,MAAM,GAAG,2BAA2B,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAEtE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAC;QACtD,OAAO;IACR,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,sCAAsC,MAAM,EAAE,CAAC,CAAC;QAC5D,OAAO;IACR,CAAC;IAED,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,CAAC,IAAI,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,eAAe,CAAC;AAsB5E,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,OAAO,EAAE,OAAO,QAqInD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { dataPull } from "./data/pull";
|
|
2
|
+
import { dataApply } from "./data/apply";
|
|
3
|
+
import { dataRemove } from "./data/remove";
|
|
4
|
+
import { resolveLatestSnapshot } from "./data/helpers";
|
|
5
|
+
const dataHelp = `
|
|
6
|
+
instance data snapshots (read-only pull, apply to dev)
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
blt data pull <instance> [--out DIR] [--format json|yaml|toon] [--skip-users] [--include-secrets] [--skip-extra-tables]
|
|
10
|
+
blt data apply <snapshotDir|latest> [--dry-run] [--out DIR]
|
|
11
|
+
blt data remove <instance> [--out DIR] [--dry-run]
|
|
12
|
+
|
|
13
|
+
Pull reads from BLT_DATA_PULL_URL (recommended) or SUPABASE_CONNECTION_STRING in a READ ONLY transaction.
|
|
14
|
+
|
|
15
|
+
Apply executes generated SQL against BLT_DATA_LOAD_URL or SUPABASE_CONNECTION_STRING — use only on disposable dev databases.
|
|
16
|
+
Use "latest" as the snapshot target to automatically apply the most recent snapshot.
|
|
17
|
+
|
|
18
|
+
See tools/cli/README.md → "Data snapshots".
|
|
19
|
+
`;
|
|
20
|
+
export default function dataCommand(program) {
|
|
21
|
+
program
|
|
22
|
+
.command("data", "Instance data snapshot pull / apply / remove")
|
|
23
|
+
.help(dataHelp)
|
|
24
|
+
.action(() => {
|
|
25
|
+
console.log(dataHelp);
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command("data pull", "Export full instance-shaped snapshot (read-only)")
|
|
29
|
+
.hide()
|
|
30
|
+
.argument("<instance>", "Snapshot label / folder name (e.g. laf, staging)")
|
|
31
|
+
.option("-o, --out <dir>", 'Base directory for snapshots under cwd', {
|
|
32
|
+
default: "data-snapshots",
|
|
33
|
+
})
|
|
34
|
+
.option("-f, --format <fmt>", 'Per-asset format: json, yaml, or toon (compact LLM-oriented)', {
|
|
35
|
+
default: "json",
|
|
36
|
+
validator: ["json", "yaml", "toon"],
|
|
37
|
+
})
|
|
38
|
+
.option("--skip-users", "Omit create_user and profile_roles_extra assets (e.g. prod pulls)", {
|
|
39
|
+
default: false,
|
|
40
|
+
})
|
|
41
|
+
.option("--include-secrets", "Include api_key / encrypted secrets in payment_providers rows", {
|
|
42
|
+
default: false,
|
|
43
|
+
})
|
|
44
|
+
.option("--skip-extra-tables", "Only pull the curated manifest (omit other public.* base tables discovered in the DB)", {
|
|
45
|
+
default: false,
|
|
46
|
+
})
|
|
47
|
+
.action(async ({ args, options, logger, }) => {
|
|
48
|
+
try {
|
|
49
|
+
const instance = args.instance;
|
|
50
|
+
const fmt = options.format;
|
|
51
|
+
const format = fmt === "yaml" ? "yaml" : fmt === "toon" ? "toon" : "json";
|
|
52
|
+
await dataPull(instance, {
|
|
53
|
+
outBaseDir: options.out || "data-snapshots",
|
|
54
|
+
format,
|
|
55
|
+
includeUsers: !options.skipUsers,
|
|
56
|
+
includeSecrets: !!options.includeSecrets,
|
|
57
|
+
includeExtraTables: !options.skipExtraTables,
|
|
58
|
+
}, logger);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
logger.error(`data pull failed: ${message}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
program
|
|
67
|
+
.command("data apply", "Replay a snapshot bundle as SQL on the load database")
|
|
68
|
+
.hide()
|
|
69
|
+
.argument("<snapshotDir>", 'Path to stamped snapshot folder (contains manifest.json), or "latest"')
|
|
70
|
+
.option("--dry-run", "Print combined SQL instead of executing", { default: false })
|
|
71
|
+
.option("-o, --out <dir>", 'Snapshot base directory (used with "latest")', {
|
|
72
|
+
default: "data-snapshots",
|
|
73
|
+
})
|
|
74
|
+
.action(async ({ args, options, logger, }) => {
|
|
75
|
+
try {
|
|
76
|
+
let dir = args.snapshotDir;
|
|
77
|
+
if (dir === "latest") {
|
|
78
|
+
const baseDir = options.out || "data-snapshots";
|
|
79
|
+
const resolved = resolveLatestSnapshot(baseDir);
|
|
80
|
+
if (!resolved) {
|
|
81
|
+
logger.error(`No snapshots found under ${baseDir}/`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
dir = resolved;
|
|
85
|
+
logger.info(`Resolved latest → ${dir}`);
|
|
86
|
+
}
|
|
87
|
+
await dataApply(dir, { dryRun: !!options.dryRun }, logger);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
+
logger.error(`data apply failed: ${message}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
program
|
|
96
|
+
.command("data remove", "Delete all snapshots for an instance under data-snapshots/<instance>")
|
|
97
|
+
.hide()
|
|
98
|
+
.argument("<instance>", "Snapshot folder label (e.g. laf) — removes every stamped run under it")
|
|
99
|
+
.option("-o, --out <dir>", 'Snapshot base directory under cwd (must contain <instance>/)', {
|
|
100
|
+
default: "data-snapshots",
|
|
101
|
+
})
|
|
102
|
+
.option("--dry-run", "Print path that would be removed without deleting", { default: false })
|
|
103
|
+
.action(async ({ args, options, logger, }) => {
|
|
104
|
+
try {
|
|
105
|
+
const instance = args.instance;
|
|
106
|
+
dataRemove(instance, { outBaseDir: options.out || "data-snapshots", dryRun: !!options.dryRun }, logger);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
logger.error(`data remove failed: ${message}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dns.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/dns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,wBAAsB,OAAO,CAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createDnsRecord, listDnsRecords, deleteDnsRecord } from "../../lib/digitalocean";
|
|
2
|
+
import { formatTable } from "./helpers";
|
|
3
|
+
export async function spinDns(action, domain, opts, logger) {
|
|
4
|
+
switch (action) {
|
|
5
|
+
case "create": {
|
|
6
|
+
if (!opts.name || !opts.ip) {
|
|
7
|
+
throw new Error("spin dns create requires --name and --ip");
|
|
8
|
+
}
|
|
9
|
+
const record = await createDnsRecord(domain, "A", opts.name, opts.ip);
|
|
10
|
+
logger.info(`DNS record created — id: ${record.id}, ${record.name}.${domain} → ${record.data}`);
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
case "list": {
|
|
14
|
+
const records = await listDnsRecords(domain);
|
|
15
|
+
if (records.length === 0) {
|
|
16
|
+
logger.info(`No DNS records found for ${domain}.`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const header = ["ID", "TYPE", "NAME", "DATA", "TTL"];
|
|
20
|
+
const rows = records.map((r) => [
|
|
21
|
+
String(r.id),
|
|
22
|
+
r.type,
|
|
23
|
+
r.name,
|
|
24
|
+
r.data,
|
|
25
|
+
String(r.ttl),
|
|
26
|
+
]);
|
|
27
|
+
console.log(formatTable([header, ...rows]));
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case "delete": {
|
|
31
|
+
if (!opts.recordId) {
|
|
32
|
+
throw new Error("spin dns delete requires --record-id");
|
|
33
|
+
}
|
|
34
|
+
await deleteDnsRecord(domain, Number(opts.recordId));
|
|
35
|
+
logger.info(`DNS record ${opts.recordId} deleted from ${domain}.`);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`Unknown dns action "${action}". Use: create, list, delete`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=dns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dns.js","sourceRoot":"","sources":["../../../src/commands/spin/dns.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,OAAO,CAC5B,MAAc,EACd,MAAc,EACd,IAAuD,EACvD,MAAc;IAEd,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YACtE,MAAM,CAAC,IAAI,CAAC,4BAA4B,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,IAAI,MAAM,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAChG,MAAM;QACP,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACb,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,4BAA4B,MAAM,GAAG,CAAC,CAAC;gBACnD,OAAO;YACR,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBACZ,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI;gBACN,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;aACb,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM;QACP,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,iBAAiB,MAAM,GAAG,CAAC,CAAC;YACnE,MAAM;QACP,CAAC;QAED;YACC,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,8BAA8B,CAAC,CAAC;IAC/E,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"down.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/down.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB9F"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { deleteDroplet, getDroplet } from "../../lib/digitalocean";
|
|
2
|
+
import { confirm, getPublicIp } from "./helpers";
|
|
3
|
+
export async function spinDown(nameOrId, force, logger) {
|
|
4
|
+
const droplet = await getDroplet(nameOrId);
|
|
5
|
+
const ip = getPublicIp(droplet) ?? "n/a";
|
|
6
|
+
if (!force) {
|
|
7
|
+
const ok = await confirm(`Destroy droplet "${droplet.name}" (id: ${droplet.id}, ip: ${ip})?`);
|
|
8
|
+
if (!ok) {
|
|
9
|
+
logger.info("Aborted.");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
logger.info(`Destroying droplet "${droplet.name}" (${droplet.id})…`);
|
|
14
|
+
await deleteDroplet(droplet.id);
|
|
15
|
+
logger.info("Droplet destroyed.");
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=down.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"down.js","sourceRoot":"","sources":["../../../src/commands/spin/down.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB,EAAE,KAAc,EAAE,MAAc;IAC9E,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;IAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,EAAE,GAAG,MAAM,OAAO,CACvB,oBAAoB,OAAO,CAAC,IAAI,UAAU,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,CACnE,CAAC;QACF,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;IACF,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,uBAAuB,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IACrE,MAAM,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Logger } from "@caporal/core";
|
|
2
|
+
import { type Droplet } from "../../lib/digitalocean";
|
|
3
|
+
export declare function getPublicIp(droplet: Droplet): string | undefined;
|
|
4
|
+
export declare function waitForActive(idOrName: string, logger: Logger, timeoutMs?: number, intervalMs?: number): Promise<Droplet>;
|
|
5
|
+
export declare function confirm(prompt: string): Promise<boolean>;
|
|
6
|
+
export declare function formatTable(rows: string[][]): string;
|
|
7
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAElE,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEhE;AAED,wBAAsB,aAAa,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,SAAU,EACnB,UAAU,SAAQ,GAChB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ9D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAWpD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { getDroplet } from "../../lib/digitalocean";
|
|
3
|
+
export function getPublicIp(droplet) {
|
|
4
|
+
return droplet.networks.v4.find((n) => n.type === "public")?.ip_address;
|
|
5
|
+
}
|
|
6
|
+
export async function waitForActive(idOrName, logger, timeoutMs = 120_000, intervalMs = 5_000) {
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
while (Date.now() - start < timeoutMs) {
|
|
9
|
+
const d = await getDroplet(idOrName);
|
|
10
|
+
const ip = getPublicIp(d);
|
|
11
|
+
if (d.status === "active" && ip)
|
|
12
|
+
return d;
|
|
13
|
+
logger.info(` status: ${d.status} — waiting…`);
|
|
14
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`Timed out waiting for droplet ${idOrName} to become active`);
|
|
17
|
+
}
|
|
18
|
+
export async function confirm(prompt) {
|
|
19
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question(`${prompt} [y/N] `, (answer) => {
|
|
22
|
+
rl.close();
|
|
23
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function formatTable(rows) {
|
|
28
|
+
if (rows.length === 0)
|
|
29
|
+
return "";
|
|
30
|
+
const widths = rows[0].map((_, col) => Math.max(...rows.map((row) => (row[col] ?? "").length)));
|
|
31
|
+
return rows
|
|
32
|
+
.map((row) => row.map((cell, i) => {
|
|
33
|
+
const w = widths[i];
|
|
34
|
+
return cell.length >= w ? cell : cell + " ".repeat(w - cell.length);
|
|
35
|
+
}).join(" "))
|
|
36
|
+
.join("\n");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/commands/spin/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAgB,MAAM,wBAAwB,CAAC;AAElE,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC3C,OAAO,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,UAAU,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,QAAgB,EAChB,MAAc,EACd,SAAS,GAAG,OAAO,EACnB,UAAU,GAAG,KAAK;IAElB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE;YAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC;QAChD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,mBAAmB,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,MAAc;IAC3C,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,EAAE,CAAC,QAAQ,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;YAC1C,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAgB;IAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CACrC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CACvD,CAAC;IACF,OAAO,IAAI;SACT,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACb,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBzE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { listDroplets } from "../../lib/digitalocean";
|
|
2
|
+
import { formatTable, getPublicIp } from "./helpers";
|
|
3
|
+
export async function spinList(tag, logger) {
|
|
4
|
+
const droplets = await listDroplets(tag);
|
|
5
|
+
if (droplets.length === 0) {
|
|
6
|
+
logger.info(`No droplets found with tag "${tag}".`);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const header = ["NAME", "ID", "IP", "STATUS", "REGION", "SIZE"];
|
|
10
|
+
const rows = droplets.map((d) => [
|
|
11
|
+
d.name,
|
|
12
|
+
String(d.id),
|
|
13
|
+
getPublicIp(d) ?? "pending",
|
|
14
|
+
d.status,
|
|
15
|
+
d.region.slug,
|
|
16
|
+
d.size_slug,
|
|
17
|
+
]);
|
|
18
|
+
console.log(formatTable([header, ...rows]));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/spin/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,MAAc;IACzD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IAEzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAAC;QACpD,OAAO;IACR,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAChC,CAAC,CAAC,IAAI;QACN,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACZ,WAAW,CAAC,CAAC,CAAC,IAAI,SAAS;QAC3B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM,CAAC,IAAI;QACb,CAAC,CAAC,SAAS;KACX,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAO5C,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC7F"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getDroplet } from "../../lib/digitalocean";
|
|
5
|
+
import { getPublicIp } from "./helpers";
|
|
6
|
+
export async function spinSetup(nameOrId, user, logger) {
|
|
7
|
+
const droplet = await getDroplet(nameOrId);
|
|
8
|
+
const ip = getPublicIp(droplet);
|
|
9
|
+
if (!ip) {
|
|
10
|
+
throw new Error(`Droplet "${droplet.name}" has no public IP yet (status: ${droplet.status})`);
|
|
11
|
+
}
|
|
12
|
+
const scriptPath = join(process.cwd(), "deploy", "scripts", "bootstrap-droplet.sh");
|
|
13
|
+
if (!existsSync(scriptPath)) {
|
|
14
|
+
logger.info("Bootstrap script not found — showing what would run:");
|
|
15
|
+
logger.info(` 1. scp ${scriptPath} ${user}@${ip}:/tmp/bootstrap-droplet.sh`);
|
|
16
|
+
logger.info(` 2. ssh ${user}@${ip} 'bash /tmp/bootstrap-droplet.sh'`);
|
|
17
|
+
logger.info(`\nCreate deploy/scripts/bootstrap-droplet.sh to enable automatic setup.`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
logger.info(`Uploading bootstrap script to ${user}@${ip}…`);
|
|
21
|
+
const scp = spawnSync("scp", ["-o", "StrictHostKeyChecking=accept-new", scriptPath, `${user}@${ip}:/tmp/bootstrap-droplet.sh`], { stdio: "inherit" });
|
|
22
|
+
if (scp.status !== 0) {
|
|
23
|
+
throw new Error(`scp failed with exit code ${scp.status}`);
|
|
24
|
+
}
|
|
25
|
+
logger.info("Running bootstrap script…");
|
|
26
|
+
const ssh = spawnSync("ssh", ["-o", "StrictHostKeyChecking=accept-new", `${user}@${ip}`, "bash /tmp/bootstrap-droplet.sh"], { stdio: "inherit" });
|
|
27
|
+
if (ssh.status !== 0) {
|
|
28
|
+
throw new Error(`Bootstrap script failed with exit code ${ssh.status}`);
|
|
29
|
+
}
|
|
30
|
+
logger.info("Bootstrap complete.");
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/commands/spin/setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAY,EAAE,MAAc;IAC7E,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,IAAI,mCAAmC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;IACpF,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACpE,MAAM,CAAC,IAAI,CAAC,YAAY,UAAU,IAAI,IAAI,IAAI,EAAE,4BAA4B,CAAC,CAAC;QAC9E,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,mCAAmC,CAAC,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACvF,OAAO;IACR,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,iCAAiC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,SAAS,CACpB,KAAK,EACL,CAAC,IAAI,EAAE,kCAAkC,EAAE,UAAU,EAAE,GAAG,IAAI,IAAI,EAAE,4BAA4B,CAAC,EACjG,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,SAAS,CACpB,KAAK,EACL,CAAC,IAAI,EAAE,kCAAkC,EAAE,GAAG,IAAI,IAAI,EAAE,EAAE,EAAE,gCAAgC,CAAC,EAC7F,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,0CAA0C,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACpC,CAAC"}
|