在 CI 里跑 `codex exec`:非仓库、免配置与超时这三件事

2026-08-09

Codex(OpenAI Codex)的 codex exec 子命令,官方给的一句话说明是 “Run Codex non-interactively”,别名 e。听上去只是”把交互式换成一条命令”,但真往流水线里塞,卡住人的往往不是提示词,而是三件很土的事:CI 的工作目录不是 Git 仓库、runner 上没有你本机那份 config.toml、以及这条命令到底会跑多久、超时归谁管。

这三件事在官方选项表里都有对应,只是分散在不同位置。下面按”命令怎么写 → 产出物在哪 → 怎么验收 → 什么时候别用”的顺序过一遍。

一、先把命令写出来

先看骨架。Codex CLI 的用法是 codex [OPTIONS] <COMMAND> [ARGS]沙箱、审批、工作目录这些是顶层选项,要写在 exec 前面--skip-git-repo-check--json 这些是 exec 独有的,写在后面。照 usage 的位置摆,别把两类选项混着写。

Linux / macOS runner(POSIX shell):

printenv OPENAI_API_KEY | codex login --with-api-key

codex \
  -s read-only \
  -a never \
  -C "$PWD" \
  -c hide_agent_reasoning=true \
  exec \
  --skip-git-repo-check \
  --ignore-user-config \
  --ephemeral \
  --color never \
  --json \
  -o codex-last-message.txt \
  "把 docs/ 目录下的改动整理成一段不超过 20 行的发布说明草稿"

逐个说为什么在这:

  • printenv OPENAI_API_KEY | codex login --with-api-key 是官方给的 API key 登录写法,key 从 stdin 读,不会出现在命令行参数里(参数会被很多 CI 的日志和进程列表看到)。另一种认证是 ChatGPT 登录,走浏览器,在无浏览器的 runner 上不现实;官方对远程机器给的首选是 codex login --device-auth(设备码登录,官方标注 beta),需要人工输一次性验证码——这在无人值守的流水线里同样不成立。所以 CI 场景基本只剩 API key 这条路。密钥一律从 CI 的密钥管理里注入,别落到仓库。
  • -s read-only:沙箱模式,取值只有 read-only / workspace-write / danger-full-access 三个。CI 里第一版建议先 read-only 跑通链路,确认它不改文件;确实要它落盘改动再换 workspace-write
  • -a never:审批策略。官方释义是”从不询问,执行失败直接回传给模型”。注意 never 不等于放开权限——它只是不弹审批,该被沙箱挡住的照样挡。这个词容易理解反。非交互模式下没人能点确认,所以审批策略必须显式给。
  • -C "$PWD":显式指定 agent 的工作根目录,别依赖 runner 的隐式当前目录。
  • -c hide_agent_reasoning=true:这个配置键的官方说明就是”在 TUI 与 codex exec 输出里抑制推理内容”,CI 日志能干净不少。
  • --ephemeral:不把会话文件落盘。runner 大多是一次性的,落了也没人看;更实际的原因是磁盘——本机在 codex-cli 0.147.0(Windows 11)上跑 codex doctor --summary,Notes 区直接提示 rollouts 占了 3.07 GB。这个体积在长期复用的自托管 runner 上会变成运维问题。
  • --color never--color 取值 always / never / auto,默认 auto。CI 日志里的 ANSI 转义序列会把后续文本分析搞乱,显式关掉。

二、“非仓库”:--skip-git-repo-check

codex exec 有一个独有选项 --skip-git-repo-check,作用是允许在非 Git 仓库里运行。换句话说,默认情况下它会检查当前目录是不是 Git 仓库。

CI 里踩到这条的情况比想象中多:把产物解压到临时目录再让 Codex 读一遍、容器里只挂了 dist/、或者流水线做了浅克隆之后又把 .git 清掉减体积。这些目录都不是仓库,不加这个开关就会被拦下。

反过来,如果你的作业本来就在完整仓库里跑,不建议无脑加。这个检查某种意义上是道保险:它能在”工作目录没切对”的时候提前失败,而不是让 Codex 在一个空目录里认真地思考一遍。

三、“免配置”:让 runner 上的结果可复现

本机跑得好好的,CI 上行为不一样,八成是配置差异。相关的有三个开关,作用范围各不相同:

选项作用注意
--ignore-user-config不加载 $CODEX_HOME/config.tomlauth 仍然使用 CODEX_HOME,登录凭据不受影响
--ignore-rules不加载用户或项目的 execpolicy .rules 文件与配置文件是两套东西
--strict-configconfig.toml 里出现本版本不认识的字段时直接报错退出顶层选项,写在 exec

这里有个容易忽略的点:--ignore-user-config 只关配置,不关认证——官方说明明确写了 auth 仍然走 CODEX_HOME。所以你不会因为加了它就掉登录,但也别指望它能隔离掉凭据。

