openai/codex-action

Public

mirrored from https://github.com/openai/codex-actionAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
pr6

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

action.yml

208lines · modecode

1name: "Codex Exec Action"
2description: "Run `codex exec` with a prompt."
3author: "OpenAI"
4inputs:
5 prompt:
6 description: "Prompt to pass to `codex exec`. `prompt` or `prompt_file` must be provided."
7 required: false
8 default: ""
9 prompt_file:
10 description: "Path to file that contains the prompt to pass to `codex exec`. `prompt` or `prompt_file` must be provided."
11 required: false
12 default: ""
13 openai_api_key:
14 description: "OpenAI API key used by the Codex CLI."
15 required: true
16 working_directory:
17 description: "Optional working directory to `cd` into before running `codex exec`."
18 required: false
19 default: ""
20 codex_version:
21 description: "Version of `@openai/codex` to install."
22 required: false
23 default: "0.43.0-alpha.17"
24 codex_args:
25 description: "Additional args to pass through to `codex exec`. If this value starts with `[`, it will be parsed as a JSON array; otherwise, it will be parsed as a shell-like string."
26 required: false
27 default: ""
28 output_file:
29 description: "Path to a JSON Schema file describing the model's final response shape."
30 required: false
31 default: ""
32 output_schema_file:
33 description: "File path to the schema that should be passed to `codex exec --output-schema`."
34 required: false
35 default: ""
36 model:
37 description: "Model the agent should use"
38 required: false
39 default: ""
40 codex_home:
41 description: "Directory to use as the Codex home directory. If empty, the default Codex home directory will be used."
42 required: false
43 default: ""
44 safety_strategy:
45 description: |
46 Specify one of the following options (on Windows, the only supported option is `unsafe`):
47
48 * `drop_sudo` (default, IRREVERSIBLE) Drop sudo privileges (if any) from
49 the default user before running Codex, and run Codex as that user. This
50 is only supported on Linux and macOS runners. This option is
51 irreversible: if the default user has sudo privileges, they will be
52 removed permanently for the duration of the job.
53 * `unprivileged_user` Run Codex as the specified user specified by the
54 `codex_user` option (the user must already exist). Note the caller is
55 responsible for ensuring the specified user has the privileges it needs
56 to perform the requested actions. For example, the copy of the repo
57 created by `actions/checkout` is not world-readable by default.
58 * `read_only` Run Codex in a sandbox that can read any file on disk,
59 but cannot write to disk or access the network. Note Codex will still
60 run as the default user for this Action, which likely has sudo
61 privileges, so it could read `openai_api_key` from memory and reveal it
62 by printing it to the output of the GitHub Action.
63 * `unsafe` (NOT RECOMMENDED) Do not try to restrict Codex's privileges at all.
64 This is extremely dangerous, as the default user for this Action likely
65 has sudo privileges, which means it can read secrets stored in memory
66 (such as the value of `openai_api_key`) and print them to the output
67 of the GitHub Action or exfiltrate them in other ways.
68 required: false
69 default: "drop_sudo"
70 codex_user:
71 description: "If `safety_strategy` is set to `unprivileged_user`, this specifies the UNIX username to run Codex as."
72 required: false
73 default: ""
74 require_repo_write:
75 description: "Whether to require the triggering actor to have write access to the repository before running."
76 required: false
77 default: "true"
78 allow_bots:
79 description: "Allow runs triggered by GitHub Apps/bot accounts to bypass the write-access check."
80 required: false
81 default: "false"
82outputs:
83 final_message:
84 description: "Raw output emitted by `codex exec`."
85 value: ${{ steps.run_codex.outputs.final_message }}
86runs:
87 using: "composite"
88 steps:
89 - name: Validate Windows safety strategy
90 if: ${{ runner.os == 'Windows' }}
91 shell: bash
92 run: |
93 if [ "${{ inputs.safety_strategy }}" != "unsafe" ]; then
94 echo "On Windows, inputs.safety_strategy must be 'unsafe'" >&2
95 echo "because no viable sandboxing options are available at this time." >&2
96 exit 1
97 fi
98
99 - name: Ensure Node.js available
100 uses: actions/setup-node@v4
101 with:
102 node-version: "20"
103
104 - name: Check repository write access
105 if: ${{ inputs.require_repo_write == 'true' }}
106 env:
107 ALLOW_BOTS: ${{ inputs.allow_bots }}
108 GITHUB_TOKEN: ${{ github.token }}
109 shell: bash
110 run: |
111 node "${{ github.action_path }}/dist/main.js" check-write-access --allow-bots "$ALLOW_BOTS"
112
113 - name: Install Codex CLI
114 shell: bash
115 run: npm install -g "@openai/codex@${{ inputs.codex_version }}"
116
117 - name: Install Codex Responses API proxy
118 shell: bash
119 run: npm install -g "@openai/codex-responses-api-proxy@${{ inputs.codex_version }}"
120
121 - name: Start Responses API proxy
122 id: start_proxy
123 env:
124 OPENAI_API_KEY: ${{ inputs.openai_api_key }}
125 shell: bash
126 run: |
127 tmpfile=$(mktemp)
128 (
129 printenv OPENAI_API_KEY | codex-responses-api-proxy --http-shutdown --server-info "$tmpfile"
130 ) &
131 for _ in {1..10}; do
132 if [ -s "$tmpfile" ]; then
133 break
134 fi
135 sleep 1
136 done
137 if [ ! -s "$tmpfile" ]; then
138 echo "responses-api-proxy did not write server info" >&2
139 exit 1
140 fi
141 echo "server_info_file=$tmpfile" >> "$GITHUB_OUTPUT"
142
143 # This step has an output named `port`.
144 - name: Read server info
145 id: read_server_info
146 shell: bash
147 run: node "${{ github.action_path }}/dist/main.js" read-server-info "${{ steps.start_proxy.outputs.server_info_file }}"
148
149 - name: Drop sudo privilege, if appropriate
150 if: ${{ inputs.safety_strategy == 'drop_sudo' }}
151 shell: bash
152 run: |
153 case "${RUNNER_OS}" in
154 Linux)
155 node "${{ github.action_path }}/dist/main.js" drop-sudo --user runner --group sudo
156 ;;
157 macOS)
158 node "${{ github.action_path }}/dist/main.js" drop-sudo --user runner --group admin
159 ;;
160 *)
161 echo "Unsupported OS for drop_sudo: ${RUNNER_OS}" >&2
162 exit 1
163 ;;
164 esac
165
166 - name: Verify sudo privilege removed
167 if: ${{ inputs.safety_strategy == 'drop_sudo' }}
168 shell: bash
169 run: |
170 if sudo -n true 2>/dev/null; then
171 echo "Expected sudo to be disabled, but sudo succeeded." >&2
172 exit 1
173 fi
174 echo "Confirmed sudo privilege is disabled."
175
176 - name: Run codex exec
177 id: run_codex
178 env:
179 CODEX_PROMPT: ${{ inputs.prompt }}
180 CODEX_PROMPT_FILE: ${{ inputs.prompt_file }}
181 CODEX_HOME: ${{ inputs.codex_home }}
182 CODEX_WORKING_DIRECTORY: ${{ inputs.working_directory || github.workspace }}
183 CODEX_ARGS: ${{ inputs.codex_args }}
184 CODEX_OUTPUT_FILE: ${{ inputs.output_file }}
185 CODEX_OUTPUT_SCHEMA_FILE: ${{ inputs.output_schema_file }}
186 CODEX_MODEL: ${{ inputs.model }}
187 CODEX_SAFETY_STRATEGY: ${{ inputs.safety_strategy }}
188 CODEX_USER: ${{ inputs.codex_user }}
189 shell: bash
190 run: |
191 node "${{ github.action_path }}/dist/main.js" run-codex-exec \
192 --prompt "${CODEX_PROMPT}" \
193 --prompt-file "${CODEX_PROMPT_FILE}" \
194 --codex-home "$CODEX_HOME" \
195 --cd "$CODEX_WORKING_DIRECTORY" \
196 --proxy-port "${{ steps.read_server_info.outputs.port }}" \
197 --extra-args "$CODEX_ARGS" \
198 --output-file "$CODEX_OUTPUT_FILE" \
199 --output-schema-file "$CODEX_OUTPUT_SCHEMA_FILE" \
200 --model "$CODEX_MODEL" \
201 --safety-strategy "$CODEX_SAFETY_STRATEGY" \
202 --codex-user "$CODEX_USER"
203
204 - name: Shutdown codex webserver
205 env:
206 CODEX_SERVER_PORT: ${{ steps.read_server_info.outputs.port }}
207 shell: bash
208 run: curl --fail --silent --show-error "http://127.0.0.1:${CODEX_SERVER_PORT}/shutdown"
209