mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 06:04:11 +01:00
Fix forced CI execution and align OpenCode shell fixture (#18230)
Use one explicit Turbo cache mode for forced CI execution and preserve cache trust boundaries. Match OpenCode’s ignored stdin in the relocated Linux shell-hook fixture while retaining strict OOM and parent-isolation assertions. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
16816d45e6
commit
99ec767a42
4 files changed
+59
-6
No files matched your search
+2
-1
@@ -184,7 +184,8 @@ changed cache definitions.
|
||||
The repository variable `CI_INCREMENTAL_MODE` accepts `enabled` (the default),
|
||||
`shadow` (report selections but execute everything), or `full`. Add the `full-ci`
|
||||
label to force all checks on one PR. Authorized `profile-ci` runs also execute
|
||||
everything. Forced and shadow runs bypass task-result caches. Every routing run
|
||||
everything. Forced and shadow runs disable local and remote task-result cache
|
||||
reads and writes. Every routing run
|
||||
with a valid plan publishes affected and scheduled job counts with input reasons
|
||||
in its job summary. Required aggregates reject failed, cancelled, and unexpectedly
|
||||
skipped selected suites; the verify aggregate includes serialized tests and the
|
||||
|
||||
@@ -58,14 +58,14 @@ describe("prepareOpenCodeRuntimeConfig", () => {
|
||||
expect(hookEnv.parent).toBeNull();
|
||||
expect(hookEnv.child.BASH_ENV).toContain(relocated);
|
||||
const command = spawnSync("bash", ["-c", 'printf "%s" "$1"; exit 7', "bash", "literal ' $() value"], {
|
||||
env: { ...process.env, ...hookEnv.child }, encoding: "utf8",
|
||||
env: { ...process.env, ...hookEnv.child }, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
expect(command.stdout).toBe("literal ' $() value");
|
||||
expect(command.status).toBe(7);
|
||||
if (process.platform === "linux") {
|
||||
const parentScore = await fs.readFile("/proc/self/oom_score_adj", "utf8");
|
||||
expect(execFileSync("bash", ["-c", "cat /proc/self/oom_score_adj"], {
|
||||
env: { ...process.env, ...hookEnv.child }, encoding: "utf8",
|
||||
env: { ...process.env, ...hookEnv.child }, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],
|
||||
}).trim()).toBe("1000");
|
||||
expect(await fs.readFile("/proc/self/oom_score_adj", "utf8")).toBe(parentScore);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, copyFileSync, existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
@@ -73,3 +73,55 @@ test("real Turbo executes existing suite scripts and invalidates source, argumen
|
||||
assert.equal(calls(), 7, "a failed task must execute on every attempt");
|
||||
} finally { rmSync(directory, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test("the task launcher bypasses cache reads and writes when execution is forced", () => {
|
||||
const directory = mkdtempSync(path.join(tmpdir(), "ci-turbo-forced-"));
|
||||
const write = (name, value) => writeFileSync(path.join(directory, name), value);
|
||||
const json = (name, value) => write(name, JSON.stringify(value));
|
||||
try {
|
||||
execFileSync("git", ["init", "-q"], { cwd: directory });
|
||||
mkdirSync(path.join(directory, "scripts/ci"), { recursive: true });
|
||||
mkdirSync(path.join(directory, "bin"));
|
||||
mkdirSync(path.join(directory, "packages/fixture"), { recursive: true });
|
||||
for (const name of ["run-task.mjs", "execution-plan.mjs"]) {
|
||||
copyFileSync(new URL(`scripts/ci/${name}`, repositoryRoot), path.join(directory, "scripts/ci", name));
|
||||
}
|
||||
write(".gitignore", ".turbo/\ncalls.txt\nresult.txt\nbin/\n");
|
||||
json("packages/fixture/package.json", { name: "fixture", private: true });
|
||||
json("package.json", { name: "forced-cache-proof", private: true, packageManager: "npm@10.0.0",
|
||||
workspaces: ["packages/*"], scripts: { "test:run:general": "node check.cjs" } });
|
||||
json("package-lock.json", { name: "forced-cache-proof", lockfileVersion: 3, packages: { "": { name: "forced-cache-proof" } } });
|
||||
json("turbo.json", { globalDependencies: [], globalEnv: ["CI_TASK_CACHE_SCOPE"], tasks: {
|
||||
"//#ci:server": { inputs: ["input.txt", "check.cjs"], outputs: ["result.txt"], cache: true },
|
||||
} });
|
||||
write("input.txt", "result");
|
||||
write("check.cjs", "const fs=require('node:fs');fs.appendFileSync('calls.txt','call\\n');fs.writeFileSync('result.txt',fs.readFileSync('input.txt'));");
|
||||
write("bin/pnpm", `#!${process.execPath}\nconst {spawnSync}=require('node:child_process');const args=process.argv.slice(2);if(args[0]==='--version'){console.log('9.15.4');}else{if(args[0]!=='exec'||args[1]!=='turbo')process.exit(2);const result=spawnSync(${JSON.stringify(turbo)},args.slice(2),{stdio:'inherit'});process.exit(result.status??1);}`);
|
||||
chmodSync(path.join(directory, "bin/pnpm"), 0o755);
|
||||
const run = force => {
|
||||
const result = spawnSync(process.execPath, [path.join(directory, "scripts/ci/run-task.mjs")], {
|
||||
cwd: directory, encoding: "utf8", env: { ...process.env, PATH: `${path.join(directory, "bin")}${path.delimiter}${process.env.PATH}`,
|
||||
CI_TASK: "ci:server", CI_TASK_ARGS: "[]", CI_TASK_DRY_RUN: "false", CI_FORCE_TASKS: String(force),
|
||||
TURBO_TOKEN: "", TURBO_TEAM: "", TURBO_TELEMETRY_DISABLED: "1" },
|
||||
});
|
||||
assert.equal(result.status, 0, result.stdout + result.stderr);
|
||||
};
|
||||
const calls = () => readFileSync(path.join(directory, "calls.txt"), "utf8").trim().split("\n").length;
|
||||
const cacheFiles = () => existsSync(path.join(directory, ".turbo/cache"))
|
||||
? readdirSync(path.join(directory, ".turbo/cache"), { recursive: true }).sort() : [];
|
||||
run(true); run(true);
|
||||
assert.equal(calls(), 2, "forced runs must execute even when a previous result exists");
|
||||
assert.deepEqual(cacheFiles(), [], "forced runs must not populate the task cache");
|
||||
run(false); run(false);
|
||||
assert.equal(calls(), 3, "ordinary runs still reuse local results");
|
||||
const retainedCache = cacheFiles();
|
||||
assert.ok(retainedCache.length > 0);
|
||||
run(true);
|
||||
assert.equal(calls(), 4, "forced runs must bypass an existing cache hit");
|
||||
assert.deepEqual(cacheFiles(), retainedCache);
|
||||
symlinkSync("input.txt", path.join(directory, "unclassified-link"));
|
||||
run(false);
|
||||
assert.equal(calls(), 5, "unclassified non-regular input must force execution");
|
||||
assert.deepEqual(cacheFiles(), retainedCache);
|
||||
} finally { rmSync(directory, { recursive: true, force: true }); }
|
||||
});
|
||||
@@ -41,9 +41,9 @@ const scope = {
|
||||
};
|
||||
const force = process.env.CI_FORCE_TASKS === "true" || uncacheable;
|
||||
const result = spawnSync("pnpm", ["exec", "turbo", "run", execution.target,
|
||||
"--root-turbo-json", runtimeConfig, "--summarize", "--cache=local:rw", "--cache-dir=.turbo/cache",
|
||||
"--root-turbo-json", runtimeConfig, "--summarize", force ? "--cache=" : "--cache=local:rw", "--cache-dir=.turbo/cache",
|
||||
...(process.env.CI_TASK_DRY_RUN === "true" ? ["--dry=json"] : []),
|
||||
...(force ? ["--force"] : []), "--", ...execution.args], {
|
||||
"--", ...execution.args], {
|
||||
stdio: "inherit",
|
||||
env: { ...process.env, TURBO_TELEMETRY_DISABLED: "1",
|
||||
CI_TASK_CACHE_SCOPE: createHash("sha256").update(JSON.stringify(scope)).digest("hex") },
|
||||
|
||||
Reference in new issue
Block a user