Archived
fix: improve PI WEB updates panel commands
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@jmfederico/pi-web": patch
|
||||
---
|
||||
|
||||
Avoid suggesting unavailable `pi-web` restart commands for local checkout installs, and show native service commands only when PI WEB can detect matching service files.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@jmfederico/pi-web": patch
|
||||
---
|
||||
|
||||
Rename the PI WEB status workspace tab to Updates so version and restart guidance is easier to find.
|
||||
@@ -69,12 +69,30 @@ function renderCommand(html: HtmlTemplateTag, label: string, command: string): T
|
||||
`;
|
||||
}
|
||||
|
||||
function renderCommands(html: HtmlTemplateTag, status: PiWebStatusResponse): TemplateResult | undefined {
|
||||
const commands = [
|
||||
["Update", status.commands.update],
|
||||
["Restart all", status.commands.restart],
|
||||
["Restart Web/UI", status.commands.restartWeb],
|
||||
["Restart session daemon", status.commands.restartSessiond],
|
||||
["Status", status.commands.status],
|
||||
].filter((entry): entry is [string, string] => typeof entry[1] === "string" && entry[1] !== "");
|
||||
|
||||
if (commands.length === 0) return undefined;
|
||||
return html`
|
||||
<section>
|
||||
<strong>Suggested commands</strong>
|
||||
${commands.map(([label, command]) => renderCommand(html, label, command))}
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderStatusPanel(html: HtmlTemplateTag, state: AppState): TemplateResult {
|
||||
const status = statusFor(state);
|
||||
if (status === undefined) {
|
||||
return html`
|
||||
<section class="toolbar"><strong>PI WEB</strong></section>
|
||||
<section class="viewer"><p class="muted">Checking PI WEB status…</p></section>
|
||||
<section class="toolbar"><strong>Updates</strong></section>
|
||||
<section class="viewer"><p class="muted">Checking PI WEB update status…</p></section>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -98,7 +116,7 @@ function renderStatusPanel(html: HtmlTemplateTag, state: AppState): TemplateResu
|
||||
.pi-web-command > span { grid-column: 1 / -1; }
|
||||
}
|
||||
</style>
|
||||
<section class="toolbar"><strong>PI WEB</strong><span class="stale">beta</span>${messages.length > 0 ? html`<span class="stale">${String(messages.length)}</span>` : null}</section>
|
||||
<section class="toolbar"><strong>Updates</strong><span class="stale">beta</span>${messages.length > 0 ? html`<span class="stale">${String(messages.length)}</span>` : null}</section>
|
||||
<section class="viewer pi-web-status">
|
||||
<section>
|
||||
${messages.length === 0 ? html`<p class="muted">No PI WEB update or restart messages.</p>` : messages.map((message) => html`
|
||||
@@ -116,13 +134,7 @@ function renderStatusPanel(html: HtmlTemplateTag, state: AppState): TemplateResu
|
||||
${renderComponent(html, status.components.sessiond)}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<strong>Commands</strong>
|
||||
${renderCommand(html, "Update", status.commands.update)}
|
||||
${renderCommand(html, "Restart", status.commands.restart)}
|
||||
${renderCommand(html, "systemd", status.commands.restartSystemd)}
|
||||
${renderCommand(html, "dev", status.commands.restartDev)}
|
||||
</section>
|
||||
${renderCommands(html, status)}
|
||||
|
||||
<section class="pi-web-meta">
|
||||
<span>Generated ${status.generatedAt}</span>
|
||||
@@ -136,13 +148,13 @@ function renderStatusPanel(html: HtmlTemplateTag, state: AppState): TemplateResu
|
||||
|
||||
const plugin: PiWebPlugin = {
|
||||
apiVersion: 1,
|
||||
name: "PI WEB Status",
|
||||
name: "PI WEB Updates",
|
||||
activate: ({ html }) => ({
|
||||
contributions: {
|
||||
workspacePanels: [
|
||||
{
|
||||
id: "workspace.status",
|
||||
title: "PI WEB",
|
||||
title: "Updates",
|
||||
order: 100,
|
||||
visible: (context) => shouldShowStatusPanel(context.state),
|
||||
badge: (context) => {
|
||||
|
||||
@@ -418,7 +418,13 @@ function parsePiWebReleaseStatus(value: unknown): PiWebReleaseStatus {
|
||||
|
||||
function parsePiWebCommands(value: unknown): PiWebStatusResponse["commands"] {
|
||||
const record = requireRecord(value);
|
||||
return { update: requireString(record, "update"), restart: requireString(record, "restart"), restartSystemd: requireString(record, "restartSystemd"), restartDev: requireString(record, "restartDev") };
|
||||
return {
|
||||
...optionalField("update", optionalString(record, "update")),
|
||||
...optionalField("restart", optionalString(record, "restart")),
|
||||
...optionalField("restartWeb", optionalString(record, "restartWeb")),
|
||||
...optionalField("restartSessiond", optionalString(record, "restartSessiond")),
|
||||
...optionalField("status", optionalString(record, "status")),
|
||||
};
|
||||
}
|
||||
|
||||
function parsePiWebStatusMessage(value: unknown): PiWebStatusMessage {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { comparePackageVersions, getPiWebStatus, getPiWebVersionStatus } from "./piWebStatus.js";
|
||||
import { SessionDaemonClient } from "../sessiond/sessionDaemonClient.js";
|
||||
import type { PiWebComponentStatus } from "../shared/apiTypes.js";
|
||||
|
||||
const originalSkipVersionCheck = process.env["PI_WEB_SKIP_VERSION_CHECK"];
|
||||
const originalHome = process.env["HOME"];
|
||||
|
||||
afterEach(() => {
|
||||
if (originalSkipVersionCheck === undefined) delete process.env["PI_WEB_SKIP_VERSION_CHECK"];
|
||||
else process.env["PI_WEB_SKIP_VERSION_CHECK"] = originalSkipVersionCheck;
|
||||
restoreEnv("PI_WEB_SKIP_VERSION_CHECK", originalSkipVersionCheck);
|
||||
restoreEnv("HOME", originalHome);
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
@@ -18,20 +23,13 @@ describe("PI WEB status", () => {
|
||||
});
|
||||
|
||||
it("returns installed and running version components without release metadata", async () => {
|
||||
const daemon = new SessionDaemonClient();
|
||||
vi.spyOn(daemon, "request").mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
version: {
|
||||
component: "sessiond",
|
||||
label: "Session daemon",
|
||||
runtimeVersion: "1.202605.7",
|
||||
installedVersion: "1.202605.8",
|
||||
stale: true,
|
||||
available: true,
|
||||
},
|
||||
}),
|
||||
const daemon = daemonWithComponent({
|
||||
component: "sessiond",
|
||||
label: "Session daemon",
|
||||
runtimeVersion: "1.202605.7",
|
||||
installedVersion: "1.202605.8",
|
||||
stale: true,
|
||||
available: true,
|
||||
});
|
||||
|
||||
const status = await getPiWebVersionStatus(daemon);
|
||||
@@ -44,21 +42,14 @@ describe("PI WEB status", () => {
|
||||
|
||||
it("reports stale session daemon versions as messages", async () => {
|
||||
process.env["PI_WEB_SKIP_VERSION_CHECK"] = "1";
|
||||
const daemon = new SessionDaemonClient();
|
||||
vi.spyOn(daemon, "request").mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
version: {
|
||||
component: "sessiond",
|
||||
label: "Session daemon",
|
||||
runtimeVersion: "1.202605.7",
|
||||
installedVersion: "1.202605.8",
|
||||
stale: true,
|
||||
available: true,
|
||||
installation: { kind: "pi-package", source: "npm:@jmfederico/pi-web", scope: "user", path: "/tmp/pi-web" },
|
||||
},
|
||||
}),
|
||||
const daemon = daemonWithComponent({
|
||||
component: "sessiond",
|
||||
label: "Session daemon",
|
||||
runtimeVersion: "1.202605.7",
|
||||
installedVersion: "1.202605.8",
|
||||
stale: true,
|
||||
available: true,
|
||||
installation: { kind: "pi-package", source: "npm:@jmfederico/pi-web", scope: "user", path: "/tmp/pi-web" },
|
||||
});
|
||||
|
||||
const status = await getPiWebStatus(daemon);
|
||||
@@ -66,7 +57,81 @@ describe("PI WEB status", () => {
|
||||
expect(status.release.skipped).toBe(true);
|
||||
expect(status.components.sessiond.stale).toBe(true);
|
||||
expect(status.components.sessiond.installation).toMatchObject({ kind: "pi-package", source: "npm:@jmfederico/pi-web", scope: "user" });
|
||||
expect(status.commands.update).not.toBe("");
|
||||
expect(status.messages.map((message) => message.id)).toContain("sessiond-stale");
|
||||
});
|
||||
|
||||
it("suggests native systemd commands for local development services", async () => {
|
||||
if (process.platform !== "linux") return;
|
||||
process.env["PI_WEB_SKIP_VERSION_CHECK"] = "1";
|
||||
const home = await tempHome();
|
||||
try {
|
||||
process.env["HOME"] = home;
|
||||
await installSystemdServiceFiles(home, ["pi-web-sessiond.service", "pi-web-ui-dev.service"]);
|
||||
const daemon = daemonWithComponent(staleLocalSessiond());
|
||||
|
||||
const status = await getPiWebStatus(daemon);
|
||||
|
||||
expect(status.commands.restart).toBe("systemctl --user restart pi-web-sessiond.service pi-web-ui-dev.service");
|
||||
expect(status.commands.restartWeb).toBe("systemctl --user restart pi-web-ui-dev.service");
|
||||
expect(status.commands.restartSessiond).toBe("systemctl --user restart pi-web-sessiond.service");
|
||||
expect(status.messages.find((message) => message.id === "sessiond-stale")?.command).toBe("systemctl --user restart pi-web-sessiond.service");
|
||||
} finally {
|
||||
await rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("omits local restart commands when no native service command is known", async () => {
|
||||
process.env["PI_WEB_SKIP_VERSION_CHECK"] = "1";
|
||||
const home = await tempHome();
|
||||
try {
|
||||
process.env["HOME"] = home;
|
||||
const daemon = daemonWithComponent(staleLocalSessiond());
|
||||
|
||||
const status = await getPiWebStatus(daemon);
|
||||
const staleMessage = status.messages.find((message) => message.id === "sessiond-stale");
|
||||
|
||||
expect(status.commands.restart).toBeUndefined();
|
||||
expect(staleMessage?.command).toBeUndefined();
|
||||
expect(JSON.stringify(status)).not.toContain("pi-web restart");
|
||||
} finally {
|
||||
await rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function daemonWithComponent(component: PiWebComponentStatus): SessionDaemonClient {
|
||||
const daemon = new SessionDaemonClient();
|
||||
vi.spyOn(daemon, "request").mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ version: component }),
|
||||
});
|
||||
return daemon;
|
||||
}
|
||||
|
||||
function staleLocalSessiond(): PiWebComponentStatus {
|
||||
return {
|
||||
component: "sessiond",
|
||||
label: "Session daemon",
|
||||
runtimeVersion: "1.202605.7",
|
||||
installedVersion: "1.202605.8",
|
||||
stale: true,
|
||||
available: true,
|
||||
installation: { kind: "local", path: "/srv/dev/pi-web" },
|
||||
};
|
||||
}
|
||||
|
||||
async function tempHome(): Promise<string> {
|
||||
return await mkdtemp(join(tmpdir(), "pi-web-status-"));
|
||||
}
|
||||
|
||||
async function installSystemdServiceFiles(home: string, names: string[]): Promise<void> {
|
||||
const dir = join(home, ".config", "systemd", "user");
|
||||
await mkdir(dir, { recursive: true });
|
||||
await Promise.all(names.map((name) => writeFile(join(dir, name), "")));
|
||||
}
|
||||
|
||||
function restoreEnv(key: string, value: string | undefined): void {
|
||||
if (value === undefined) Reflect.deleteProperty(process.env, key);
|
||||
else process.env[key] = value;
|
||||
}
|
||||
|
||||
+171
-21
@@ -1,6 +1,7 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { readFile, realpath, stat } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { dirname, join, relative, resolve, sep } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { DefaultPackageManager, getAgentDir, SettingsManager } from "@earendil-works/pi-coding-agent";
|
||||
@@ -14,12 +15,46 @@ const DEFAULT_VERSION = "0.0.0-dev";
|
||||
const LATEST_RELEASE_CACHE_MS = 6 * 60 * 60 * 1000;
|
||||
const VERSION_CHECK_TIMEOUT_MS = 5000;
|
||||
|
||||
const restartCommands = {
|
||||
restart: "pi-web restart",
|
||||
restartSystemd: "pi-web restart",
|
||||
restartDev: "pi-web restart",
|
||||
type ServiceId = "sessiond" | "web" | "uiDev";
|
||||
type NativeServiceBackendKind = "systemd" | "launchd";
|
||||
|
||||
interface NativeServiceRef {
|
||||
id: ServiceId;
|
||||
systemdName: string;
|
||||
launchdLabel: string;
|
||||
launchdPlistName: string;
|
||||
}
|
||||
|
||||
interface NativeServiceCommands {
|
||||
restart?: string;
|
||||
restartWeb?: string;
|
||||
restartSessiond?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
const serviceRefs: Record<ServiceId, NativeServiceRef> = {
|
||||
sessiond: {
|
||||
id: "sessiond",
|
||||
systemdName: "pi-web-sessiond.service",
|
||||
launchdLabel: "com.pi-web.sessiond",
|
||||
launchdPlistName: "com.pi-web.sessiond.plist",
|
||||
},
|
||||
web: {
|
||||
id: "web",
|
||||
systemdName: "pi-web.service",
|
||||
launchdLabel: "com.pi-web.web",
|
||||
launchdPlistName: "com.pi-web.web.plist",
|
||||
},
|
||||
uiDev: {
|
||||
id: "uiDev",
|
||||
systemdName: "pi-web-ui-dev.service",
|
||||
launchdLabel: "com.pi-web.ui-dev",
|
||||
launchdPlistName: "com.pi-web.ui-dev.plist",
|
||||
},
|
||||
};
|
||||
|
||||
const startServiceOrder: ServiceId[] = ["sessiond", "web", "uiDev"];
|
||||
|
||||
interface PackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
@@ -65,7 +100,7 @@ export async function getPiWebStatus(daemon = new SessionDaemonClient()): Promis
|
||||
const { web, sessiond } = versionStatus.components;
|
||||
const release = await getLatestReleaseStatus(web.installedVersion ?? web.runtimeVersion ?? DEFAULT_VERSION);
|
||||
const components = { web, sessiond };
|
||||
const commands = commandsFor(web.installation ?? sessiond.installation);
|
||||
const commands = commandsFor(components);
|
||||
const messages = buildMessages(components, release, commands);
|
||||
return {
|
||||
...versionStatus,
|
||||
@@ -247,21 +282,122 @@ async function fetchLatestNpmVersion(currentVersion: string): Promise<string> {
|
||||
return version;
|
||||
}
|
||||
|
||||
function commandsFor(installation: PiWebInstallationInfo | undefined): PiWebStatusResponse["commands"] {
|
||||
function commandsFor(components: PiWebStatusResponse["components"]): PiWebStatusResponse["commands"] {
|
||||
const installation = preferredInstallation(components);
|
||||
const serviceCommands = nativeServiceCommands();
|
||||
const cliCommands = piWebCliCommands(installation);
|
||||
const restart = restartCommandFor(installation, serviceCommands, cliCommands);
|
||||
const restartWeb = serviceCommands.restartWeb ?? cliCommands.restart;
|
||||
const restartSessiond = serviceCommands.restartSessiond ?? cliCommands.restart;
|
||||
const status = serviceCommands.status ?? cliCommands.status;
|
||||
const update = updateCommandFor(installation, restart);
|
||||
|
||||
return {
|
||||
update: updateCommandFor(installation),
|
||||
...restartCommands,
|
||||
...(update === undefined ? {} : { update }),
|
||||
...(restart === undefined ? {} : { restart }),
|
||||
...(restartWeb === undefined ? {} : { restartWeb }),
|
||||
...(restartSessiond === undefined ? {} : { restartSessiond }),
|
||||
...(status === undefined ? {} : { status }),
|
||||
};
|
||||
}
|
||||
|
||||
function updateCommandFor(installation: PiWebInstallationInfo | undefined): string {
|
||||
function preferredInstallation(components: PiWebStatusResponse["components"]): PiWebInstallationInfo | undefined {
|
||||
const web = components.web.installation;
|
||||
const sessiond = components.sessiond.installation;
|
||||
if (web?.kind === "local" || sessiond?.kind === "local") return web?.kind === "local" ? web : sessiond;
|
||||
return web ?? sessiond;
|
||||
}
|
||||
|
||||
function piWebCliCommands(installation: PiWebInstallationInfo | undefined): NativeServiceCommands {
|
||||
if (installation?.kind !== "npm-global" || !hasCommand("pi-web")) return {};
|
||||
return { restart: "pi-web restart", status: "pi-web status" };
|
||||
}
|
||||
|
||||
function restartCommandFor(installation: PiWebInstallationInfo | undefined, serviceCommands: NativeServiceCommands, cliCommands: NativeServiceCommands): string | undefined {
|
||||
if (installation?.kind === "local" || installation?.kind === "pi-package") return serviceCommands.restart ?? cliCommands.restart;
|
||||
return cliCommands.restart ?? serviceCommands.restart;
|
||||
}
|
||||
|
||||
function updateCommandFor(installation: PiWebInstallationInfo | undefined, restartCommand: string | undefined): string | undefined {
|
||||
if (restartCommand === undefined) return undefined;
|
||||
if (installation?.kind === "pi-package") {
|
||||
return `pi update ${installation.source ?? PI_WEB_NPM_SOURCE} && ${restartCommands.restart}`;
|
||||
if (!hasCommand("pi")) return undefined;
|
||||
return `pi update ${installation.source ?? PI_WEB_NPM_SOURCE} && ${restartCommand}`;
|
||||
}
|
||||
if (installation?.kind === "local" && installation.path !== undefined) {
|
||||
return `cd ${shellQuote(installation.path)} && git pull && npm install && npm run build && ${restartCommands.restart}`;
|
||||
if (!hasCommand("npm") || !isGitCheckoutWithUpstream(installation.path)) return undefined;
|
||||
return `cd ${shellQuote(installation.path)} && git pull --ff-only && npm install && npm run build && ${restartCommand}`;
|
||||
}
|
||||
return `npm install -g ${PI_WEB_PACKAGE_NAME} && ${restartCommands.restart}`;
|
||||
if (installation?.kind !== "npm-global" || !hasCommand("npm")) return undefined;
|
||||
return `npm install -g ${PI_WEB_PACKAGE_NAME} && ${restartCommand}`;
|
||||
}
|
||||
|
||||
function nativeServiceCommands(): NativeServiceCommands {
|
||||
const backend = nativeServiceBackend();
|
||||
if (backend === undefined) return {};
|
||||
const installed = installedServiceIds(backend);
|
||||
if (installed.size === 0) return {};
|
||||
const web = installedServiceRefs(installed, ["web", "uiDev"]);
|
||||
const sessiond = installedServiceRefs(installed, ["sessiond"]);
|
||||
const restartable = web.length === 0 ? [] : installedServiceRefs(installed);
|
||||
const status = installedServiceRefs(installed);
|
||||
return {
|
||||
...(restartable.length === 0 ? {} : { restart: restartNativeServicesCommand(backend, restartable) }),
|
||||
...(web.length === 0 ? {} : { restartWeb: restartNativeServicesCommand(backend, web) }),
|
||||
...(sessiond.length === 0 ? {} : { restartSessiond: restartNativeServicesCommand(backend, sessiond) }),
|
||||
...(status.length === 0 ? {} : { status: statusNativeServicesCommand(backend, status) }),
|
||||
};
|
||||
}
|
||||
|
||||
function nativeServiceBackend(): NativeServiceBackendKind | undefined {
|
||||
if (process.platform === "linux" && hasCommand("systemctl")) return "systemd";
|
||||
if (process.platform === "darwin" && hasCommand("launchctl")) return "launchd";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function installedServiceIds(backend: NativeServiceBackendKind): Set<ServiceId> {
|
||||
return new Set(startServiceOrder.filter((id) => existsSync(serviceFilePath(backend, serviceRefs[id]))));
|
||||
}
|
||||
|
||||
function installedServiceRefs(installed: Set<ServiceId>, candidates: ServiceId[] = startServiceOrder): NativeServiceRef[] {
|
||||
return startServiceOrder.filter((id) => candidates.includes(id) && installed.has(id)).map((id) => serviceRefs[id]);
|
||||
}
|
||||
|
||||
function serviceFilePath(backend: NativeServiceBackendKind, ref: NativeServiceRef): string {
|
||||
return backend === "systemd" ? join(systemdServiceDir(), ref.systemdName) : join(launchdServiceDir(), ref.launchdPlistName);
|
||||
}
|
||||
|
||||
function systemdServiceDir(): string {
|
||||
return join(homedir(), ".config", "systemd", "user");
|
||||
}
|
||||
|
||||
function launchdServiceDir(): string {
|
||||
return join(homedir(), "Library", "LaunchAgents");
|
||||
}
|
||||
|
||||
function restartNativeServicesCommand(backend: NativeServiceBackendKind, refs: NativeServiceRef[]): string {
|
||||
if (backend === "systemd") return `systemctl --user restart ${refs.map((ref) => ref.systemdName).join(" ")}`;
|
||||
return refs.map((ref) => `launchctl kickstart -k gui/$(id -u)/${ref.launchdLabel}`).join(" && ");
|
||||
}
|
||||
|
||||
function statusNativeServicesCommand(backend: NativeServiceBackendKind, refs: NativeServiceRef[]): string {
|
||||
if (backend === "systemd") return `systemctl --user status ${refs.map((ref) => ref.systemdName).join(" ")}`;
|
||||
return refs.map((ref) => `launchctl print gui/$(id -u)/${ref.launchdLabel}`).join(" && ");
|
||||
}
|
||||
|
||||
function isGitCheckoutWithUpstream(path: string): boolean {
|
||||
return hasCommand("git")
|
||||
&& commandSucceeds("git", ["-C", path, "rev-parse", "--is-inside-work-tree"])
|
||||
&& commandSucceeds("git", ["-C", path, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
|
||||
}
|
||||
|
||||
function hasCommand(command: string): boolean {
|
||||
return commandSucceeds("/usr/bin/env", ["sh", "-c", `command -v ${command}`]);
|
||||
}
|
||||
|
||||
function commandSucceeds(command: string, args: string[]): boolean {
|
||||
const result = spawnSync(command, args, { encoding: "utf8" });
|
||||
return result.status === 0;
|
||||
}
|
||||
|
||||
function shellQuote(value: string): string {
|
||||
@@ -277,18 +413,23 @@ function buildMessages(components: PiWebStatusResponse["components"], release: P
|
||||
id: "update-available",
|
||||
severity: "info",
|
||||
title: "PI WEB update available",
|
||||
body: `PI WEB ${release.latestVersion} is available${installedVersion === undefined ? "" : `; installed version is ${installedVersion}`}. Update PI WEB, then restart PI WEB services.`,
|
||||
command: commands.update,
|
||||
body: commands.update === undefined
|
||||
? `PI WEB ${release.latestVersion} is available${installedVersion === undefined ? "" : `; installed version is ${installedVersion}`}. Update PI WEB, then restart the services or processes for this installation.`
|
||||
: `PI WEB ${release.latestVersion} is available${installedVersion === undefined ? "" : `; installed version is ${installedVersion}`}. Run the update command to update PI WEB and restart its services.`,
|
||||
...optionalMessageCommand(commands.update),
|
||||
});
|
||||
}
|
||||
|
||||
if (components.web.stale) {
|
||||
const command = commands.restartWeb ?? commands.restart;
|
||||
messages.push({
|
||||
id: "web-stale",
|
||||
severity: "warning",
|
||||
title: "Web/UI service restart needed",
|
||||
body: `The Web/UI service is running ${formatVersion(components.web.runtimeVersion)}, but ${formatVersion(components.web.installedVersion)} is installed. Restart the service to use the installed version.`,
|
||||
command: commands.restart,
|
||||
body: command === undefined
|
||||
? `The Web/UI service is running ${formatVersion(components.web.runtimeVersion)}, but ${formatVersion(components.web.installedVersion)} is installed. Restart the Web/UI service or process to use the installed version.`
|
||||
: `The Web/UI service is running ${formatVersion(components.web.runtimeVersion)}, but ${formatVersion(components.web.installedVersion)} is installed. Restart the service to use the installed version.`,
|
||||
...optionalMessageCommand(command),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -297,22 +438,31 @@ function buildMessages(components: PiWebStatusResponse["components"], release: P
|
||||
id: "sessiond-unavailable",
|
||||
severity: "warning",
|
||||
title: "Session daemon version unavailable",
|
||||
body: `PI WEB could not check the session daemon version${components.sessiond.error === undefined ? "." : `: ${components.sessiond.error}`}`,
|
||||
command: "pi-web status",
|
||||
body: commands.status === undefined
|
||||
? `PI WEB could not check the session daemon version${components.sessiond.error === undefined ? "." : `: ${components.sessiond.error}`}. Check the session daemon service or process that runs this installation.`
|
||||
: `PI WEB could not check the session daemon version${components.sessiond.error === undefined ? "." : `: ${components.sessiond.error}`}`,
|
||||
...optionalMessageCommand(commands.status),
|
||||
});
|
||||
} else if (components.sessiond.stale) {
|
||||
const command = commands.restartSessiond ?? commands.restart;
|
||||
messages.push({
|
||||
id: "sessiond-stale",
|
||||
severity: "warning",
|
||||
title: "Session daemon restart needed",
|
||||
body: `The session daemon is running ${formatVersion(components.sessiond.runtimeVersion)}, but ${formatVersion(components.sessiond.installedVersion)} is installed. Restart the daemon to use the installed version.`,
|
||||
command: commands.restart,
|
||||
body: command === undefined
|
||||
? `The session daemon is running ${formatVersion(components.sessiond.runtimeVersion)}, but ${formatVersion(components.sessiond.installedVersion)} is installed. Restart the session daemon service or process to use the installed version.`
|
||||
: `The session daemon is running ${formatVersion(components.sessiond.runtimeVersion)}, but ${formatVersion(components.sessiond.installedVersion)} is installed. Restart the daemon to use the installed version.`,
|
||||
...optionalMessageCommand(command),
|
||||
});
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
function optionalMessageCommand(command: string | undefined): Pick<PiWebStatusMessage, "command"> | object {
|
||||
return command === undefined ? {} : { command };
|
||||
}
|
||||
|
||||
function skipVersionCheck(): boolean {
|
||||
return ["PI_WEB_SKIP_VERSION_CHECK", "PI_WEB_OFFLINE", "PI_SKIP_VERSION_CHECK", "PI_OFFLINE"].some((key) => {
|
||||
const value = process.env[key];
|
||||
|
||||
@@ -296,10 +296,11 @@ export interface PiWebVersionResponse {
|
||||
export interface PiWebStatusResponse extends PiWebVersionResponse {
|
||||
release: PiWebReleaseStatus;
|
||||
commands: {
|
||||
update: string;
|
||||
restart: string;
|
||||
restartSystemd: string;
|
||||
restartDev: string;
|
||||
update?: string;
|
||||
restart?: string;
|
||||
restartWeb?: string;
|
||||
restartSessiond?: string;
|
||||
status?: string;
|
||||
};
|
||||
messages: PiWebStatusMessage[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user