@codemarc/blt 1.7.0 → 1.8.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 +162 -4
- package/dist/blt +4 -0
- package/dist/blt.d.ts.map +1 -1
- package/dist/blt.js.map +1 -1
- package/dist/commands/app/data.d.ts +29 -0
- package/dist/commands/app/data.d.ts.map +1 -0
- package/dist/commands/app/data.js +70 -0
- package/dist/commands/app/data.js.map +1 -0
- package/dist/commands/app/env-check.d.ts +36 -0
- package/dist/commands/app/env-check.d.ts.map +1 -0
- package/dist/commands/app/env-check.js +110 -0
- package/dist/commands/app/env-check.js.map +1 -0
- package/dist/commands/app/paths.d.ts +6 -0
- package/dist/commands/app/paths.d.ts.map +1 -0
- package/dist/commands/app/paths.js +41 -0
- package/dist/commands/app/paths.js.map +1 -0
- package/dist/commands/app/pos.d.ts +8 -0
- package/dist/commands/app/pos.d.ts.map +1 -0
- package/dist/commands/app/pos.js +74 -0
- package/dist/commands/app/pos.js.map +1 -0
- package/dist/commands/app/shell.d.ts +9 -0
- package/dist/commands/app/shell.d.ts.map +1 -0
- package/dist/commands/app/shell.js +33 -0
- package/dist/commands/app/shell.js.map +1 -0
- package/dist/commands/app.d.ts +3 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +139 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/data/apply.d.ts.map +1 -1
- package/dist/commands/data/apply.js +20 -3
- package/dist/commands/data/apply.js.map +1 -1
- package/dist/commands/data/helpers.d.ts +12 -2
- package/dist/commands/data/helpers.d.ts.map +1 -1
- package/dist/commands/data/helpers.js +44 -3
- package/dist/commands/data/helpers.js.map +1 -1
- package/dist/commands/data/pull.js +2 -2
- package/dist/commands/data/pull.js.map +1 -1
- package/dist/commands/data.d.ts.map +1 -1
- package/dist/commands/data.js +18 -3
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/env/get.d.ts.map +1 -1
- package/dist/commands/env/get.js +4 -2
- package/dist/commands/env/get.js.map +1 -1
- 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 +33 -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/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/repositories.d.ts.map +1 -1
- package/dist/lib/repositories.js +6 -0
- package/dist/lib/repositories.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/env/get.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { formatEnvFileHeader, BLT_ENV_NAME_KEY } from "../app/env-check";
|
|
3
4
|
/** Single path segment for `.env.<name>` under `.trailz/env/`. */
|
|
4
5
|
const NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
5
6
|
/**
|
|
@@ -49,7 +50,8 @@ export async function envGet(name, logger) {
|
|
|
49
50
|
process.exit(proc.exitCode ?? 1);
|
|
50
51
|
}
|
|
51
52
|
const outPath = join(cwd, ".env");
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
const body = proc.stdout.toString().replace(/^\uFEFF/, "").trimStart();
|
|
54
|
+
await Bun.write(outPath, `${formatEnvFileHeader(name)}${body}\n`);
|
|
55
|
+
logger.info(`Wrote ${outPath} (${BLT_ENV_NAME_KEY}=${name})`);
|
|
54
56
|
}
|
|
55
57
|
//# sourceMappingURL=get.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/env/get.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/env/get.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzE,kEAAkE;AAClE,MAAM,OAAO,GAAG,8BAA8B,CAAC;AAE/C;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,GAAW;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5E,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,MAAc;IACxD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,KAAK,CACX,sEAAsE,CACtE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IACxC,qEAAqE;IACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;IAElC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,KAAK,CACX,oDAAoD,OAAO,IAAI;YAC9D,6FAA6F,CAC9F,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC;QAC1B,GAAG,EAAE,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC;QAClC,GAAG;QACH,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,SAAS;KACjB,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;IACvE,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,IAAI,CAAC,SAAS,OAAO,KAAK,gBAAgB,IAAI,IAAI,GAAG,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -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,CAsC7F"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
const scriptPath = join(process.cwd(), "bootstrap-droplet.sh");
|
|
14
|
+
if (!existsSync(scriptPath)) {
|
|
15
|
+
logger.info("Bootstrap script not found — showing what would run:");
|
|
16
|
+
logger.info(` 1. scp ${scriptPath} ${user}@${ip}:/tmp/bootstrap-droplet.sh`);
|
|
17
|
+
logger.info(` 2. ssh ${user}@${ip} 'bash /tmp/bootstrap-droplet.sh'`);
|
|
18
|
+
logger.info(`\nCreate deploy/scripts/bootstrap-droplet.sh to enable automatic setup.`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
logger.info(`Uploading bootstrap script to ${user}@${ip}…`);
|
|
22
|
+
const scp = spawnSync("scp", ["-o", "StrictHostKeyChecking=accept-new", scriptPath, `${user}@${ip}:/tmp/bootstrap-droplet.sh`], { stdio: "inherit" });
|
|
23
|
+
if (scp.status !== 0) {
|
|
24
|
+
throw new Error(`scp failed with exit code ${scp.status}`);
|
|
25
|
+
}
|
|
26
|
+
logger.info("Running bootstrap script…");
|
|
27
|
+
const ssh = spawnSync("ssh", ["-o", "StrictHostKeyChecking=accept-new", `${user}@${ip}`, "bash /tmp/bootstrap-droplet.sh"], { stdio: "inherit" });
|
|
28
|
+
if (ssh.status !== 0) {
|
|
29
|
+
throw new Error(`Bootstrap script failed with exit code ${ssh.status}`);
|
|
30
|
+
}
|
|
31
|
+
logger.info("Bootstrap complete.");
|
|
32
|
+
}
|
|
33
|
+
//# 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,sFAAsF;IACtF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAC/D,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3F"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { getDroplet } from "../../lib/digitalocean";
|
|
3
|
+
import { getPublicIp } from "./helpers";
|
|
4
|
+
export async function spinSsh(nameOrId, user, logger) {
|
|
5
|
+
const droplet = await getDroplet(nameOrId);
|
|
6
|
+
const ip = getPublicIp(droplet);
|
|
7
|
+
if (!ip) {
|
|
8
|
+
throw new Error(`Droplet "${droplet.name}" has no public IP yet (status: ${droplet.status})`);
|
|
9
|
+
}
|
|
10
|
+
logger.info(`Connecting to ${user}@${ip} (${droplet.name})…`);
|
|
11
|
+
const result = spawnSync("ssh", ["-o", "StrictHostKeyChecking=accept-new", `${user}@${ip}`], { stdio: "inherit" });
|
|
12
|
+
process.exit(result.status ?? 0);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ssh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../../src/commands/spin/ssh.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB,EAAE,IAAY,EAAE,MAAc;IAC3E,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,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,SAAS,CACvB,KAAK,EACL,CAAC,IAAI,EAAE,kCAAkC,EAAE,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,EAC3D,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBjF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getDroplet } from "../../lib/digitalocean";
|
|
2
|
+
import { formatTable, getPublicIp } from "./helpers";
|
|
3
|
+
export async function spinStatus(nameOrId, _logger) {
|
|
4
|
+
const d = await getDroplet(nameOrId);
|
|
5
|
+
const ip = getPublicIp(d) ?? "pending";
|
|
6
|
+
console.log(formatTable([
|
|
7
|
+
["Name", d.name],
|
|
8
|
+
["ID", String(d.id)],
|
|
9
|
+
["Status", d.status],
|
|
10
|
+
["IP", ip],
|
|
11
|
+
["Region", `${d.region.slug} (${d.region.name})`],
|
|
12
|
+
["Size", d.size_slug],
|
|
13
|
+
["Image", `${d.image.slug} (${d.image.name})`],
|
|
14
|
+
["vCPUs", String(d.vcpus)],
|
|
15
|
+
["Memory", `${d.memory} MB`],
|
|
16
|
+
["Disk", `${d.disk} GB`],
|
|
17
|
+
["Tags", d.tags.join(", ") || "none"],
|
|
18
|
+
["Created", d.created_at],
|
|
19
|
+
]));
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/commands/spin/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,OAAe;IACjE,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IAEvC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACvB,CAAC,MAAM,EAAK,CAAC,CAAC,IAAI,CAAC;QACnB,CAAC,IAAI,EAAO,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC,QAAQ,EAAG,CAAC,CAAC,MAAM,CAAC;QACrB,CAAC,IAAI,EAAO,EAAE,CAAC;QACf,CAAC,QAAQ,EAAG,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;QAClD,CAAC,MAAM,EAAK,CAAC,CAAC,SAAS,CAAC;QACxB,CAAC,OAAO,EAAI,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;QAChD,CAAC,OAAO,EAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,QAAQ,EAAG,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,MAAM,EAAK,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC;QAC3B,CAAC,MAAM,EAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QACxC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;KACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Logger } from "@caporal/core";
|
|
2
|
+
export type SpinUpOpts = {
|
|
3
|
+
name: string;
|
|
4
|
+
size: string;
|
|
5
|
+
region: string;
|
|
6
|
+
image: string;
|
|
7
|
+
sshKeys: string[];
|
|
8
|
+
tag: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function spinUp(opts: SpinUpOpts, logger: Logger): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=up.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"up.d.ts","sourceRoot":"","sources":["../../../src/commands/spin/up.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC5E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createDroplet, listSshKeys } from "../../lib/digitalocean";
|
|
2
|
+
import { getPublicIp, waitForActive } from "./helpers";
|
|
3
|
+
export async function spinUp(opts, logger) {
|
|
4
|
+
if (opts.sshKeys.length === 0) {
|
|
5
|
+
const keys = await listSshKeys();
|
|
6
|
+
const bltKeys = keys.filter((k) => k.name.toLowerCase().includes("blt"));
|
|
7
|
+
if (bltKeys.length > 0) {
|
|
8
|
+
opts.sshKeys = bltKeys.map((k) => String(k.id));
|
|
9
|
+
logger.info(`Auto-selected SSH keys: ${bltKeys.map((k) => k.name).join(", ")}`);
|
|
10
|
+
}
|
|
11
|
+
else if (keys.length > 0) {
|
|
12
|
+
opts.sshKeys = keys.map((k) => String(k.id));
|
|
13
|
+
logger.info(`No 'blt' SSH keys found — using all account keys: ${keys.map((k) => k.name).join(", ")}`);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
logger.warn("No SSH keys found in DO account. Droplet will use password auth (check email for root password).");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
logger.info(`Creating droplet "${opts.name}" (${opts.size}) in ${opts.region}…`);
|
|
20
|
+
const droplet = await createDroplet({
|
|
21
|
+
name: opts.name,
|
|
22
|
+
size: opts.size,
|
|
23
|
+
region: opts.region,
|
|
24
|
+
image: opts.image,
|
|
25
|
+
sshKeys: opts.sshKeys,
|
|
26
|
+
tags: [opts.tag],
|
|
27
|
+
});
|
|
28
|
+
logger.info(`Droplet created — id: ${droplet.id}, status: ${droplet.status}`);
|
|
29
|
+
logger.info("Waiting for droplet to become active…");
|
|
30
|
+
const ready = await waitForActive(String(droplet.id), logger);
|
|
31
|
+
const ip = getPublicIp(ready) ?? "unknown";
|
|
32
|
+
logger.info(`Droplet ready — id: ${ready.id}, ip: ${ip}`);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=up.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"up.js","sourceRoot":"","sources":["../../../src/commands/spin/up.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAWvD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAgB,EAAE,MAAc;IAC5D,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC3C,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CACV,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClE,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CACV,qDAAqD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,IAAI,CACV,kGAAkG,CAClG,CAAC;QACH,CAAC;IACF,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACjF,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC;QACnC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;KAChB,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,EAAE,aAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9E,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC;IAC3C,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spin.d.ts","sourceRoot":"","sources":["../../src/commands/spin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,eAAe,CAAC;AAwB5E,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,OAAO,EAAE,OAAO,QAuNnD"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { spinUp } from "./spin/up";
|
|
2
|
+
import { spinDown } from "./spin/down";
|
|
3
|
+
import { spinList } from "./spin/list";
|
|
4
|
+
import { spinStatus } from "./spin/status";
|
|
5
|
+
import { spinSsh } from "./spin/ssh";
|
|
6
|
+
import { spinDns } from "./spin/dns";
|
|
7
|
+
import { spinSetup } from "./spin/setup";
|
|
8
|
+
const spinHelp = `
|
|
9
|
+
Manage DigitalOcean droplets for BLT test environments
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
blt spin up --name <name> [--size slug] [--region slug] [--image slug] [--ssh-keys ids] [--tag tag]
|
|
13
|
+
blt spin down <name> [--force]
|
|
14
|
+
blt spin list [--tag tag]
|
|
15
|
+
blt spin status <name>
|
|
16
|
+
blt spin ssh <name> [--user user]
|
|
17
|
+
blt spin dns <action> --domain <domain> [--name sub] [--ip addr] [--record-id id]
|
|
18
|
+
blt spin setup <name>
|
|
19
|
+
|
|
20
|
+
Requires DIGITALOCEAN_ACCESS_TOKEN env var.
|
|
21
|
+
`;
|
|
22
|
+
export default function spinCommand(program) {
|
|
23
|
+
program
|
|
24
|
+
.command("spin", "Manage DigitalOcean droplets for BLT environments")
|
|
25
|
+
.help(spinHelp)
|
|
26
|
+
.action(() => {
|
|
27
|
+
console.log(spinHelp);
|
|
28
|
+
});
|
|
29
|
+
// -- spin up ---------------------------------------------------------------
|
|
30
|
+
program
|
|
31
|
+
.command("spin up", "Create a new droplet")
|
|
32
|
+
.hide()
|
|
33
|
+
.option("--name <name>", "Droplet name", { required: true })
|
|
34
|
+
.option("--size <size>", "Droplet size slug", {
|
|
35
|
+
default: "s-1vcpu-512mb-10gb",
|
|
36
|
+
})
|
|
37
|
+
.option("--region <region>", "Region slug", { default: "nyc3" })
|
|
38
|
+
.option("--image <image>", "OS image slug", {
|
|
39
|
+
default: "ubuntu-24-04-x64",
|
|
40
|
+
})
|
|
41
|
+
.option("--ssh-keys <keys>", "Comma-separated SSH key IDs or fingerprints")
|
|
42
|
+
.option("--tag <tag>", "Tag for the droplet", { default: "blt" })
|
|
43
|
+
.action(async ({ options, logger, }) => {
|
|
44
|
+
try {
|
|
45
|
+
const sshKeysRaw = options.sshKeys;
|
|
46
|
+
const sshKeys = sshKeysRaw
|
|
47
|
+
? String(sshKeysRaw).split(",").map((k) => k.trim()).filter(Boolean)
|
|
48
|
+
: [];
|
|
49
|
+
await spinUp({
|
|
50
|
+
name: String(options.name),
|
|
51
|
+
size: String(options.size),
|
|
52
|
+
region: String(options.region),
|
|
53
|
+
image: String(options.image),
|
|
54
|
+
tag: String(options.tag),
|
|
55
|
+
sshKeys,
|
|
56
|
+
}, logger);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
+
logger.error(`spin up failed: ${message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// -- spin down -------------------------------------------------------------
|
|
65
|
+
program
|
|
66
|
+
.command("spin down", "Destroy a droplet")
|
|
67
|
+
.hide()
|
|
68
|
+
.argument("<name>", "Droplet name or ID")
|
|
69
|
+
.option("--force", "Skip confirmation", { default: false })
|
|
70
|
+
.action(async ({ args, options, logger, }) => {
|
|
71
|
+
try {
|
|
72
|
+
await spinDown(args.name, !!options.force, logger);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
+
logger.error(`spin down failed: ${message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// -- spin list -------------------------------------------------------------
|
|
81
|
+
program
|
|
82
|
+
.command("spin list", "List BLT droplets")
|
|
83
|
+
.hide()
|
|
84
|
+
.option("--tag <tag>", "Filter by tag", { default: "blt" })
|
|
85
|
+
.action(async ({ options, logger, }) => {
|
|
86
|
+
try {
|
|
87
|
+
await spinList(options.tag, logger);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
+
logger.error(`spin list failed: ${message}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
// -- spin status -----------------------------------------------------------
|
|
96
|
+
program
|
|
97
|
+
.command("spin status", "Check droplet status")
|
|
98
|
+
.hide()
|
|
99
|
+
.argument("<name>", "Droplet name or ID")
|
|
100
|
+
.action(async ({ args, logger, }) => {
|
|
101
|
+
try {
|
|
102
|
+
await spinStatus(args.name, logger);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
logger.error(`spin status failed: ${message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// -- spin ssh --------------------------------------------------------------
|
|
111
|
+
program
|
|
112
|
+
.command("spin ssh", "SSH into a droplet")
|
|
113
|
+
.hide()
|
|
114
|
+
.argument("<name>", "Droplet name or ID")
|
|
115
|
+
.option("--user <user>", "SSH user", { default: "blt" })
|
|
116
|
+
.action(async ({ args, options, logger, }) => {
|
|
117
|
+
try {
|
|
118
|
+
await spinSsh(args.name, options.user, logger);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
122
|
+
logger.error(`spin ssh failed: ${message}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// -- spin dns --------------------------------------------------------------
|
|
127
|
+
program
|
|
128
|
+
.command("spin dns", "Manage DNS records for a domain")
|
|
129
|
+
.hide()
|
|
130
|
+
.argument("<action>", "Action: create, list, delete")
|
|
131
|
+
.option("--domain <domain>", "Domain name", { default: "bltcore.com" })
|
|
132
|
+
.option("--name <name>", "Record name (subdomain)")
|
|
133
|
+
.option("--ip <ip>", "IP address for A record")
|
|
134
|
+
.option("--record-id <id>", "Record ID (for delete)")
|
|
135
|
+
.action(async ({ args, options, logger, }) => {
|
|
136
|
+
try {
|
|
137
|
+
await spinDns(args.action, options.domain, {
|
|
138
|
+
name: options.name,
|
|
139
|
+
ip: options.ip,
|
|
140
|
+
recordId: options.recordId,
|
|
141
|
+
}, logger);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
145
|
+
logger.error(`spin dns failed: ${message}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
// -- spin setup ------------------------------------------------------------
|
|
150
|
+
program
|
|
151
|
+
.command("spin setup", "Bootstrap a fresh droplet (Docker, users, dirs)")
|
|
152
|
+
.hide()
|
|
153
|
+
.argument("<name>", "Droplet name or ID")
|
|
154
|
+
.option("--user <user>", "SSH user for bootstrap", { default: "root" })
|
|
155
|
+
.action(async ({ args, options, logger, }) => {
|
|
156
|
+
try {
|
|
157
|
+
await spinSetup(args.name, options.user, logger);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
161
|
+
logger.error(`spin setup failed: ${message}`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=spin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spin.js","sourceRoot":"","sources":["../../src/commands/spin.ts"],"names":[],"mappings":"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"}
|