Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rich-chairs-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/dev-process-manager": minor
---

Refactor status output (as json) in preparation for vscode extension
Comment thread
Fabian-Fynn marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
/lib
/vscode-extension/out
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
"lib/**/*"
],
"scripts": {
"prepare": "husky",
"build": "tsc",
"build-and-publish": "npm run build && npx changeset publish",
"lint": "run-p lint:eslint lint:tsc",
"lint:eslint": "eslint src/",
"lint:tsc": "tsc",
"build": "tsc",
"prepare": "husky",
"start": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",
"build-and-publish": "npm run build && npx changeset publish"
"test:watch": "vitest"
},
"repository": {
"type": "git",
Expand Down
15 changes: 15 additions & 0 deletions shared-types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Shared type definitions used by both the dev-pm package and the vscode-extension.
// This file is intentionally a .d.ts so it can be imported from packages with a
// restricted rootDir (like the vscode-extension) without emitting JS output.

export type ScriptStatus = "started" | "stopping" | "stopped" | "waiting" | "backoff";

export interface ScriptStatusEntry {
id: number;
name: string;
status: ScriptStatus;
cpu: string;
memory: string;
pid: number | undefined;
restarts: number;
}
10 changes: 6 additions & 4 deletions src/commands/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ export async function connect(): Promise<Socket> {
client.on("connect", () => {
resolve(client);
});
client.on("data", (data) => {
//TODO handle stderr/stdin and also write on stderr
process.stdout.write(data);
});
});
}

export function pipeToStdout(client: Socket): void {
client.on("data", (data) => {
process.stdout.write(data);
});
}
3 changes: 2 additions & 1 deletion src/commands/logs.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type LogsCommandOptions } from "../daemon-command/logs.daemon-command.js";
import { connect } from "./connect.js";
import { connect, pipeToStdout } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

export const logs = async (options: LogsCommandOptions): Promise<void> => {
await validConfigOrExit();
const client = await connect();
pipeToStdout(client);
client.write(`logs ${JSON.stringify(options)}`);
};
3 changes: 2 additions & 1 deletion src/commands/restart.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type RestartCommandOptions } from "../daemon-command/restart.daemon-command.js";
import { connect } from "./connect.js";
import { connect, pipeToStdout } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

export const restart = async (options: RestartCommandOptions): Promise<void> => {
await validConfigOrExit();
const client = await connect();
pipeToStdout(client);
client.write(`restart ${JSON.stringify(options)}`);
};
3 changes: 2 additions & 1 deletion src/commands/shutdown.command.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { connect } from "./connect.js";
import { connect, pipeToStdout } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

export const shutdown = async (): Promise<void> => {
await validConfigOrExit();
const client = await connect();
pipeToStdout(client);
client.write("shutdown");
};
10 changes: 8 additions & 2 deletions src/commands/start-daemon.command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import colors from "colors";
import { existsSync, watchFile } from "fs";
import { existsSync, readFileSync, watchFile } from "fs";
import { createServer, type Server } from "net";
import { dirname } from "path";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";

import { logsDaemonCommand } from "../daemon-command/logs.daemon-command.js";
import { restartDaemonCommand } from "../daemon-command/restart.daemon-command.js";
Expand Down Expand Up @@ -86,6 +87,11 @@ export const startDaemon = async (): Promise<void> => {
statusDaemonCommand(daemon, s, names);
} else if (cmd == "shutdown") {
shutdownDaemonCommand(daemon, s);
} else if (cmd == "version") {
const __dirname = dirname(fileURLToPath(import.meta.url));
const { version } = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf-8"));
s.write(`${JSON.stringify({ version })}\n`);
s.end();
} else {
s.write(`unknown command ${cmd}`);
s.end();
Expand Down
3 changes: 2 additions & 1 deletion src/commands/start.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type StartCommandOptions } from "../daemon-command/start.daemon-command.js";
import { connect } from "./connect.js";
import { connect, pipeToStdout } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

export const start = async (options: StartCommandOptions): Promise<void> => {
await validConfigOrExit();
const client = await connect();
pipeToStdout(client);
client.write(`start ${JSON.stringify(options)}`);
};
60 changes: 59 additions & 1 deletion src/commands/status.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,67 @@
import { type StatusCommandOptions } from "../daemon-command/status.daemon-command.js";
import CLITable from "cli-table3";
import colors from "colors";
import { create as createLogUpdate } from "log-update";

import { type ScriptStatus } from "../daemon-command/script.js";
import { type ScriptStatusEntry, type StatusCommandOptions } from "../daemon-command/status.daemon-command.js";
import { connect } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

const statusTexts: { [status in ScriptStatus]: string } = {
started: colors.green("Running"),
stopping: colors.red("Stopping"),
stopped: "Stopped",
waiting: colors.yellow("Waiting"),
backoff: colors.red("Backoff"),
};

function renderTable(entries: ScriptStatusEntry[]): string {
const table = new CLITable({
head: [
colors.blue.bold("ID"),
colors.blue.bold("Script"),
colors.blue.bold("Status"),
colors.blue.bold("CPU"),
colors.blue.bold("Mem"),
colors.bold.blue("PID"),
colors.bold.blue("Restarts"),
],
style: { compact: true },
});
for (const entry of entries) {
table.push([entry.id, entry.name, statusTexts[entry.status], entry.cpu, entry.memory, entry.pid?.toString(), entry.restarts]);
}
return table.toString();
}

