fix: persist session workspace across runs
This commit is contained in:
@@ -142,7 +142,9 @@ export async function createKubernetesRunnerJob(options: { store: AgentRunStore;
|
|||||||
const pvcName = refreshed?.storagePvcName ?? ensured.pvcName;
|
const pvcName = refreshed?.storagePvcName ?? ensured.pvcName;
|
||||||
if (!pvcName) throw new AgentRunError("infra-failed", `session ${run.sessionRef.sessionId} PVC was not resolved for runner job`, { httpStatus: 502 });
|
if (!pvcName) throw new AgentRunError("infra-failed", `session ${run.sessionRef.sessionId} PVC was not resolved for runner job`, { httpStatus: 502 });
|
||||||
const subdir = refreshed?.codexRolloutSubdir ?? ensured.codexRolloutSubdir ?? "sessions";
|
const subdir = refreshed?.codexRolloutSubdir ?? ensured.codexRolloutSubdir ?? "sessions";
|
||||||
sessionPvc = { pvcName, namespace: refreshed?.storageNamespace ?? ensured.namespace ?? namespace, mountPath: `/home/agentrun/.codex-${run.backendProfile}/${subdir}`, codexRolloutSubdir: subdir };
|
const mountPath = `/home/agentrun/.codex-${run.backendProfile}/${subdir}`;
|
||||||
|
const workspacePath = `${mountPath}/agentrun-workspace`;
|
||||||
|
sessionPvc = { pvcName, namespace: refreshed?.storageNamespace ?? ensured.namespace ?? namespace, mountPath, codexRolloutSubdir: subdir, workspacePath };
|
||||||
sessionPvcSummary = {
|
sessionPvcSummary = {
|
||||||
sessionId: run.sessionRef.sessionId,
|
sessionId: run.sessionRef.sessionId,
|
||||||
pvcName: sessionPvc.pvcName,
|
pvcName: sessionPvc.pvcName,
|
||||||
@@ -150,6 +152,7 @@ export async function createKubernetesRunnerJob(options: { store: AgentRunStore;
|
|||||||
pvcPhase: refreshed?.storagePvcPhase ?? ensured.pvcPhase ?? null,
|
pvcPhase: refreshed?.storagePvcPhase ?? ensured.pvcPhase ?? null,
|
||||||
mountPath: sessionPvc.mountPath,
|
mountPath: sessionPvc.mountPath,
|
||||||
codexRolloutSubdir: sessionPvc.codexRolloutSubdir,
|
codexRolloutSubdir: sessionPvc.codexRolloutSubdir,
|
||||||
|
workspacePath: sessionPvc.workspacePath ?? null,
|
||||||
valuesPrinted: false,
|
valuesPrinted: false,
|
||||||
};
|
};
|
||||||
if (ensured.pvcPhase === "NotFound" || ensured.pvcPhase === "Unknown") {
|
if (ensured.pvcPhase === "NotFound" || ensured.pvcPhase === "Unknown") {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export interface RunnerSessionPvcOptions {
|
|||||||
namespace: string;
|
namespace: string;
|
||||||
mountPath: string;
|
mountPath: string;
|
||||||
codexRolloutSubdir: string;
|
codexRolloutSubdir: string;
|
||||||
|
workspacePath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RunnerTransientEnv {
|
export interface RunnerTransientEnv {
|
||||||
@@ -279,6 +280,8 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
|
|||||||
{ name: "AGENTRUN_SESSION_PVC_NAMESPACE", value: context.sessionPvc.namespace },
|
{ name: "AGENTRUN_SESSION_PVC_NAMESPACE", value: context.sessionPvc.namespace },
|
||||||
{ name: "AGENTRUN_SESSION_PVC_MOUNT_PATH", value: context.sessionPvc.mountPath },
|
{ name: "AGENTRUN_SESSION_PVC_MOUNT_PATH", value: context.sessionPvc.mountPath },
|
||||||
{ name: "AGENTRUN_CODEX_ROLLOUT_SUBDIR", value: context.sessionPvc.codexRolloutSubdir },
|
{ name: "AGENTRUN_CODEX_ROLLOUT_SUBDIR", value: context.sessionPvc.codexRolloutSubdir },
|
||||||
|
{ name: "AGENTRUN_WORKSPACE_PATH", value: context.sessionPvc.workspacePath ?? `${context.sessionPvc.mountPath}/agentrun-workspace` },
|
||||||
|
{ name: "AGENTRUN_SESSION_WORKSPACE_PATH", value: context.sessionPvc.workspacePath ?? `${context.sessionPvc.mountPath}/agentrun-workspace` },
|
||||||
] : []),
|
] : []),
|
||||||
...runnerEgressProxyEnvVars(),
|
...runnerEgressProxyEnvVars(),
|
||||||
...runnerGitTransportEnvVars(),
|
...runnerGitTransportEnvVars(),
|
||||||
|
|||||||
@@ -88,7 +88,11 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
|||||||
const runScope = env.AGENTRUN_RUN_ID ?? env.AGENTRUN_ATTEMPT_ID ?? "standalone";
|
const runScope = env.AGENTRUN_RUN_ID ?? env.AGENTRUN_ATTEMPT_ID ?? "standalone";
|
||||||
const assemblyRoot = path.join(workspaceRoot, `gitbundle-${stableHash({ runScope, resourceBundleRef }).slice(0, 16)}`);
|
const assemblyRoot = path.join(workspaceRoot, `gitbundle-${stableHash({ runScope, resourceBundleRef }).slice(0, 16)}`);
|
||||||
const checkoutRoot = path.join(assemblyRoot, "checkouts");
|
const checkoutRoot = path.join(assemblyRoot, "checkouts");
|
||||||
const workspacePath = path.join(assemblyRoot, "workspace");
|
const explicitWorkspacePath = optionalNonEmpty(env.AGENTRUN_WORKSPACE_PATH);
|
||||||
|
const workspacePath = explicitWorkspacePath ? path.resolve(explicitWorkspacePath) : path.join(assemblyRoot, "workspace");
|
||||||
|
if (explicitWorkspacePath && isSameOrChildPath(workspacePath, assemblyRoot)) {
|
||||||
|
throw new AgentRunError("schema-invalid", "AGENTRUN_WORKSPACE_PATH must not be inside transient resource assembly root", { httpStatus: 400, details: { workspacePath: pathSummary(workspacePath), assemblyRoot: pathSummary(assemblyRoot), valuesPrinted: false } });
|
||||||
|
}
|
||||||
await rm(assemblyRoot, { recursive: true, force: true });
|
await rm(assemblyRoot, { recursive: true, force: true });
|
||||||
await mkdir(checkoutRoot, { recursive: true });
|
await mkdir(checkoutRoot, { recursive: true });
|
||||||
await mkdir(workspacePath, { recursive: true });
|
await mkdir(workspacePath, { recursive: true });
|
||||||
@@ -130,6 +134,7 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
|||||||
treeId: defaultCheckout.treeId,
|
treeId: defaultCheckout.treeId,
|
||||||
checkoutPath: pathSummary(defaultCheckout.checkoutPath),
|
checkoutPath: pathSummary(defaultCheckout.checkoutPath),
|
||||||
workspacePath: pathSummary(workspacePath),
|
workspacePath: pathSummary(workspacePath),
|
||||||
|
workspacePersistence: explicitWorkspacePath ? { mode: "session", path: pathSummary(workspacePath), valuesPrinted: false } : { mode: "run", path: pathSummary(workspacePath), valuesPrinted: false },
|
||||||
gitTransport: gitTransportSummary(),
|
gitTransport: gitTransportSummary(),
|
||||||
bundles: {
|
bundles: {
|
||||||
count: materializedBundles.length,
|
count: materializedBundles.length,
|
||||||
@@ -146,6 +151,11 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSameOrChildPath(candidate: string, parent: string): boolean {
|
||||||
|
const relative = path.relative(parent, candidate);
|
||||||
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
||||||
|
}
|
||||||
|
|
||||||
function gitMirrorConfig(resourceBundleRef: ResourceBundleRef, env: NodeJS.ProcessEnv): GitMirrorConfig | undefined {
|
function gitMirrorConfig(resourceBundleRef: ResourceBundleRef, env: NodeJS.ProcessEnv): GitMirrorConfig | undefined {
|
||||||
void resourceBundleRef;
|
void resourceBundleRef;
|
||||||
return defaultGitMirrorConfig(env);
|
return defaultGitMirrorConfig(env);
|
||||||
|
|||||||
Reference in New Issue
Block a user