Files
tutti/tools/scripts/check-codexproto-generated.mjs
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

216 lines
6.1 KiB
JavaScript

import { spawnSync } from "node:child_process";
import {
mkdtempSync,
readdirSync,
readFileSync,
realpathSync,
rmSync,
statSync
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { createHash } from "node:crypto";
import { createIsolatedGitEnvironment } from "./git-environment.mjs";
const codexRepoUrl = "https://github.com/openai/codex.git";
const codexSourceCommit = "6d2168f06ae275d5e1f73cabf935d2bcc8549998";
const schemaSubdir = "codex-rs/app-server-protocol/schema/json";
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
const workspaceRoot = join(scriptDirectory, "..", "..");
const daemonRoot = join(workspaceRoot, "packages", "agent", "daemon");
const codexprotoRoot = join(daemonRoot, "runtime", "codexproto");
const vendoredSchemaRoot = join(codexprotoRoot, "schema", "json");
const workspaceEnv = createIsolatedGitEnvironment(workspaceRoot);
// The upstream re-fetch against the pinned Codex commit is CI's job
// (ADR 0002): local check:full runs must not block on cloning
// github.com/openai/codex (and must work offline). Pass --upstream to
// opt in locally.
if (process.env.CI || process.argv.includes("--upstream")) {
compareVendoredSchemaAgainstUpstream();
} else {
console.log(
"skipping upstream codex schema comparison (CI-only; pass --upstream to run locally)"
);
}
run(
"go",
["run", "./runtime/codexproto/internal/codegen"],
daemonRoot,
workspaceEnv
);
run(
"git",
["diff", "--exit-code", "--", "packages/agent/daemon/runtime/codexproto"],
workspaceRoot,
workspaceEnv
);
assertNoUntrackedGeneratedFiles();
console.log("codexproto generated artifacts are current");
function compareVendoredSchemaAgainstUpstream() {
const tempRoot = mkdtempSync(join(tmpdir(), "tutti-codexproto-"));
try {
const upstreamRoot = join(tempRoot, "codex");
const gitEnv = createIsolatedGitEnvironment(upstreamRoot);
run("git", ["init", upstreamRoot], workspaceRoot, gitEnv);
const gitDir = output(
"git",
["-C", upstreamRoot, "rev-parse", "--absolute-git-dir"],
workspaceRoot,
gitEnv
);
if (realpathSync(gitDir) !== realpathSync(join(upstreamRoot, ".git"))) {
throw new Error(`upstream Git fixture escaped ${upstreamRoot}`);
}
run(
"git",
["-C", upstreamRoot, "remote", "add", "origin", codexRepoUrl],
workspaceRoot,
gitEnv
);
run(
"git",
["-C", upstreamRoot, "fetch", "--depth=1", "origin", codexSourceCommit],
workspaceRoot,
gitEnv
);
run(
"git",
["-C", upstreamRoot, "checkout", "FETCH_HEAD", "--", schemaSubdir],
workspaceRoot,
gitEnv
);
compareDirectories(join(upstreamRoot, schemaSubdir), vendoredSchemaRoot);
} finally {
rmSync(tempRoot, { recursive: true, force: true });
}
}
function compareDirectories(expectedRoot, actualRoot) {
const expected = fileHashes(expectedRoot);
const actual = fileHashes(actualRoot);
const paths = new Set([...expected.keys(), ...actual.keys()]);
const mismatches = [];
for (const path of [...paths].sort()) {
if (!expected.has(path)) {
mismatches.push(`unexpected vendored schema: ${path}`);
continue;
}
if (!actual.has(path)) {
mismatches.push(`missing vendored schema: ${path}`);
continue;
}
if (expected.get(path) !== actual.get(path)) {
mismatches.push(`vendored schema differs: ${path}`);
}
}
if (mismatches.length > 0) {
throw new Error(
[
"codexproto schema drift detected against pinned Codex commit",
...mismatches.map((line) => `- ${line}`)
].join("\n")
);
}
}
// `git diff --exit-code` ignores untracked files, so codegen creating a
// brand-new artifact (e.g. a fresh *_gen.go) would otherwise pass the check
// while the file is uncommitted.
function assertNoUntrackedGeneratedFiles() {
const result = spawnSync(
"git",
["status", "--porcelain", "--", "packages/agent/daemon/runtime/codexproto"],
{ cwd: workspaceRoot, encoding: "utf8", env: workspaceEnv }
);
if (result.status !== 0) {
throw new Error("git status for codexproto failed");
}
const untracked = result.stdout
.split("\n")
.filter((line) => line.startsWith("??"));
if (untracked.length > 0) {
throw new Error(
[
"codexproto codegen produced untracked files (invisible to git diff); commit them:",
...untracked.map((line) => `- ${line.slice(3)}`)
].join("\n")
);
}
}
function fileHashes(root) {
const out = new Map();
for (const path of walkFiles(root)) {
const rel = relative(root, path);
const content = rel.endsWith(".json")
? canonicalJson(readFileSync(path, "utf8"))
: readFileSync(path);
const hash = createHash("sha256").update(content).digest("hex");
out.set(rel, hash);
}
return out;
}
function canonicalJson(source) {
return JSON.stringify(sortJsonValue(JSON.parse(source)));
}
function sortJsonValue(value) {
if (Array.isArray(value)) {
return value.map(sortJsonValue);
}
if (value && typeof value === "object") {
return Object.fromEntries(
Object.keys(value)
.sort()
.map((key) => [key, sortJsonValue(value[key])])
);
}
return value;
}
function walkFiles(root) {
const out = [];
for (const entry of readdirSync(root)) {
const path = join(root, entry);
const stat = statSync(path);
if (stat.isDirectory()) {
out.push(...walkFiles(path));
} else if (stat.isFile()) {
out.push(path);
}
}
return out;
}
function run(command, args, cwd, env = process.env) {
const result = spawnSync(command, args, {
cwd,
env,
stdio: "inherit"
});
if (result.status !== 0) {
throw new Error(`${command} ${args.join(" ")} failed`);
}
}
function output(command, args, cwd, env = process.env) {
const result = spawnSync(command, args, { cwd, encoding: "utf8", env });
if (result.status !== 0) {
throw new Error(
result.stderr.trim() || `${command} ${args.join(" ")} failed`
);
}
return result.stdout.trim();
}