fix: stream docker helper logs inline

This commit is contained in:
Pi Web Agent
2026-07-10 20:50:26 +00:00
parent 410242aae8
commit 8f1b6b91ab
5 changed files with 150 additions and 5 deletions
+92
View File
@@ -0,0 +1,92 @@
import { execFile as execFileCallback } from "node:child_process";
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { describe, expect, it } from "vitest";
const execFile = promisify(execFileCallback);
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
describe("pi-web-docker entrypoint", () => {
it("streams detached helper logs inline after scheduling runtime updates", async () => {
const tempDir = await mkdtemp(join(tmpdir(), "pi-web-docker-entrypoint-"));
try {
const runtimeRoot = join(tempDir, "runtime");
const binDir = join(tempDir, "bin");
const dockerCallsPath = join(tempDir, "docker-calls.log");
await mkdir(runtimeRoot);
await mkdir(binDir);
await writeFile(join(runtimeRoot, ".env"), [
`PI_WEB_DOCKER_INSTALL_DIR=${runtimeRoot}`,
"COMPOSE_PROJECT_NAME=pi-web-test",
"PI_WEB_UID=1000",
"PI_WEB_GID=1000",
"DOCKER_GID=998",
"PI_WEB_IMAGE=pi-web:test",
"",
].join("\n"));
const fakeDockerPath = join(binDir, "docker");
await writeFile(fakeDockerPath, fakeDockerScript(dockerCallsPath));
await chmod(fakeDockerPath, 0o755);
const { stdout, stderr } = await execFile(join(repoRoot, "docker/pi-web-docker"), ["update"], {
env: {
...process.env,
PATH: `${binDir}:${process.env["PATH"] ?? ""}`,
PI_WEB_DOCKER_RUNTIME: "1",
PI_WEB_DOCKER_MODE: "runtime",
PI_WEB_DOCKER_INSTALL_DIR: runtimeRoot,
PI_WEB_DOCKER_CONTAINER_ID: "current-web-container",
},
});
const output = `${stdout}${stderr}`;
expect(output).toContain("Started detached PI WEB Docker helper: pi-web-docker-update-");
expect(output).toContain("Streaming detached PI WEB Docker helper logs inline.");
expect(output).toContain("Reconnect with: docker logs -f pi-web-docker-update-");
expect(output).toContain("helper log: update in progress");
expect(output).not.toContain("Follow progress with:");
const dockerCalls = await readFile(dockerCallsPath, "utf8");
expect(dockerCalls).toContain("__run-detached update");
expect(dockerCalls).toMatch(/(?:^|\n)logs -f pi-web-docker-update-\d{14}-\d+(?:\n|$)/);
} finally {
await rm(tempDir, { recursive: true, force: true });
}
});
});
function fakeDockerScript(dockerCallsPath: string): string {
return `#!/usr/bin/env sh
set -eu
printf '%s\\n' "$*" >> ${shellQuote(dockerCallsPath)}
case "$1" in
ps)
exit 0
;;
run)
printf '%s\\n' fake-helper-container-id
;;
logs)
printf '%s\\n' 'helper log: update in progress'
;;
inspect)
printf '%s\\n' 0
;;
rm)
exit 0
;;
*)
printf 'unexpected docker command: %s\\n' "$*" >&2
exit 42
;;
esac
`;
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'"'"'`)}'`;
}
+19 -1
View File
@@ -309,7 +309,9 @@ describe("Docker command assets", () => {
const result = await runDockerCommand(["restart-sessiond"], runtimeEnv(fakeDocker, installDir));
expect(result.stdout).toContain("Started detached PI WEB Docker helper");
expect(result.stdout).toContain("Follow progress with: docker logs -f pi-web-docker-restart-sessiond-");
expect(result.stdout).toContain("Streaming detached PI WEB Docker helper logs inline.");
expect(result.stdout).toContain("Reconnect with: docker logs -f pi-web-docker-restart-sessiond-");
expect(result.stdout).toContain("fake helper log");
const log = await readFile(fakeDocker.logPath, "utf8");
expect(log).toContain("container inspect");
expect(log).toContain("run -d");
@@ -518,6 +520,22 @@ case "\${1:-}" in
printf 'fake-helper-container-id\n'
exit 0
;;
logs)
printf 'fake helper log\n'
exit 0
;;
inspect)
for arg in "$@"; do
case "$arg" in
*State.ExitCode*)
printf '0\n'
exit 0
;;
esac
done
printf '{}\n'
exit 0
;;
esac
printf 'unexpected fake docker args: %s\n' "$*" >&2
exit 9