🛠️

codex-actionでIssueからのPR作成を自動化する

に公開

OpenAI公式から、codex-actionが公開され、Github ActionsでCodexが使いやすくなりました。
公式ドキュメントではPRがオープンしたときに自動でレビューを行うワークフローが公開されています。
これだけでもだいぶ助かりますが、どうせなら作業もしてほしいですよね。

というわけで作ってみました。
IssueやPRで@codexとメンションするとフィードバックしてくれたり、作業してくれたり、(Issueの場合)PRを開いてくれたりするワークフローを以下のリポジトリで公開しています。
https://github.com/famano/codex-action-assistant

使い方

  1. cloneするかexample/以下から好きなワークフローをダウンロードしてください。ワークフローはIssue用とPR用があります。
  2. ワークフローを自分のリポジトリの.github/workflows/以下に置きます。
  3. 必要であれば依存関係関連のワークフローを自分の実行環境に合わせて調整してください。
  4. OpenAIのAPIキーをOPENAI_API_KEYという名前でリポジトリのシークレットにセットしてください。
  5. Issue用のアシスタントがPRを開くことができるように、リポジトリの Settings > Actions > General > Workflow permissionsAllow GitHub Actions to create and approve pull requests にチェックを入れてください。
  6. デフォルトブランチにワークフローをcommitしてpushするのを忘れずに!

以上のセットアップが終わったら、IssueやPR内で@codexとつけて話しかけてみてください。
READMEにも書いていますので、ご一読を。

依存関係の調整

Codexにはcommitをする前にLinterやTestを実行してほしいですよね。
しかし、Codexはサンドボックス環境で動いているため、インターネットにアクセスできません。そのため、必要な依存関係を自力でダウンロードしてくることができません。
そこで、以下のような形でcodex-actionを実行する前に依存関係をインストールしています。

codex-action-assistant.yaml
      # this step assumes a Node.js project; modify as needed for other environments
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '24'

      # this step assumes a Node.js project; modify as needed for other environments
      # dependencies must installed before running Codex Action because Codex do not have internet access
      - name: run npm install
        run: npm install

見ての通り、Node.jsを前提にしているため、自分の実行環境に合わせてここは書き換えてください。
ちなみに、Codexの設定でsandbox: 'danger-full-access'とするとインターネットアクセスが解禁されるようですが、なんにでもアクセスできるとプロンプトインジェクションの危険があります。
アクセス先をドメインで限定できるとよいですが、現状はできないようです。

動かしてみた

codex-action自身に自分のワークフローファイルの例を修正してもらいました。

以下のようにメンションすると、数分後にコメントが返ってきます。

ちゃんとPRも作成されています。
めちゃくちゃ簡単ですね。

ちなみにCodexくんのアイコンは私が設定したわけではありません。gitのユーザーを設定したら画像がすでに設定されていたので、公式か誰かが作ったのだと思われます。

注意点

  • APIキーを使うので、トークン数に応じた従量課金です。サブスクリプションを使ったアクセスには対応していないようです。
  • CodexにはIssue/PRのタイトル/本文とメンションしたコメントの内容がコンテキストとして渡されます。(インターネットアクセスがないため)前のコメントは参照できませんのでご注意。
  • Codexくんの気まぐれでたまにPRを作成してくれないときがあります。その場合でもcommitの追加があれば強制的にpushさせているので、codex/issue-から始まるブランチを探してみてください。

苦労したポイント

Issue用ワークフローでは、アウトプットのスキーマを設定して、JSONを最終出力にしています。
これにより、返すコメントの内容と、PRを作成するかどうか(単にコメント返しなら作成しない)、PRのタイトル、内容を分けて出力させています。
ちなみにこのスキーマ自体の設定もなかなか大変でした。オブジェクトにはadditionalPropertiesというキーを必ずfalseで入れないといけないし、requiredには必ずすべてのプロパティを入れないとエラーになります。なぜ一意にしか書けないものをわざわざ書かせるのかは謎です。

codex-issue-assistant.yaml
      - name: Run Codex Issue Action
        id: run_codex
        uses: openai/codex-action@v1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          output-schema: |
            {
              "type": "object",
              "properties": {
                "feedback": {
                  "type": "string"
                },
                "create-pull-request": {
                  "type": "object",
                  "properties": {
                    "enable": {
                      "type": "boolean"
                    },
                    "title": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": false,
                  "required": [
                    "enable",
                    "title",
                    "body"
                  ]
                }
              },
              "additionalProperties": false,
              "required": [
                "feedback",
                "create-pull-request"
              ]
            }

output-schemaを設定したはいいものの、Github ActionsではJSONを出力しても文字列として格納されてしまうので、JSONとしてパースしてあげる必要があります。
以下の部分でjqを使ってパースしています。

codex-issue-assistant.yaml
      - name: Extract Codex Outputs
        id: extract_codex
        env:
          CODEX_FINAL_MESSAGE: ${{ steps.run_codex.outputs.final-message }}
        run: |
          final_message_file=$(mktemp)
          printf '%s' "$CODEX_FINAL_MESSAGE" > "$final_message_file"

          feedback=$(jq -r '.feedback' "$final_message_file")
          create_pr=$(jq -r '."create-pull-request".enable' "$final_message_file")
          pr_title=$(jq -r '."create-pull-request".title' "$final_message_file")
          pr_body=$(jq -r '."create-pull-request".body' "$final_message_file")

          {
            echo "feedback<<EOF"
            printf '%s\n' "$feedback"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

          {
            echo "create_pr<<EOF"
            printf '%s\n' "$create_pr"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

          {
            echo "pr_title<<EOF"
            printf '%s\n' "$pr_title"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

          {
            echo "pr_body<<EOF"
            printf '%s\n' "$pr_body"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

          rm -f "$final_message_file"

Codexの返すコメント内容は任意なので、クォーテーションや括弧等が入ると構文が壊れます。そのため、Codexの出力内容を一度環境変数に格納して、一時ファイルに出力してからパースしています。
また、Github Actionsのステップのアウトプットには複数行が入らないので、以下の構文で複数行が入るようにしています。公式ドキュメントも推奨のやり方です。

{name}<<{delimiter}
{value}
{delimiter}

Github Actionsはテスト環境を用意するのが大変なので、このあたりは試行錯誤するのも苦労しました。
そもそもcodex-actionが公式でoutput-schemaの設定を用意しているのに、出てきた出力はパースしないと使い物にならないのが困りものです。暇があればIssueを投げたいところです。

ともかく、ワークフロー自体はかなり便利なものができたと思うので、ぜひ使ってみて感想ください。

Discussion