From 05bb5d7d46d843fcad2eeadebcbe860e0a629823 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 12 May 2025 21:27:15 -0700 Subject: [PATCH] fix: always load version from package.json at runtime (#909) Note the high-level motivation behind this change is to avoid the need to make temporary changes in the source tree in order to cut a release build since that runs the risk of leaving things in an inconsistent state in the event of a failure. The existing code: ``` import pkg from "../../package.json" assert { type: "json" }; ``` did not work as intended because, as written, ESBuild would bake the contents of the local `package.json` into the release build at build time whereas we want it to read the contents at runtime so we can use the `package.json` in the tree to build the code and later inject a modified version into the release package with a timestamped build version. Changes: * move `CLI_VERSION` out of `src/utils/session.ts` and into `src/version.ts` so `../package.json` is a correct relative path both from `src/version.ts` in the source tree and also in the final `dist/cli.js` build output * change `assert` to `with` in `import pkg` as apparently `with` became standard in Node 22 * mark `"../package.json"` as external in `build.mjs` so the version is not baked into the `.js` at build time After using `pnpm stage-release` to build a release version, if I use Node 22.0 to run Codex, I see the following printed to stderr at startup: ``` (node:71308) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created) ``` Note it is a warning and does not prevent Codex from running. In Node 22.12, the warning goes away, but the warning still appears in Node 22.11. For Node 22, 22.15.0 is the current LTS version, so LTS users will not see this. Also, something about moving the definition of `CLI_VERSION` caused a problem with the mocks in `check-updates.test.ts`. I asked Codex to fix it, and it came up with the change to the test configs. I don't know enough about vitest to understand what it did, but the tests seem healthy again, so I'm going with it. --- codex-cli/build.mjs | 3 +++ codex-cli/src/app.tsx | 3 ++- .../src/components/chat/terminal-chat-input.tsx | 2 +- codex-cli/src/components/chat/terminal-chat.tsx | 2 +- codex-cli/src/utils/agent/agent-loop.ts | 2 +- codex-cli/src/utils/check-updates.ts | 2 +- codex-cli/src/utils/session.ts | 6 ------ codex-cli/src/version.ts | 8 ++++++++ codex-cli/tests/check-updates.test.ts | 4 ++-- codex-cli/vite.config.ts | 4 ---- codex-cli/vitest.config.ts | 12 ++++++++++++ 11 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 codex-cli/src/version.ts delete mode 100644 codex-cli/vite.config.ts create mode 100644 codex-cli/vitest.config.ts diff --git a/codex-cli/build.mjs b/codex-cli/build.mjs index 465e8b92..16664d76 100644 --- a/codex-cli/build.mjs +++ b/codex-cli/build.mjs @@ -72,6 +72,9 @@ if (isDevBuild) { esbuild .build({ entryPoints: ["src/cli.tsx"], + // Do not bundle the contents of package.json at build time: always read it + // at runtime. + external: ["../package.json"], bundle: true, format: "esm", platform: "node", diff --git a/codex-cli/src/app.tsx b/codex-cli/src/app.tsx index 5d859db5..3f84935c 100644 --- a/codex-cli/src/app.tsx +++ b/codex-cli/src/app.tsx @@ -1,12 +1,13 @@ import type { ApprovalPolicy } from "./approvals"; import type { AppConfig } from "./utils/config"; +import type { TerminalChatSession } from "./utils/session.js"; import type { ResponseItem } from "openai/resources/responses/responses"; import TerminalChat from "./components/chat/terminal-chat"; import TerminalChatPastRollout from "./components/chat/terminal-chat-past-rollout"; import { checkInGit } from "./utils/check-in-git"; -import { CLI_VERSION, type TerminalChatSession } from "./utils/session.js"; import { onExit } from "./utils/terminal"; +import { CLI_VERSION } from "./version"; import { ConfirmInput } from "@inkjs/ui"; import { Box, Text, useApp, useStdin } from "ink"; import React, { useMemo, useState } from "react"; diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index 819b8ea3..e22ec82e 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -584,7 +584,7 @@ export default function TerminalChatInput({ try { const os = await import("node:os"); - const { CLI_VERSION } = await import("../../utils/session.js"); + const { CLI_VERSION } = await import("../../version.js"); const { buildBugReportUrl } = await import( "../../utils/bug-report.js" ); diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 998a190c..f34ab792 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -24,9 +24,9 @@ import { uniqueById, } from "../../utils/model-utils.js"; import { createOpenAIClient } from "../../utils/openai-client.js"; -import { CLI_VERSION } from "../../utils/session.js"; import { shortCwd } from "../../utils/short-path.js"; import { saveRollout } from "../../utils/storage/save-rollout.js"; +import { CLI_VERSION } from "../../version.js"; import ApprovalModeOverlay from "../approval-mode-overlay.js"; import DiffOverlay from "../diff-overlay.js"; import HelpOverlay from "../help-overlay.js"; diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 60749a23..97041def 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -11,6 +11,7 @@ import type { } from "openai/resources/responses/responses.mjs"; import type { Reasoning } from "openai/resources.mjs"; +import { CLI_VERSION } from "../../version.js"; import { OPENAI_TIMEOUT_MS, OPENAI_ORGANIZATION, @@ -24,7 +25,6 @@ import { parseToolCallArguments } from "../parsers.js"; import { responsesCreateViaChatCompletions } from "../responses.js"; import { ORIGIN, - CLI_VERSION, getSessionId, setCurrentModel, setSessionId, diff --git a/codex-cli/src/utils/check-updates.ts b/codex-cli/src/utils/check-updates.ts index 5e326c1c..6999c90c 100644 --- a/codex-cli/src/utils/check-updates.ts +++ b/codex-cli/src/utils/check-updates.ts @@ -1,7 +1,7 @@ import type { AgentName } from "package-manager-detector"; import { detectInstallerByPath } from "./package-manager-detector"; -import { CLI_VERSION } from "./session"; +import { CLI_VERSION } from "../version"; import boxen from "boxen"; import chalk from "chalk"; import { getLatestVersion } from "fast-npm-meta"; diff --git a/codex-cli/src/utils/session.ts b/codex-cli/src/utils/session.ts index 19867220..201929f6 100644 --- a/codex-cli/src/utils/session.ts +++ b/codex-cli/src/utils/session.ts @@ -1,9 +1,3 @@ -// Node ESM supports JSON imports behind an assertion. TypeScript's -// `resolveJsonModule` takes care of the typings. -import pkg from "../../package.json" assert { type: "json" }; - -// Read the version directly from package.json. -export const CLI_VERSION: string = (pkg as { version: string }).version; export const ORIGIN = "codex_cli_ts"; export type TerminalChatSession = { diff --git a/codex-cli/src/version.ts b/codex-cli/src/version.ts new file mode 100644 index 00000000..89f638df --- /dev/null +++ b/codex-cli/src/version.ts @@ -0,0 +1,8 @@ +// Note that "../package.json" is marked external in build.mjs. This ensures +// that the contents of package.json will always be read at runtime, which is +// preferable so we do not have to make a temporary change to package.json in +// the source tree to update the version number in the code. +import pkg from "../package.json" with { type: "json" }; + +// Read the version directly from package.json. +export const CLI_VERSION: string = (pkg as { version: string }).version; diff --git a/codex-cli/tests/check-updates.test.ts b/codex-cli/tests/check-updates.test.ts index 75ec8aaf..4f77fc51 100644 --- a/codex-cli/tests/check-updates.test.ts +++ b/codex-cli/tests/check-updates.test.ts @@ -9,7 +9,7 @@ import { renderUpdateCommand, } from "../src/utils/check-updates"; import { detectInstallerByPath } from "../src/utils/package-manager-detector"; -import { CLI_VERSION } from "../src/utils/session"; +import { CLI_VERSION } from "../src/version"; // In-memory FS mock let memfs: Record = {}; @@ -37,8 +37,8 @@ vi.mock("node:fs/promises", async (importOriginal) => { // Mock package name & CLI version const MOCK_PKG = "my-pkg"; +vi.mock("../src/version", () => ({ CLI_VERSION: "1.0.0" })); vi.mock("../package.json", () => ({ name: MOCK_PKG })); -vi.mock("../src/utils/session", () => ({ CLI_VERSION: "1.0.0" })); vi.mock("../src/utils/package-manager-detector", async (importOriginal) => { return { ...(await importOriginal()), diff --git a/codex-cli/vite.config.ts b/codex-cli/vite.config.ts deleted file mode 100644 index 669a10f2..00000000 --- a/codex-cli/vite.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { defineConfig } from 'vite'; - -// Provide a stub Vite config in the CLI package to avoid resolving a parent-level vite.config.js -export default defineConfig({}); \ No newline at end of file diff --git a/codex-cli/vitest.config.ts b/codex-cli/vitest.config.ts new file mode 100644 index 00000000..97af07fc --- /dev/null +++ b/codex-cli/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +/** + * Vitest configuration for the CLI package. + * Disables worker threads to avoid pool recursion issues in sandbox. + */ +export default defineConfig({ + test: { + threads: false, + environment: "node", + }, +});