@launchmatic/cli 0.7.1 → 0.7.3
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/dist/index.js +151 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21963,13 +21963,16 @@ function discoverServices(repoRoot = findRepoRoot()) {
|
|
|
21963
21963
|
for (const absDir of dirs) {
|
|
21964
21964
|
if (!isLikelyDeployable(absDir)) continue;
|
|
21965
21965
|
const detection = detectLocal(absDir);
|
|
21966
|
+
const rootDir = toPosix(relative(repoRoot, absDir));
|
|
21967
|
+
const dockerfile = existsSync3(join2(absDir, "Dockerfile")) ? `${rootDir}/Dockerfile` : void 0;
|
|
21966
21968
|
out.push({
|
|
21967
21969
|
name: deriveName(repoRoot, absDir),
|
|
21968
|
-
rootDir
|
|
21970
|
+
rootDir,
|
|
21969
21971
|
framework: detection.framework,
|
|
21970
21972
|
buildCmd: detection.buildCmd,
|
|
21971
21973
|
startCmd: detection.startCmd,
|
|
21972
|
-
port: detection.port
|
|
21974
|
+
port: detection.port,
|
|
21975
|
+
...dockerfile ? { dockerfile } : {}
|
|
21973
21976
|
});
|
|
21974
21977
|
}
|
|
21975
21978
|
out.sort((a, b) => a.rootDir.localeCompare(b.rootDir));
|
|
@@ -22427,42 +22430,124 @@ async function pollDeploymentStatus(deploymentId, spinner) {
|
|
|
22427
22430
|
}
|
|
22428
22431
|
|
|
22429
22432
|
// src/commands/logs.ts
|
|
22433
|
+
function resolveServiceId(opts) {
|
|
22434
|
+
try {
|
|
22435
|
+
return readContext().serviceId;
|
|
22436
|
+
} catch {
|
|
22437
|
+
}
|
|
22438
|
+
const manifest = readManifest();
|
|
22439
|
+
if (!manifest || manifest.services.length === 0) {
|
|
22440
|
+
console.error(
|
|
22441
|
+
source_default.red(
|
|
22442
|
+
`No .launchmatic.json here and no ${MANIFEST_FILE} at the repo root.`
|
|
22443
|
+
)
|
|
22444
|
+
);
|
|
22445
|
+
return null;
|
|
22446
|
+
}
|
|
22447
|
+
if (!opts.service) {
|
|
22448
|
+
const names = manifest.services.map((s) => s.name).join(", ");
|
|
22449
|
+
console.error(
|
|
22450
|
+
source_default.red(
|
|
22451
|
+
`Specify which service with --service <name>. Known: ${names}`
|
|
22452
|
+
)
|
|
22453
|
+
);
|
|
22454
|
+
return null;
|
|
22455
|
+
}
|
|
22456
|
+
const match = manifest.services.find((s) => s.name === opts.service);
|
|
22457
|
+
if (!match?.serviceId) {
|
|
22458
|
+
console.error(source_default.red(`No service named "${opts.service}" in ${MANIFEST_FILE}.`));
|
|
22459
|
+
return null;
|
|
22460
|
+
}
|
|
22461
|
+
return match.serviceId;
|
|
22462
|
+
}
|
|
22430
22463
|
function registerLogs(program3) {
|
|
22431
|
-
program3.command("logs").description(
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22464
|
+
program3.command("logs").description(
|
|
22465
|
+
"Stream runtime logs (default) or print build logs for a deployment"
|
|
22466
|
+
).option(
|
|
22467
|
+
"-b, --build",
|
|
22468
|
+
"Print build logs for a deployment instead of streaming runtime logs (use --deployment to target a specific one; defaults to the latest)"
|
|
22469
|
+
).option(
|
|
22470
|
+
"-d, --deployment <id>",
|
|
22471
|
+
"Deployment id for --build (defaults to the latest deployment)"
|
|
22472
|
+
).option(
|
|
22473
|
+
"-s, --service <name>",
|
|
22474
|
+
"Monorepo: which service to read logs for (matches launchmatic.json)"
|
|
22475
|
+
).action(
|
|
22476
|
+
async (opts) => {
|
|
22477
|
+
if (!isLoggedIn()) {
|
|
22478
|
+
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|
|
22479
|
+
process.exitCode = 1;
|
|
22480
|
+
return;
|
|
22481
|
+
}
|
|
22482
|
+
const serviceId = resolveServiceId({ service: opts.service });
|
|
22483
|
+
if (!serviceId) {
|
|
22484
|
+
process.exitCode = 1;
|
|
22485
|
+
return;
|
|
22486
|
+
}
|
|
22487
|
+
if (opts.build) {
|
|
22488
|
+
await printBuildLogs(serviceId, opts.deployment);
|
|
22489
|
+
return;
|
|
22490
|
+
}
|
|
22491
|
+
await streamRuntimeLogs(serviceId);
|
|
22436
22492
|
}
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
const
|
|
22443
|
-
|
|
22493
|
+
);
|
|
22494
|
+
}
|
|
22495
|
+
async function printBuildLogs(serviceId, deploymentId) {
|
|
22496
|
+
let targetId = deploymentId;
|
|
22497
|
+
if (!targetId) {
|
|
22498
|
+
const { data: data2 } = await api(
|
|
22499
|
+
`/api/deployments?serviceId=${serviceId}&page=1&limit=1`
|
|
22444
22500
|
);
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22501
|
+
if (!data2 || data2.length === 0) {
|
|
22502
|
+
console.log(source_default.dim("No deployments yet."));
|
|
22503
|
+
return;
|
|
22504
|
+
}
|
|
22505
|
+
targetId = data2[0].id;
|
|
22506
|
+
}
|
|
22507
|
+
const { data } = await api(
|
|
22508
|
+
`/api/deployments/${targetId}`
|
|
22509
|
+
);
|
|
22510
|
+
console.log(source_default.bold("Deployment: ") + data.id);
|
|
22511
|
+
console.log(source_default.bold("Status: ") + data.status);
|
|
22512
|
+
if (data.commitSha) console.log(source_default.bold("Commit: ") + data.commitSha);
|
|
22513
|
+
if (data.branch) console.log(source_default.bold("Branch: ") + data.branch);
|
|
22514
|
+
console.log(source_default.bold("Started: ") + new Date(data.createdAt).toLocaleString());
|
|
22515
|
+
console.log();
|
|
22516
|
+
if (!data.buildLogs) {
|
|
22517
|
+
console.log(source_default.dim("No build logs recorded for this deployment."));
|
|
22518
|
+
return;
|
|
22519
|
+
}
|
|
22520
|
+
console.log(source_default.bold("\u2500\u2500 Build logs \u2500\u2500"));
|
|
22521
|
+
console.log(data.buildLogs);
|
|
22522
|
+
}
|
|
22523
|
+
async function streamRuntimeLogs(serviceId) {
|
|
22524
|
+
const apiUrl = getApiUrl();
|
|
22525
|
+
const wsUrl = apiUrl.replace(/^http/, "ws");
|
|
22526
|
+
const wsToken = await getWsToken();
|
|
22527
|
+
console.log(source_default.dim("Streaming logs (Ctrl+C to stop)...\n"));
|
|
22528
|
+
const ws = new wrapper_default(
|
|
22529
|
+
`${wsUrl}/ws/runtime-logs/${serviceId}?token=${wsToken}`
|
|
22530
|
+
);
|
|
22531
|
+
ws.on("message", (data) => {
|
|
22532
|
+
const msg = data.toString();
|
|
22533
|
+
try {
|
|
22534
|
+
const parsed = JSON.parse(msg);
|
|
22535
|
+
const ts = parsed.timestamp ? source_default.dim(`[${new Date(parsed.timestamp).toLocaleTimeString()}]`) : "";
|
|
22536
|
+
const line = parsed.message || parsed.line || msg;
|
|
22537
|
+
console.log(`${ts} ${line}`);
|
|
22538
|
+
} catch {
|
|
22539
|
+
console.log(msg);
|
|
22540
|
+
}
|
|
22541
|
+
});
|
|
22542
|
+
ws.on("error", (err) => {
|
|
22543
|
+
console.error(source_default.red(`WebSocket error: ${err.message}`));
|
|
22544
|
+
process.exitCode = 1;
|
|
22545
|
+
});
|
|
22546
|
+
ws.on("close", () => {
|
|
22547
|
+
console.log(source_default.dim("\nLog stream ended."));
|
|
22548
|
+
});
|
|
22549
|
+
process.on("SIGINT", () => {
|
|
22550
|
+
ws.close();
|
|
22466
22551
|
});
|
|
22467
22552
|
}
|
|
22468
22553
|
|
|
@@ -25673,6 +25758,14 @@ function registerDeployments(program3) {
|
|
|
25673
25758
|
if (data.imageTag) {
|
|
25674
25759
|
console.log(source_default.bold("Image: ") + data.imageTag);
|
|
25675
25760
|
}
|
|
25761
|
+
if (data.buildLogs) {
|
|
25762
|
+
console.log();
|
|
25763
|
+
console.log(source_default.bold("\u2500\u2500 Build logs \u2500\u2500"));
|
|
25764
|
+
console.log(data.buildLogs);
|
|
25765
|
+
} else {
|
|
25766
|
+
console.log();
|
|
25767
|
+
console.log(source_default.dim("(no build logs recorded)"));
|
|
25768
|
+
}
|
|
25676
25769
|
} catch (err) {
|
|
25677
25770
|
spinner.fail(source_default.red(`Failed: ${err instanceof Error ? err.message : "Unknown error"}`));
|
|
25678
25771
|
process.exitCode = 1;
|
|
@@ -26328,10 +26421,15 @@ Discovered ${discoveredInfra.length} infra dependenc${discoveredInfra.length ===
|
|
|
26328
26421
|
discovered = await reviewList(
|
|
26329
26422
|
`services`,
|
|
26330
26423
|
discovered,
|
|
26331
|
-
(s) =>
|
|
26424
|
+
(s) => {
|
|
26425
|
+
const fw = s.framework ? source_default.dim(` [${s.framework}]`) : "";
|
|
26426
|
+
const df = s.dockerfile ? source_default.dim(` Dockerfile=${s.dockerfile}`) : "";
|
|
26427
|
+
return `${source_default.cyan(s.name)} ${source_default.dim(s.rootDir)}${fw}${df}`;
|
|
26428
|
+
},
|
|
26332
26429
|
[
|
|
26333
26430
|
{ key: "name", label: "name" },
|
|
26334
26431
|
{ key: "rootDir", label: "rootDir" },
|
|
26432
|
+
{ key: "dockerfile", label: "dockerfile (path from repo root)" },
|
|
26335
26433
|
{ key: "framework", label: "framework" },
|
|
26336
26434
|
{ key: "buildCmd", label: "buildCmd" },
|
|
26337
26435
|
{ key: "startCmd", label: "startCmd" },
|
|
@@ -26426,6 +26524,7 @@ Discovered ${discoveredInfra.length} infra dependenc${discoveredInfra.length ===
|
|
|
26426
26524
|
if (s.framework) payload.framework = s.framework;
|
|
26427
26525
|
if (s.buildCmd) payload.buildCmd = s.buildCmd;
|
|
26428
26526
|
if (s.startCmd) payload.startCmd = s.startCmd;
|
|
26527
|
+
if (s.dockerfile) payload.dockerfilePath = s.dockerfile;
|
|
26429
26528
|
if (gitInfo.repoOwner && gitInfo.repoName) {
|
|
26430
26529
|
payload.repoOwner = gitInfo.repoOwner;
|
|
26431
26530
|
payload.repoName = gitInfo.repoName;
|
|
@@ -26453,7 +26552,8 @@ Discovered ${discoveredInfra.length} infra dependenc${discoveredInfra.length ===
|
|
|
26453
26552
|
framework: s.framework,
|
|
26454
26553
|
buildCmd: s.buildCmd,
|
|
26455
26554
|
startCmd: s.startCmd,
|
|
26456
|
-
port: s.port
|
|
26555
|
+
port: s.port,
|
|
26556
|
+
...s.dockerfile ? { dockerfile: s.dockerfile } : {}
|
|
26457
26557
|
});
|
|
26458
26558
|
}
|
|
26459
26559
|
const infra = [];
|
|
@@ -26612,6 +26712,20 @@ async function up(opts) {
|
|
|
26612
26712
|
console.log();
|
|
26613
26713
|
const results = await Promise.allSettled(
|
|
26614
26714
|
selected.map(async ({ entry }) => {
|
|
26715
|
+
const patch = {};
|
|
26716
|
+
if (entry.dockerfile !== void 0) patch.dockerfilePath = entry.dockerfile || null;
|
|
26717
|
+
if (entry.rootDir) patch.rootDir = `/${entry.rootDir.replace(/^\/+/, "")}`;
|
|
26718
|
+
if (entry.buildCmd) patch.buildCmd = entry.buildCmd;
|
|
26719
|
+
if (entry.startCmd) patch.startCmd = entry.startCmd;
|
|
26720
|
+
if (Object.keys(patch).length > 0) {
|
|
26721
|
+
try {
|
|
26722
|
+
await api(`/api/services/${entry.serviceId}`, {
|
|
26723
|
+
method: "PATCH",
|
|
26724
|
+
body: JSON.stringify(patch)
|
|
26725
|
+
});
|
|
26726
|
+
} catch {
|
|
26727
|
+
}
|
|
26728
|
+
}
|
|
26615
26729
|
const res = await api("/api/deployments", {
|
|
26616
26730
|
method: "POST",
|
|
26617
26731
|
body: JSON.stringify({
|