于是 CI 里有两条路线,选哪条取决于你要不要用仓库里那份配置:

路线 A:完全不吃本机配置。--ignore-user-config,然后把需要的键用 -c 一条条显式给出来。好处是 runner 换了、镜像重建了、别人的机器上跑,行为都一样。代价是每个键都得自己写。

路线 B:吃配置,但用 --strict-config 兜拼写错误。 适合团队把 config.toml 纳入版本管理的情形。

-c 的写法有个坑必须说清楚:-c key=value 里的 value 按 TOML 解析,解析失败则按字面字符串处理,点号路径表示嵌套。官方给的三个例子是 -c model="o3"-c 'sandbox_permissions=["disk-full-read-access"]'-c shell_environment_policy.inherit=all。留意第二个例子整段被单引号包住——数组、表这类值里带引号和方括号,shell 会先吃一道,不整体加引号必然传歪。而”解析失败按字面字符串”这个设计意味着传错了不一定报错,可能悄悄变成一个字符串值,这比报错更难查。

--strict-config 也有边界。本机在 codex-cli 0.147.0(Windows 11)上执行 codex -c model_reasoning_effortt=high --strict-config exec --help(键名故意多打一个 t),结果正常打印了 help,没有报未知字段错误。说明它的校验发生在真正加载配置去跑会话时,--help 这类不进入会话的路径不触发。所以别把”CI 里加了 --strict-config 就能拦住所有拼写错误”当成结论。

Windows runner 上还有两处差异要留意:配置键 features.unified_exec 官方标注默认 trueWindows 除外,本机 codex features list 在 0.147.0 上实测该项生效值为 false,两边对得上;另外 windows.sandbox 这个键的取值是 unelevatedelevated,Windows 的原生沙箱机制本来就和 Linux 不是一套东西。跨平台矩阵跑同一条命令时,别默认两边行为一致。

四、“超时”:谁来管这条命令跑多久

先说结论:本机在 codex-cli 0.147.0(Windows 11)上看 codex exec --help,独有选项里没有一个控制整体运行时长的开关。也就是说,“这条命令最多跑 15 分钟”这件事得由 CI 平台的作业超时、或者外部的 timeout 之类工具来兜,不是 Codex 的功能。

Codex 侧能配的是几个局部超时,都在配置键里,且各管各的:

配置键默认值管什么
mcp_servers.<id>.startup_timeout_sec10MCP server 启动超时
mcp_servers.<id>.tool_timeout_sec60MCP 工具调用超时
background_terminal_max_timeout300000后台终端(毫秒,即 5 分钟)
model_providers.<id>.stream_idle_timeout_ms300000自定义提供方的流空闲超时
model_providers.<id>.request_max_retries4自定义提供方的请求重试次数

MCP 那两个值得单独盯一下。启动超时的默认值只有 10 秒——这是官方文档给的默认值,我们没有在任何 CI/runner 上实际跑过 MCP server,下面这段是按默认值做的推断而不是实测:冷缓存的 runner 上装依赖、拉镜像、启进程,10 秒很容易不够。更麻烦的是 mcp_servers.<id>.required启用的服务器初始化失败会导致启动失败,于是表现成”整条流水线在还没开始干活的时候就红了”。要么把 startup_timeout_sec 调大,要么在 CI 里干脆用 --ignore-user-config 把这些 server 一起排除掉——后者更干脆,前提是你的任务确实不依赖 MCP。

(上表把多个键放在一起是为了对照,实际写进 config.toml 时按官方文档的表结构分节写;以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。)

还有一个和”跑多久”无关但会咬人的事实:check_for_update_on_startup 默认 true,Codex 具备自更新能力。本机采集当天,开头执行 codex --version 得到 codex-cli 0.131.0,十几分钟后同一台机器同一条命令得到 0.147.0which -a codex 全程只有一个可执行文件。放到 CI 语境里就是:你的流水线用的版本可能和上周不是同一个。排查任何版本相关问题,都要以当次 codex --version 的实时输出为准,别用记忆里的版本号;需要可复现的场合,把版本号打进日志的第一行。

五、产出物长什么样

codex exec 有两个和产出直接相关的选项,加上一个描述结构的:

  • --json:事件以 JSONL 打到 stdout,一行一个事件。CI 里可以边跑边收,也可以重定向到文件留档。具体有哪些事件类型、字段叫什么,得看你当次的实际输出——本文不列字段名,因为我们没有可引用的依据,照抄别人博客里的字段名是给自己挖坑。
  • -o, --output-last-message <FILE>:把 agent 的最后一条消息写到文件。这是最实用的一个:流水线的下一步(贴评论、发通知、存产物)只想要结论,不想解析事件流,那就读这个文件。
  • --output-schema <FILE>:接一个 JSON Schema 文件的路径,用来描述模型最终回复的结构。想让下游程序化消费结果,比事后用正则抠文本靠谱。

另外提示词的传入方式也值得单说:位置参数 [PROMPT] 可以直接写在命令行;不给参数或给 - 时从 stdin 读;如果 stdin 是管道且同时给了 prompt,stdin 会作为 <stdin> 块追加。这条在 CI 里很好用——固定的指令写死在 pipeline 里当 prompt,变动的内容(diff、日志、构建报错)从管道灌进去,两者会合并而不是互相覆盖。

六、怎么验收

在跑真任务之前,先加一段预检。这几处按顺序查,出问题的位置基本跑不出这个范围:

codex --version
codex login status
codex doctor --summary
  • codex --version:把版本打进日志第一行,上面说过原因。
  • codex login status:确认认证真的生效了。本机在 codex-cli 0.147.0(Windows 11)上执行这条命令,输出是一行 Logged in using ChatGPT;CI 里用 API key 登录的话输出会不同,重点是它得给出一个明确的已登录状态,而不是报错。登录失败的诊断信息会写在配置的日志目录里的 codex-login.log。如果公司网络有 TLS 代理或私有根 CA,官方给的做法是登录前设置环境变量 CODEX_CA_CERTIFICATE
  • codex doctor --summary:重点看 Configuration 分组里的 config 那一行。本机在 0.147.0 上故意传了一段语法不合法的 TOML(codex -c 'features=[unclosed' doctor --summary),命令没有崩溃退出,doctor 照常跑完,但输出里出现了这么一行:
✗ config       config could not be loaded - Fix the reported config error, then rerun codex doctor.

这是”改完配置没生效”最快的判定手段。它同时也说明一件事:配置坏掉不一定让命令失败,CI 里如果只看退出码,可能一路带着没加载成功的配置跑到底。doctor 的结尾统计行是 N ok · N idle · N notes · N warn · N fail 的格式,状态符号有 (ok)、(idle)、(notes/warn)、(fail)四种,把这一行捞出来做断言比人眼看日志靠谱。

codex doctor --json 的官方说明是 “Emit a redacted machine-readable report”——是脱敏的。这一点有直接的实操意义:CI 上出了问题要贴诊断结果给别人看,用这个输出,比截一屏日志安全。同理,codex mcp list 本机实测 Env 列里的环境变量值会被打成 *****,只显示键名,也可以放心贴。

另外有三个容易出错的位置,不分先后,都值得在预检里顺手扫一眼:① 顶层选项和 exec 独有选项的位置摆反了;② -c 的值没整体加引号,被 shell 吃掉引号后按字面字符串处理,静默走偏;③ MCP server 的启动超时用的还是 10 秒默认值,任务还没开始就失败。

七、什么情况不适用

  • 需要人判断的活儿别放进来。 非交互模式下没有审批交互,-a never 的语义是”从不询问,执行失败直接回传给模型”。凡是你希望有人看一眼再放行的操作,本来就不该走这条路。
  • --dangerously-bypass-approvals-and-sandbox 别当省事开关。 官方原文是 “EXTREMELY DANGEROUS. Intended solely for running in environments that are externally sandboxed”,--dangerously-bypass-hook-trust 也写着 “DANGEROUS. Intended only for automation that already vets hook sources”。它们的适用前提是外部已经有沙箱hook 来源已经过审,不是”CI 里反正是容器所以随便开”。你的 runner 有没有到那个程度,得自己评估。
  • 需要联网的任务要单独确认。 配置键 web_search 默认值是 cached,既不是关闭也不是实时;CLI 的 --search 对应开实时搜索,且官方说明写明启用后无逐次调用审批。另外 sandbox_workspace_write.network_access 控制 workspace-write 沙箱内是否允许出网——CI 里要装依赖的场景,这两处得对上。
  • 别指望它对成本和耗时给承诺。 本文的所有实测结论都来自只读命令,我们没有发起过任何模型对话请求,因此没有任何耗时、token 消耗或模型表现数据可引用。要评估流水线成本,只能自己在小范围跑一段时间实测。
  • exec-servercloud 这些别混进来。 在 codex-cli 0.147.0 上,codex exec-server(Run the standalone exec-server service)和 codex cloud 的子命令说明里都带着 [EXPERIMENTAL] 标签。生产流水线里用带这个标签的东西,得做好它下个版本就变的准备。

最后一句实在话:把 codex exec 塞进 CI,收益最大的通常不是”让它自动改代码提 PR”,而是那些读一堆东西、产出一段结论的活儿——整理发布说明、汇总构建失败原因、把长日志压成一段人话。这类任务用 -s read-only 就够,产出用 -o 落一个文件,失败了大不了这一步跳过,不会污染仓库。链路先从这里跑通,再谈要不要给它写权限。

相关阅读


本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Non-interactive mode》《Configuration Reference》《Authentication》《Codex CLI》《Sandbox》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。