mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:10:43 +00:00
test(codex): cover app-server Docker flows
This commit is contained in:
109
scripts/prepare-codex-ci-auth.ts
Normal file
109
scripts/prepare-codex-ci-auth.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env -S node --import tsx
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
type CodexAuthJson = {
|
||||
tokens?: {
|
||||
account_id?: unknown;
|
||||
id_token?: unknown;
|
||||
};
|
||||
};
|
||||
|
||||
type JwtParts = {
|
||||
header: string;
|
||||
payload: Record<string, unknown>;
|
||||
signature: string;
|
||||
};
|
||||
|
||||
function decodeBase64UrlJson(value: string): Record<string, unknown> {
|
||||
const decoded = Buffer.from(value, "base64url").toString("utf-8");
|
||||
const parsed: unknown = JSON.parse(decoded);
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new Error("JWT payload is not a JSON object.");
|
||||
}
|
||||
return parsed as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function encodeBase64UrlJson(value: Record<string, unknown>): string {
|
||||
return Buffer.from(JSON.stringify(value), "utf-8").toString("base64url");
|
||||
}
|
||||
|
||||
function parseJwt(value: string): JwtParts {
|
||||
const parts = value.split(".");
|
||||
if (parts.length !== 3 || !parts[0] || !parts[1]) {
|
||||
throw new Error("id_token is not a JWT.");
|
||||
}
|
||||
return {
|
||||
header: parts[0],
|
||||
payload: decodeBase64UrlJson(parts[1]),
|
||||
signature: parts[2] ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
function stringifyJwt(parts: JwtParts): string {
|
||||
return [parts.header, encodeBase64UrlJson(parts.payload), parts.signature].join(".");
|
||||
}
|
||||
|
||||
export function patchCodexAuthForCi(auth: CodexAuthJson): {
|
||||
auth: CodexAuthJson;
|
||||
changed: boolean;
|
||||
} {
|
||||
const tokens = auth.tokens;
|
||||
if (!tokens) {
|
||||
return { auth, changed: false };
|
||||
}
|
||||
const accountId = typeof tokens.account_id === "string" ? tokens.account_id.trim() : "";
|
||||
const idToken = typeof tokens.id_token === "string" ? tokens.id_token.trim() : "";
|
||||
if (!accountId || !idToken) {
|
||||
return { auth, changed: false };
|
||||
}
|
||||
|
||||
const jwt = parseJwt(idToken);
|
||||
if (typeof jwt.payload.chatgpt_account_id === "string" && jwt.payload.chatgpt_account_id) {
|
||||
return { auth, changed: false };
|
||||
}
|
||||
|
||||
return {
|
||||
auth: {
|
||||
...auth,
|
||||
tokens: {
|
||||
...tokens,
|
||||
// Newer Codex app-server builds read ChatGPT account metadata from
|
||||
// id_token claims. Older local auth files can have the same value only
|
||||
// at tokens.account_id, so patch the staged Docker copy for CI.
|
||||
id_token: stringifyJwt({
|
||||
...jwt,
|
||||
payload: {
|
||||
...jwt.payload,
|
||||
chatgpt_account_id: accountId,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
changed: true,
|
||||
};
|
||||
}
|
||||
|
||||
export async function prepareCodexCiAuth(authPath: string): Promise<boolean> {
|
||||
const raw = await fs.readFile(authPath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as CodexAuthJson;
|
||||
const { auth, changed } = patchCodexAuthForCi(parsed);
|
||||
if (!changed) {
|
||||
return false;
|
||||
}
|
||||
const stat = await fs.stat(authPath);
|
||||
await fs.writeFile(authPath, `${JSON.stringify(auth, null, 2)}\n`, "utf-8");
|
||||
await fs.chmod(authPath, stat.mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.basename(process.argv[1] ?? "") === "prepare-codex-ci-auth.ts") {
|
||||
const authPath = process.argv[2];
|
||||
if (!authPath) {
|
||||
throw new Error("Usage: node --import tsx scripts/prepare-codex-ci-auth.ts <auth-json-path>");
|
||||
}
|
||||
const changed = await prepareCodexCiAuth(authPath);
|
||||
if (changed) {
|
||||
console.error("Prepared staged Codex auth metadata for CI.");
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ const exclusiveLanes = [
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:openai-web-search-minimal",
|
||||
],
|
||||
["live-codex-harness", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-codex-harness"],
|
||||
["live-codex-bind", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-codex-bind"],
|
||||
[
|
||||
"live-cli-backend-codex",
|
||||
"OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-cli-backend:codex",
|
||||
|
||||
@@ -157,6 +157,9 @@ if [ "${OPENCLAW_LIVE_CODEX_HARNESS_AUTH:-codex-auth}" != "api-key" ] && [ ! -s
|
||||
echo "ERROR: missing ~/.codex/auth.json for Codex harness live test." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "${OPENCLAW_LIVE_CODEX_HARNESS_AUTH:-codex-auth}" != "api-key" ]; then
|
||||
node --import tsx /src/scripts/prepare-codex-ci-auth.ts "$HOME/.codex/auth.json"
|
||||
fi
|
||||
if [ ! -x "$NPM_CONFIG_PREFIX/bin/codex" ]; then
|
||||
npm install -g @openai/codex
|
||||
fi
|
||||
@@ -181,7 +184,7 @@ cd "$tmp_dir"
|
||||
if [ "${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}" = "1" ]; then
|
||||
node --import tsx /src/scripts/prepare-codex-ci-config.ts "$HOME/.codex/config.toml" "$tmp_dir"
|
||||
fi
|
||||
pnpm test:live src/gateway/gateway-codex-harness.live.test.ts
|
||||
pnpm test:live ${OPENCLAW_LIVE_CODEX_TEST_FILES:-src/gateway/gateway-codex-harness.live.test.ts}
|
||||
EOF
|
||||
|
||||
openclaw_live_codex_harness_append_build_extension codex
|
||||
@@ -194,6 +197,7 @@ echo "==> MCP probe: ${OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE:-1}"
|
||||
echo "==> Guardian probe: ${OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE:-1}"
|
||||
echo "==> Auth mode: $CODEX_HARNESS_AUTH_MODE"
|
||||
echo "==> CI-safe Codex config: ${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}"
|
||||
echo "==> Test files: ${OPENCLAW_LIVE_CODEX_TEST_FILES:-src/gateway/gateway-codex-harness.live.test.ts}"
|
||||
echo "==> Harness fallback: none"
|
||||
echo "==> Auth files: ${AUTH_FILES_CSV:-none}"
|
||||
DOCKER_RUN_ARGS=(docker run --rm -t \
|
||||
@@ -213,8 +217,12 @@ DOCKER_RUN_ARGS=(docker run --rm -t \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE:-1}" \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE:-1}" \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_MODEL="${OPENCLAW_LIVE_CODEX_HARNESS_MODEL:-codex/gpt-5.4}" \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_REQUIRE_GUARDIAN_EVENTS="${OPENCLAW_LIVE_CODEX_HARNESS_REQUIRE_GUARDIAN_EVENTS:-1}" \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_REQUEST_TIMEOUT_MS="${OPENCLAW_LIVE_CODEX_HARNESS_REQUEST_TIMEOUT_MS:-}" \
|
||||
-e OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG="${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}" \
|
||||
-e OPENCLAW_LIVE_CODEX_BIND="${OPENCLAW_LIVE_CODEX_BIND:-}" \
|
||||
-e OPENCLAW_LIVE_CODEX_BIND_MODEL="${OPENCLAW_LIVE_CODEX_BIND_MODEL:-}" \
|
||||
-e OPENCLAW_LIVE_CODEX_TEST_FILES="${OPENCLAW_LIVE_CODEX_TEST_FILES:-}" \
|
||||
-e OPENCLAW_LIVE_TEST=1 \
|
||||
-e OPENCLAW_VITEST_FS_MODULE_CACHE=0)
|
||||
openclaw_live_append_array DOCKER_RUN_ARGS DOCKER_AUTH_ENV
|
||||
|
||||
Reference in New Issue
Block a user