export const status = async (options: StatusCommandOptions): Promise<void> => {
await validConfigOrExit();
const client = await connect();

const logUpdate = options.interval ? createLogUpdate(process.stdout) : undefined;
let buffer = "";

client.on("data", (data: Buffer) => {
buffer += data.toString();
let newlineIndex: number;
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
const line = buffer.substring(0, newlineIndex);
buffer = buffer.substring(newlineIndex + 1);

const parsed = JSON.parse(line);
if (parsed.error) {
console.log(parsed.error);
return;
}

const output = renderTable(parsed as ScriptStatusEntry[]);
if (logUpdate) {
logUpdate(output);
} else {
console.log(output);
}
}
});

client.write(`status ${JSON.stringify(options)}`);
};
3 changes: 2 additions & 1 deletion src/commands/stop.command.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type StopCommandOptions } from "../daemon-command/stop.daemon-command.js";
import { connect } from "./connect.js";
import { connect, pipeToStdout } from "./connect.js";
import { validConfigOrExit } from "./validate-config.js";

export const stop = async (options: StopCommandOptions): Promise<void> => {
await validConfigOrExit();
const client = await connect();
pipeToStdout(client);
client.write(`stop ${JSON.stringify(options)}`);
};
2 changes: 1 addition & 1 deletion src/daemon-command/handle-log-socket-close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type Daemon } from "../commands/start-daemon.command.js";

export function handleLogSocketClose(daemon: Daemon, socket: Socket): void {
for (const script of daemon.scripts) {
const index = script.logSockets.findIndex((i) => i == socket);
const index = script.logSockets.findIndex((i) => i.socket == socket);
if (index !== -1) {
script.logSockets.splice(index, 1);
}
Expand Down
7 changes: 4 additions & 3 deletions src/daemon-command/logs.daemon-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { scriptsMatchingPattern, type ScriptsMatchingPatternOptions } from "./sc

export interface LogsCommandOptions extends ScriptsMatchingPatternOptions {
lines?: number;
hideLogPrefix?: boolean;
}

export function logsDaemonCommand(daemon: Daemon, socket: Socket, options: LogsCommandOptions): void {
Expand All @@ -20,7 +21,7 @@ export function logsDaemonCommand(daemon: Daemon, socket: Socket, options: LogsC
for (const script of scriptsToProcess) {
const lastLines = script.logBuffer.slice(-options.lines);
for (const line of lastLines) {
socket.write(`${script.logPrefix}${line}\n`);
socket.write(`${options.hideLogPrefix ? "" : script.logPrefix}${line}\n`);
}
}
socket.end();
Expand All @@ -29,9 +30,9 @@ export function logsDaemonCommand(daemon: Daemon, socket: Socket, options: LogsC

for (const script of scriptsToProcess) {
for (const line of script.logBuffer) {
socket.write(`${script.logPrefix}${line}\n`);
socket.write(`${options.hideLogPrefix ? "" : script.logPrefix}${line}\n`);
}
script.addLogSocket(socket);
script.addLogSocket(socket, { hideLogPrefix: options.hideLogPrefix ?? false });
}

socket.on("close", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/daemon-command/restart.daemon-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function restartDaemonCommand(daemon: Daemon, socket: Socket, optio
script.startProcess(); //don't await

if (options.follow) {
script.addLogSocket(socket);
script.addLogSocket(socket, { hideLogPrefix: false });
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/daemon-command/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { type Socket } from "net";
import path from "path";
import waitOn from "wait-on";

import type { ScriptStatus } from "../../shared-types.js";
import { type ScriptDefinition } from "../script-definition.type.js";

export type { ScriptStatus };

const KEEP_LOG_LINES = 100;
export type ScriptStatus = "started" | "stopping" | "stopped" | "waiting" | "backoff";

let expandedEnv: Record<string, string>;
function loadExpandedEnv() {
Expand All @@ -26,7 +28,7 @@ export class Script {
status: ScriptStatus = "stopped";
process?: ChildProcess;
logBuffer: string[] = [];
logSockets: Socket[] = [];
logSockets: { socket: Socket; hideLogPrefix: boolean }[] = [];
logPrefix: string;
restartCount = 0;

Expand Down Expand Up @@ -92,8 +94,8 @@ export class Script {
}
}

addLogSocket(socket: Socket): void {
this.logSockets.push(socket);
addLogSocket(socket: Socket, options: { hideLogPrefix: boolean }): void {
this.logSockets.push({ socket, ...options });
}

handleLogs(data: Buffer | string): void {
Expand All @@ -102,7 +104,7 @@ export class Script {
for (const line of incomingLines) {
console.log(`${this.logPrefix}${line}`);
this.logSockets.forEach((socket) => {
socket.write(`${this.logPrefix}${line}\n`);
socket.socket.write(`${socket.hideLogPrefix ? "" : this.logPrefix}${line}\n`);
});
}
const removeLines = incomingLines.length - (KEEP_LOG_LINES - this.logBuffer.length);
Expand Down
2 changes: 1 addition & 1 deletion src/daemon-command/start.daemon-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function startDaemonCommand(daemon: Daemon, socket: Socket, options
script.startProcess(); //don't await
}
if (options.follow) {
script.addLogSocket(socket);
script.addLogSocket(socket, { hideLogPrefix: false });
}
}

Expand Down
Loading