繁體中文

在 CI/CD 中維護 Codex 賬號認證(高階)

使用 Codex 內建的重新整理流程,讓 auth.json 在受信任的 CI/CD runner 中持續可用

本指南說明如何在可信的 CI/CD 執行器上維持 ChatGPT 託管的 Codex 認證,而無須自行呼叫 OAuth token endpoint。

自動化任務應優先使用 API key 認證。只有當工作流程確實需要以你的 Codex 賬號身份執行時,才使用本指南中的方案。

整體流程如下:

  1. 在可信裝置上執行一次 codex login,生成 auth.json
  2. 將該檔案放到執行器上。
  3. 正常執行 Codex。
  4. 當會話過期時,由 Codex 自動重新整理。
  5. 儲存重新整理後的 auth.json,供下一次執行使用。

這是一種面向企業及其他可信私有自動化環境的高階工作流程。對於大多數 CI/CD 任務,API key 仍是推薦方案。

工作原理

Codex 本身已經能夠重新整理由 ChatGPT 託管的會話。

以當前的開源客戶端為準:

  • Codex 會從 auth.json 載入本機認證快取。
  • 如果 last_refresh 距今超過約 8 天,Codex 會先重新整理令牌集,再繼續執行。
  • 重新整理成功後,Codex 會把新令牌和新的 last_refresh 寫回 auth.json
  • 如果請求收到 401,Codex 也有內建的重新整理並重試流程。

因此,受支援的 CI/CD 策略不是“自行呼叫重新整理 API”,而是“執行 Codex,並持久化更新後的 auth.json”。

適用條件

只有同時滿足以下條件時,才使用本指南:

  • 你需要 ChatGPT 託管的 Codex 認證,而不是 API key。
  • 遠端執行器無法執行 codex login
  • 執行器屬於可信的私有基礎設施。
  • 你能夠在多次執行之間儲存重新整理後的 auth.json
  • 一份 auth.json 只由一臺機器或一組序列執行的任務使用。

本指南適用於 Codex 管理的 ChatGPT 認證(auth_mode: "chatgpt")。

它不適用於:

  • API key 認證。
  • 由宿主應用管理外部 token 的整合(auth_mode: "chatgptAuthTokens")。
  • Codex 之外的通用 OAuth 客戶端。

如果憑據儲存在作業系統鑰匙串中,請先改用檔案儲存。參見憑據儲存

僅初始化一次 auth.json

在能夠通過瀏覽器登入的可信裝置上執行以下步驟:

  1. 設定 Codex,將憑據儲存到檔案中:
cli_auth_credentials_store = "file"
  1. 執行:
codex login
  1. 驗證該檔案是否為 ChatGPT 託管認證:
AUTH_FILE="${CODEX_HOME:-$HOME/.codex}/auth.json"

