mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 11:01:16 +01:00
fix: canonicalize owned Runner E2E temporary roots
This commit is contained in:
1 parent
f5024863e9
commit
df748c9977
4 files changed
+37
-8
No files matched your search
@@ -15,6 +15,17 @@ failures remain retained. The Mac file-edit correction also passes.
|
||||
|
||||
### Latest boundary fixes — 2026-10-05
|
||||
|
||||
The next explicit Mac attempt at `f5024863e` confirms that the description is
|
||||
no longer duplicated, but still times out with cleanup passing. Its runtime
|
||||
context advertises `/tmp/.../live`, while the native Pi grant binds the physical
|
||||
`/private/tmp/.../live` directory. The fixture now canonicalizes its owned temp
|
||||
root before configuring the instance and validates cleanup against that same
|
||||
physical parent. A symlink-ancestor regression fails before the change. The
|
||||
normal installed Pi policy rejects the old advertised alias, accepts the
|
||||
corrected physical root, and continues to deny an unrelated root, with zero
|
||||
model calls. The byte and timeout assertions remain unchanged. This correction
|
||||
only changes the harness; shipping runtime artifacts remain `f5024863e`.
|
||||
|
||||
The explicit version-12 Mac memory attempt at `867fa4711` still times out
|
||||
at the unchanged native-session limit; cleanup passes. Its valid fenced JSON
|
||||
arrives in a duplicated description: the Markdown paste capture inserts the
|
||||
|
||||
@@ -2,6 +2,7 @@ import { runnerE2EPlaywrightInvocation } from "./web-server-command.js";
|
||||
import { verifyInstalledDaytonaPlugin } from "./installed-daytona-plugin.js";
|
||||
import { assertInstalledCliSelection, assertInstalledStartupOnly, verifyInstalledCli } from "./installed-cli.js";
|
||||
import { createProcessTreeOwner, stopOwnedProcessTree } from "./process-tree-owner.js";
|
||||
import { createRunnerE2ETemporaryRoot } from "./server-config.js";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { prepareCodexCiSandbox, requiresCodexCiSandbox } from "./codex-ci-sandbox.js";
|
||||
import { spawn } from "node:child_process";
|
||||
@@ -15,8 +16,8 @@ import {
|
||||
cp,
|
||||
lstat,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
readFile,
|
||||
realpath,
|
||||
readdir,
|
||||
rm,
|
||||
symlink,
|
||||
@@ -479,9 +480,8 @@ async function runAttempt(input: {
|
||||
const installedPlugin = await verifyInstalledDaytonaPlugin(process.env, executions);
|
||||
const startedAtMs = Date.now();
|
||||
const sharedMemoryBaseline = snapshotDarwinSharedMemory();
|
||||
const temporaryRoot = await mkdtemp(
|
||||
path.join(os.tmpdir(), "paperclip-runner-e2e-"),
|
||||
);
|
||||
const temporaryParent = await realpath(os.tmpdir());
|
||||
const temporaryRoot = await createRunnerE2ETemporaryRoot(temporaryParent);
|
||||
const publishedResults: RunnerE2EResult[] = [];
|
||||
const publishedResultPaths = new Map<string, string>();
|
||||
let attemptSecrets: string[] = [];
|
||||
@@ -818,7 +818,7 @@ async function runAttempt(input: {
|
||||
if (processCleanupFailed) {
|
||||
cleanupError = new Error(`Preserving temporary state after incomplete process cleanup: ${temporaryRoot}`);
|
||||
} else if (
|
||||
temporaryRoot.startsWith(`${os.tmpdir()}${path.sep}paperclip-runner-e2e-`)
|
||||
temporaryRoot.startsWith(`${temporaryParent}${path.sep}paperclip-runner-e2e-`)
|
||||
) {
|
||||
for (let cleanupAttempt = 1; cleanupAttempt <= 3; cleanupAttempt += 1) {
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { mkdir, mkdtemp, realpath, readFile, rm, symlink } from "node:fs/promises";
|
||||
import { createServer } from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
reserveRunnerE2EDatabasePort,
|
||||
type LoopbackPortReservation,
|
||||
} from "./ports.js";
|
||||
import { prepareRunnerE2EServerConfig } from "./server-config.js";
|
||||
import { createRunnerE2ETemporaryRoot, prepareRunnerE2EServerConfig } from "./server-config.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
const reservations: LoopbackPortReservation[] = [];
|
||||
@@ -22,6 +22,18 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe("isolated paid E2E database ports", () => {
|
||||
it("uses a physical temporary root when the host temp directory has an alias", async () => {
|
||||
const parent = await realpath(await mkdtemp(path.join(os.tmpdir(), "e2e-temp-alias-")));
|
||||
roots.push(parent);
|
||||
const physical = path.join(parent, "physical");
|
||||
const alias = path.join(parent, "alias");
|
||||
await mkdir(physical);
|
||||
await symlink(physical, alias);
|
||||
const root = await createRunnerE2ETemporaryRoot(alias);
|
||||
expect(root).toBe(await realpath(root));
|
||||
expect(path.dirname(root)).toBe(physical);
|
||||
});
|
||||
|
||||
it("keeps parallel database reservations distinct and held until server spawn", async () => {
|
||||
reservations.push(
|
||||
...(await Promise.all(
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { mkdir, mkdtemp, realpath, readFile, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { paperclipConfigSchema } from "../../packages/shared/src/config-schema.js";
|
||||
import { reserveRunnerE2EDatabasePort } from "./ports.js";
|
||||
|
||||
/** Runtime instructions and native filesystem grants must use the same path. */
|
||||
export async function createRunnerE2ETemporaryRoot(parent = os.tmpdir()) {
|
||||
return await realpath(await mkdtemp(path.join(parent, "paperclip-runner-e2e-")));
|
||||
}
|
||||
|
||||
/** Seed only the disposable fixture. Onboarding preserves this validated config
|
||||
* and still creates the normal secrets and database. Restarts keep the same DB.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user