mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-25 00:42:24 +00:00
fix: handle Linux nvm CA env before startup (#51146) (thanks @GodsBoy)
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { captureFullEnv } from "../../test-utils/env.js";
|
||||
import type { DaemonActionResponse } from "./response.js";
|
||||
import { captureFullEnv } from "../../test-utils/env.js";
|
||||
|
||||
const resolveAutoNodeExtraCaCertsMock = vi.hoisted(() => vi.fn());
|
||||
const loadConfigMock = vi.hoisted(() => vi.fn());
|
||||
const readConfigFileSnapshotMock = vi.hoisted(() => vi.fn());
|
||||
const resolveGatewayPortMock = vi.hoisted(() => vi.fn(() => 18789));
|
||||
@@ -50,6 +51,10 @@ const service = vi.hoisted(() => ({
|
||||
readRuntime: vi.fn(async () => ({ status: "stopped" as const })),
|
||||
}));
|
||||
|
||||
vi.mock("../../bootstrap/node-extra-ca-certs.js", () => ({
|
||||
resolveAutoNodeExtraCaCerts: resolveAutoNodeExtraCaCertsMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../config/config.js", () => ({
|
||||
loadConfig: loadConfigMock,
|
||||
readBestEffortConfig: loadConfigMock,
|
||||
@@ -151,6 +156,7 @@ const envSnapshot = captureFullEnv();
|
||||
describe("runDaemonInstall", () => {
|
||||
beforeEach(() => {
|
||||
loadConfigMock.mockReset();
|
||||
resolveAutoNodeExtraCaCertsMock.mockReset();
|
||||
readConfigFileSnapshotMock.mockReset();
|
||||
resolveGatewayPortMock.mockClear();
|
||||
writeConfigFileMock.mockReset();
|
||||
@@ -191,6 +197,8 @@ describe("runDaemonInstall", () => {
|
||||
isGatewayDaemonRuntimeMock.mockReturnValue(true);
|
||||
installDaemonServiceAndEmitMock.mockResolvedValue(undefined);
|
||||
service.isLoaded.mockResolvedValue(false);
|
||||
service.readCommand.mockResolvedValue(null);
|
||||
resolveAutoNodeExtraCaCertsMock.mockReturnValue(undefined);
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||
});
|
||||
@@ -289,4 +297,33 @@ describe("runDaemonInstall", () => {
|
||||
expect(actionState.failed[0]?.message).toContain("read-only file system");
|
||||
expect(installDaemonServiceAndEmitMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns already-installed when the service already has the expected TLS env", async () => {
|
||||
service.isLoaded.mockResolvedValue(true);
|
||||
resolveAutoNodeExtraCaCertsMock.mockReturnValue("/etc/ssl/certs/ca-certificates.crt");
|
||||
service.readCommand.mockResolvedValue({
|
||||
programArguments: ["openclaw", "gateway", "run"],
|
||||
environment: {
|
||||
NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/ca-certificates.crt",
|
||||
},
|
||||
} as never);
|
||||
|
||||
await runDaemonInstall({ json: true });
|
||||
|
||||
expect(installDaemonServiceAndEmitMock).not.toHaveBeenCalled();
|
||||
expect(actionState.emitted.at(-1)).toMatchObject({ result: "already-installed" });
|
||||
});
|
||||
|
||||
it("reinstalls when an existing service is missing the nvm TLS CA bundle", async () => {
|
||||
service.isLoaded.mockResolvedValue(true);
|
||||
resolveAutoNodeExtraCaCertsMock.mockReturnValue("/etc/ssl/certs/ca-certificates.crt");
|
||||
service.readCommand.mockResolvedValue({
|
||||
programArguments: ["openclaw", "gateway", "run"],
|
||||
environment: {},
|
||||
} as never);
|
||||
|
||||
await runDaemonInstall({ json: true });
|
||||
|
||||
expect(installDaemonServiceAndEmitMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { DaemonInstallOptions } from "./types.js";
|
||||
import { resolveAutoNodeExtraCaCerts } from "../../bootstrap/node-extra-ca-certs.js";
|
||||
import { buildGatewayInstallPlan } from "../../commands/daemon-install-helpers.js";
|
||||
import {
|
||||
DEFAULT_GATEWAY_DAEMON_RUNTIME,
|
||||
@@ -15,7 +17,6 @@ import {
|
||||
failIfNixDaemonInstallMode,
|
||||
parsePort,
|
||||
} from "./shared.js";
|
||||
import type { DaemonInstallOptions } from "./types.js";
|
||||
|
||||
export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
const { json, stdout, warnings, emit, fail } = createDaemonInstallActionContext(opts.json);
|
||||
@@ -54,19 +55,28 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
}
|
||||
if (loaded) {
|
||||
if (!opts.force) {
|
||||
emit({
|
||||
ok: true,
|
||||
result: "already-installed",
|
||||
message: `Gateway service already ${service.loadedText}.`,
|
||||
service: buildDaemonServiceSnapshot(service, loaded),
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("openclaw gateway install --force")}`,
|
||||
);
|
||||
if (await gatewayServiceNeedsAutoNodeExtraCaCertsRefresh({ service, env: process.env })) {
|
||||
const message = "Gateway service is missing the nvm TLS CA bundle; refreshing the install.";
|
||||
if (json) {
|
||||
warnings.push(message);
|
||||
} else {
|
||||
defaultRuntime.log(message);
|
||||
}
|
||||
} else {
|
||||
emit({
|
||||
ok: true,
|
||||
result: "already-installed",
|
||||
message: `Gateway service already ${service.loadedText}.`,
|
||||
service: buildDaemonServiceSnapshot(service, loaded),
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("openclaw gateway install --force")}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,3 +130,24 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function gatewayServiceNeedsAutoNodeExtraCaCertsRefresh(params: {
|
||||
service: ReturnType<typeof resolveGatewayService>;
|
||||
env: Record<string, string | undefined>;
|
||||
}): Promise<boolean> {
|
||||
const expectedNodeExtraCaCerts = resolveAutoNodeExtraCaCerts({
|
||||
env: params.env,
|
||||
execPath: process.execPath,
|
||||
});
|
||||
if (!expectedNodeExtraCaCerts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const currentCommand = await params.service.readCommand(params.env);
|
||||
const currentNodeExtraCaCerts = currentCommand?.environment?.NODE_EXTRA_CA_CERTS?.trim();
|
||||
return currentNodeExtraCaCerts !== expectedNodeExtraCaCerts;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user