jq '{
  auth_mode,
  has_tokens: (.tokens != null),
  has_refresh_token: ((.tokens.refresh_token // "") != ""),
  last_refresh
}' "$AUTH_FILE"

只有滿足以下條件時才繼續:

  • auth_mode"chatgpt"
  • has_refresh_tokentrue

然後,將 auth.json 的內容存入 CI/CD 金鑰管理系統,或把檔案複製到可信且可持久化的執行器。

推薦方案:使用自託管執行器的 GitHub Actions

最簡單的全自動方案,是使用帶持久化 CODEX_HOME 的自託管 GitHub Actions 執行器。

這一方案具有以下優勢:

  • 執行器可以在多次任務之間保留磁碟上的 auth.json
  • Codex 可以直接重新整理原檔案。
  • 後續任務會自動使用重新整理後的令牌。
  • 原始 secret 只在初始化或重新寫入憑據時需要。

關鍵在於:僅當 auth.json 不存在時才寫入初始檔案。如果每次執行都用原始 secret 覆蓋該檔案,就會丟掉 Codex 上一次寫入的重新整理結果。

下面是一份定時工作流程範例:

name: Keep Codex auth fresh

on:
  schedule:
    - cron: "0 9 * * 1"
  workflow_dispatch:

jobs:
  keep-codex-auth-fresh:
    runs-on: self-hosted
    steps:
      - name: Bootstrap auth.json if needed
        shell: bash
        env:
          CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
        run: |
          export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
          mkdir -p "$CODEX_HOME"
          chmod 700 "$CODEX_HOME"

          if [ ! -f "$CODEX_HOME/auth.json" ]; then
            printf '%s' "$CODEX_AUTH_JSON" > "$CODEX_HOME/auth.json"
            chmod 600 "$CODEX_HOME/auth.json"
          fi

      - name: Run Codex
        shell: bash
        run: |
          codex exec --json "Reply with the single word OK." >/dev/null

該工作流程會:

  • 在首次執行時寫入 auth.json
  • 在後續執行中複用同一檔案。
  • 當快取會話達到過期條件時,在正常的 codex exec 步驟中由 Codex 自動重新整理。
  • 將重新整理後的檔案保留在磁碟上,供下一次工作流程執行使用。

當前開源客戶端會在大約 8 天后把會話視為已過期,因此每週執行一次通常就足夠。

臨時執行器:恢復檔案、執行 Codex,再持久化更新後的檔案

如果使用 GitHub 託管執行器、GitLab 共享執行器或其他臨時環境,每個任務結束後,執行器的檔案系統都會被清除。此時需要完成一次完整的往返流程:

  1. 從安全儲存中恢復當前的 auth.json
  2. 執行 Codex。
  3. 將更新後的 auth.json 寫回安全儲存。

GitHub Actions 的通用結構如下:

name: Run Codex with managed auth

on:
  workflow_dispatch:

jobs:
  codex-job:
    runs-on: ubuntu-latest
    steps:
      - name: Restore auth.json
        shell: bash
        run: |
          export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
          mkdir -p "$CODEX_HOME"
          chmod 700 "$CODEX_HOME"

          # Replace this with your secret manager or secure storage command.
          my-secret-cli read codex-auth-json > "$CODEX_HOME/auth.json"
          chmod 600 "$CODEX_HOME/auth.json"

      - name: Run Codex
        shell: bash
        run: |
          codex exec --json "summarize the failing tests"

      - name: Persist refreshed auth.json
        if: always()
        shell: bash
        run: |
          # Replace this with your secret manager or secure storage command.
          my-secret-cli write codex-auth-json < "$CODEX_HOME/auth.json"

關鍵要求是:回寫步驟必須儲存 Codex 在本次執行中生成的重新整理後文件,而不是最初用於初始化的原始檔案。

不需要單獨的重新整理命令

任何一次正常的 Codex 執行都可以重新整理會話。

因此有兩種合適的做法:

  • 讓現有的 CI/CD Codex 任務自然重新整理該檔案。
  • 如果實際任務執行得不夠頻繁,可以增加一個輕量的定時維護任務,如上面的 GitHub Actions 範例。

會話達到過期條件後,第一次執行 Codex 時就會重新整理 auth.json

關鍵執行規則

  • 每臺執行器或每組序列工作流程使用一份獨立的 auth.json
  • 不要讓併發任務或多臺機器共享同一檔案。
  • 不要在每次執行時用原始檔案覆蓋持久化執行器上已經重新整理的檔案。
  • 不要把 auth.json 存入儲存庫、日誌或公開的產物儲存。
  • 如果內建重新整理不再有效,請在可信裝置上重新生成認證檔案。

重新整理失效時的處理方法

該流程可以減少人工操作,但不能保證同一會話永久有效。

出現以下情況時,請用新的 auth.json 重新初始化執行器:

  • Codex 開始返回 401,並且執行器無法繼續重新整理。
  • refresh token 已被撤銷或過期。
  • 另一臺機器或併發任務率先輪換了令牌。
  • 安全儲存的往返流程失敗,恢復了舊檔案。

重新初始化:

  1. 在可信裝置上執行 codex login
  2. 替換 CI/CD 中儲存的 auth.json
  3. 讓下一個執行器任務繼續使用 Codex 的內建重新整理流程。

驗證執行器是否持續維護會話

檢查執行器是否仍儲存著託管認證所需的令牌,以及 last_refresh 是否存在:

AUTH_FILE="${CODEX_HOME:-$HOME/.codex}/auth.json"

jq '{
  auth_mode,
  last_refresh,
  has_access_token: ((.tokens.access_token // "") != ""),
  has_id_token: ((.tokens.id_token // "") != ""),
  has_refresh_token: ((.tokens.refresh_token // "") != "")
}' "$AUTH_FILE"

對於持久化執行器,同一檔案應在多次執行之間始終存在。對於臨時執行器,請確認回寫步驟儲存的是上一次任務生成的更新後文件。

原始碼參考

如需在開源客戶端中驗證上述行為: