codex exec 를 CI 에서 비대화형으로 실행하기
codex exec 의 기본 샌드박스와 승인, --json · -o · --output-schema 옵션, stdin 대기, CI 인증 방법, MCP 호출 취소 한계를 정리합니다.
codex exec 는 대화 화면 없이 작업 하나를 돌리고 끝납니다. CI 에 넣기 전에 기본값과 옵션을 설치된 버전에서 확인합니다. 샌드박스와 승인 값의 뜻은 Codex CLI 승인 정책과 샌드박스 설정 바로잡기에 있으니 여기서는 CI 에 필요한 것만 다룹니다.
[1] 버전과 옵션을 확인합니다.
$ codex --versioncodex-cli 0.163.0$ codex exec --help | grep -E '^\s+(-s|-o|--json|--output-schema|--ephemeral|--skip-git-repo-check|--ignore-user-config)' -s, --sandbox <SANDBOX_MODE> --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --ephemeral --ignore-user-config --output-schema <FILE> --json -o, --output-last-message <FILE>[2] 기본값은 읽기 전용 샌드박스에 승인 대기 없음입니다. 시작 때 찍히는 머리글에서 확인합니다(공식 문서에도 「By default, codex exec runs in a read-only sandbox.」).
approval: neversandbox: read-only파일을 고치는 작업이면 -s workspace-write 를 붙입니다. 이때 머리글은 sandbox: workspace-write [workdir, /tmp, $TMPDIR] 로 바뀝니다.
[3] Git 저장소가 아닌 폴더에서는 시작하지 않습니다. CI 체크아웃은 보통 저장소 안이라 문제없고, 아닐 때만 --skip-git-repo-check 를 붙입니다.
$ codex exec "hi"Not inside a trusted directory and --skip-git-repo-check was not specified.[4] stdin 을 닫아 줍니다. 프롬프트를 인자로 줘도 stdin 이 파이프이면 그 내용이 <stdin> 블록으로 덧붙고(도움말 설명), 터미널이 없는 셸에서 stdin 을 열어 둔 채 돌렸더니 입력을 기다리며 멈췄습니다. 입력이 없으면 </dev/null 을 붙입니다.
$ codex exec --skip-git-repo-check "hi" </dev/nullReading additional input from stdin...[5] 결과를 받는 방법은 셋입니다.
- 기본: 진행 상황은 stderr, 마지막 메시지만 stdout 입니다.
-o 파일(--output-last-message): 마지막 메시지를 파일에 씁니다.--json: stdout 이 JSON Lines 이벤트 스트림이 됩니다. 문서에 나온 이벤트 종류는thread.started·turn.started·turn.completed·turn.failed·item.*·error입니다.
$ codex exec --json --skip-git-repo-check "hi" </dev/null 2>/dev/null | head -2{"type":"thread.started","thread_id":"…"}{"type":"turn.started"}최종 답의 모양을 고정하려면 --output-schema 스키마.json 에 JSON Schema 파일을 줍니다. 파일이 없으면 시작 전에 멈춥니다.
Failed to read output schema file nope.json: No such file or directory (os error 2)[6] CI 에서 세션 파일을 남기지 않으려면 --ephemeral 을 붙입니다. 사용자 설정에 실행이 흔들리지 않게 하려면 --ignore-user-config 를 더하는데, 이 경우에도 인증은 CODEX_HOME 에서 읽습니다. 설정 철자 오류를 실패로 만들려면 --strict-config 입니다.
[7] 인증은 공식 문서 기준으로 둘 중 하나입니다. 로그인하지 않은 상태로 실행하면 401 Unauthorized: Missing bearer or basic authentication in header 로 재연결을 5번까지 시도하다 종료 코드 101 로 끝났습니다. codex login status 는 Not logged in 이고 종료 코드 1 입니다.
- API 키: 한 번 실행에만 인라인으로 줍니다.
CODEX_API_KEY=<키> codex exec --json "…". 문서는OPENAI_API_KEY·CODEX_API_KEY를 잡 전체 환경 변수로 두지 말라고 합니다. - ChatGPT 로그인 캐시(
~/.codex/auth.json): 비밀번호처럼 다루라고 되어 있고, 공개·오픈소스 저장소에는 권하지 않습니다.
GitHub Actions 에서는 직접 설치하지 말고 openai/codex-action 을 쓰라고 안내합니다. 문서 예시는 Codex 잡에 contents: read 만 주고 diff 를 패치 아티팩트로 올린 뒤, 별도 잡이 PR 을 엽니다. PR 을 여는 잡에는 API 키를 넘기지 않습니다.
[8] 이어서 하려면 codex exec resume --last "…" 또는 codex exec resume <SESSION_ID> 입니다.
[9] MCP 도구를 부르는 작업은 막힐 수 있습니다. openai/codex #24135(2026-05 등록, 확인 시점에도 열림)는 codex exec -s read-only 에서 MCP 도구 호출이 user cancelled MCP tool call. 로 취소된다고 보고합니다. 샌드박스를 유지한 채 허용하는 공식 방법은 이슈에 없고, --dangerously-bypass-approvals-and-sandbox 를 쓰면 호출은 되지만 샌드박스도 꺼집니다. 댓글 하나(CLI 0.154.0)는 도구별 approval_mode = "approve" 가 통했다고 적었는데, 이 글에서는 확인하지 않았습니다. 이 옵션은 외부에서 격리된 러너에서만 쓰세요.