fix(runner): persist codex home in session pvc (#242)

This commit is contained in:
Lyon
2026-06-24 13:47:59 +08:00
committed by GitHub
parent 685a0504f5
commit 3771c2b71a
2 changed files with 32 additions and 2 deletions
+25 -1
View File
@@ -1,7 +1,7 @@
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
import { createHash } from "node:crypto";
import { accessSync, constants as fsConstants, readdirSync, readFileSync } from "node:fs";
import { chmod, copyFile, mkdir } from "node:fs/promises";
import { chmod, copyFile, mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import * as readline from "node:readline";
import type { BackendEvent, BackendProfile, BackendTurnResult, CommandRecord, FailureKind, InitialPromptAssembly, JsonRecord, JsonValue, RunRecord, TerminalStatus } from "../common/types.js";
@@ -852,6 +852,7 @@ async function prepareProjectedCodexHome(codexHome: string, projectedHome: strin
await copyFile(path.join(projectedHome, fileName), path.join(codexHome, fileName));
await chmod(path.join(codexHome, fileName), 0o600);
}
await normalizeCopiedCodexConfig(codexHome);
return null;
} catch (error) {
const payload = {
@@ -875,6 +876,29 @@ async function prepareProjectedCodexHome(codexHome: string, projectedHome: strin
}
}
async function normalizeCopiedCodexConfig(codexHome: string): Promise<void> {
const configPath = path.join(codexHome, "config.toml");
const modelCatalogPath = path.join(codexHome, "model-catalog.json");
if (!fileReadable(modelCatalogPath).readable) return;
let configToml = "";
try {
configToml = readFileSync(configPath, "utf8");
} catch {
return;
}
const normalized = configToml.replace(
/(^\s*model_catalog_json\s*=\s*")([^"]+)("\s*$)/mu,
(_match, prefix: string, _oldValue: string, suffix: string) => `${prefix}${tomlBasicStringValue(modelCatalogPath)}${suffix}`,
);
if (normalized === configToml) return;
await writeFile(configPath, normalized, { mode: 0o600 });
await chmod(configPath, 0o600);
}
function tomlBasicStringValue(value: string): string {
return value.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"");
}
function codexHomeReadiness(codexHome: string, profile: BackendProfile): BackendTurnResult | null {
const auth = fileReadable(`${codexHome}/auth.json`);
const config = fileReadable(`${codexHome}/config.toml`);
+7 -1
View File
@@ -250,7 +250,7 @@ export function renderRunnerJobManifest(options: RunnerJobRenderOptions): { mani
function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string; jobName: string; runnerJobId: string; runnerId: string; attemptId: string; sourceCommit: string; secretRefs: CredentialProjection[]; toolCredentials: ToolCredentialProjection[]; sessionPvc: RunnerSessionPvcOptions | undefined; runnerIdleTimeoutMs: number; missingTerminalAfterToolTimeoutMs: number; backendRetryMaxAttempts: number; backendRetryInitialBackoffMs: number; backendRetryMaxBackoffMs: number }): JsonRecord[] {
const selectedSecret = context.secretRefs.find((item) => item.profile === options.run.backendProfile);
const codexHome = selectedSecret?.runtimeMountPath ?? defaultRuntimeHome(options.run.backendProfile);
const codexHome = runnerCodexHome(options.run.backendProfile, selectedSecret, context.sessionPvc);
const bootRepoUrl = optionalString(options.bootRepoUrl) ?? defaultBootRepoUrl;
return dedupeEnvVars([
{ name: "AGENTRUN_MGR_URL", value: options.managerUrl },
@@ -285,6 +285,7 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
{ name: "AGENTRUN_RUNNER_POLL_INTERVAL_MS", value: "250" },
{ name: "HOME", value: "/home/agentrun" },
{ name: "CODEX_HOME", value: codexHome },
{ name: "AGENTRUN_CODEX_HOME_STORAGE_KIND", value: context.sessionPvc ? "session-pvc" : "runner-home" },
...runnerOtelEnvVars(process.env),
...(selectedSecret ? [{ name: "AGENTRUN_CODEX_SECRET_HOME", value: selectedSecret.projectionMountPath }] : []),
...(context.sessionPvc ? [
@@ -580,6 +581,11 @@ function normalizeMountPath(value: string | undefined, profile: string): string
return value;
}
function runnerCodexHome(profile: string, selectedSecret: CredentialProjection | undefined, sessionPvc: RunnerSessionPvcOptions | undefined): string {
if (!sessionPvc) return selectedSecret?.runtimeMountPath ?? defaultRuntimeHome(profile);
return `${sessionPvc.mountPath}/codex-home/${sanitizeVolumeName(profile)}`;
}
function defaultRuntimeHome(profile: string): string {
return `/home/agentrun/.codex-${sanitizeVolumeName(profile)}`;
}