openai/codex-action

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
pr13

Branches

Tags

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

Clone

HTTPS

Download ZIP

action.yml

243lines · modecode

1name: "Codex Exec Action"
2description: "Run `codex exec` with a prompt."
3author: "OpenAI"
4inputs:
5 prompt:
6 description: "Prompt to pass to Codex. `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. `prompt` or `prompt-file` must be provided."
11 required: false
12 default: ""
13 output-file:
14 description: "File where the final Codex message is written. Leave empty to skip writing a file."
15 required: false
16 default: ""
17 openai-api-key:
18 description: "OpenAI API key used by Codex."
19 required: false
20 default: ""
21 working-directory:
22 description: "Working directory that Codex should use. Defaults to the repository root."
23 required: false
24 default: ""
25 sandbox:
26 description: |
27 Sandbox mode for Codex. One of `workspace-write` (default), `read-only` or `danger-full-access`.
28 required: false
29 default: "workspace-write"
30 codex-version:
31 description: "Version of `@openai/codex` to install."
32 required: false
33 default: "0.43.0-alpha.18"
34 codex-args:
35 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."
36 required: false
37 default: ""
38 output-schema:
39 description: "Inline schema contents to use with `codex exec --output-schema`."
40 required: false
41 default: ""
42 output-schema-file:
43 description: "File path to the schema that should be passed to `codex exec --output-schema`."
44 required: false
45 default: ""
46 model:
47 description: "Model the agent should use."
48 required: false
49 default: ""
50 codex-home:
51 description: "Directory to use as the Codex home directory. If empty, the default Codex home directory will be used."
52 required: false
53 default: ""
54 safety-strategy:
55 description: |
56 Specify one of the following options (on Windows, the only supported option is `unsafe`):
57
58 * `drop-sudo` (default, IRREVERSIBLE) Drop sudo privileges (if any) from
59 the default user before running Codex, and run Codex as that user. This
60 is only supported on Linux and macOS runners. This option is
61 irreversible: if the default user has sudo privileges, they will be
62 removed permanently for the duration of the job.
63 * `unprivileged-user` Run Codex as the specified user specified by the
64 `codex-user` option (the user must already exist). Note the caller is
65 responsible for ensuring the specified user has the privileges it needs
66 to perform the requested actions. For example, the copy of the repo
67 created by `actions/checkout` is not world-readable by default.
68 * `read-only` Run Codex in a sandbox that can read any file on disk,
69 but cannot write to disk or access the network. Note Codex will still
70 run as the default user for this Action, which likely has sudo
71 privileges, so it could read `openai-api-key` from memory and reveal it
72 by printing it to the output of the GitHub Action.
73 * `unsafe` (NOT RECOMMENDED) Do not try to restrict Codex's privileges at all.
74 This is extremely dangerous, as the default user for this Action likely
75 has sudo privileges, which means it can read secrets stored in memory
76 (such as the value of `openai-api-key`) and print them to the output
77 of the GitHub Action or exfiltrate them in other ways.
78 required: false
79 default: "drop-sudo"
80 codex-user:
81 description: "If `safety-strategy` is set to `unprivileged-user`, this specifies the UNIX username to run Codex as."
82 required: false
83 default: ""
84 allow-users:
85 description: "Comma-separated list of GitHub usernames who can run this action, or '*' to allow all users. Note users who have write access to the GitHub repo have access by default and do not need to be listed here."
86 required: false
87 default: ""
88 allow-bots:
89 description: "Allow runs triggered by GitHub Apps/bot accounts to bypass the write-access check."
90 required: false
91 default: "false"
92outputs:
93 final-message:
94 description: "Raw output emitted by `codex exec`."
95 value: ${{ steps.run_codex.outputs['final-message'] }}
96runs:
97 using: "composite"
98 steps:
99 - name: Validate Windows safety strategy
100 if: ${{ runner.os == 'Windows' }}
101 shell: bash
102 run: |
103 if [ "${{ inputs['safety-strategy'] }}" != "unsafe" ]; then
104 echo "On Windows, inputs['safety-strategy'] must be 'unsafe'" >&2
105 echo "because no viable sandboxing options are available at this time." >&2
106 exit 1
107 fi
108
109 - name: Ensure Node.js available
110 uses: actions/setup-node@v4
111 with:
112 node-version: "20"
113
114 - name: Check repository write access
115 env:
116 GITHUB_TOKEN: ${{ github.token }}
117 shell: bash
118 run: |
119 node "${{ github.action_path }}/dist/main.js" check-write-access \
120 --allow-bots "${{ inputs['allow-bots'] }}" \
121 --allow-users "${{ inputs['allow-users'] }}"
122
123 - name: Install Codex CLI
124 shell: bash
125 run: npm install -g "@openai/codex@${{ inputs['codex-version'] }}"
126
127 - name: Install Codex Responses API proxy
128 shell: bash
129 run: npm install -g "@openai/codex-responses-api-proxy@${{ inputs['codex-version'] }}"
130
131 - name: Resolve Codex home
132 id: resolve_home
133 shell: bash
134 run: |
135 node "${{ github.action_path }}/dist/main.js" resolve-codex-home "${{ inputs['codex-home'] }}"
136
137 - name: Determine server info path
138 id: derive_server_info
139 shell: bash
140 run: |
141 server_info_file="${{ steps.resolve_home.outputs.codex-home }}/${{ github.run_id }}.json"
142 echo "server_info_file=$server_info_file" >> "$GITHUB_OUTPUT"
143
144 - name: Start Responses API proxy
145 id: start_proxy
146 if: ${{ inputs['openai-api-key'] != '' }}
147 env:
148 OPENAI_API_KEY: ${{ inputs['openai-api-key'] }}
149 shell: bash
150 run: |
151 server_info_file="${{ steps.derive_server_info.outputs.server_info_file }}"
152 if [ -s "$server_info_file" ]; then
153 echo "Responses API proxy already appears to be running (found $server_info_file)."
154 exit 0
155 fi
156 (
157 printenv OPENAI_API_KEY | codex-responses-api-proxy --http-shutdown --server-info "$server_info_file"
158 ) &
159 for _ in {1..10}; do
160 if [ -s "$server_info_file" ]; then
161 break
162 fi
163 sleep 1
164 done
165 if [ ! -s "$server_info_file" ]; then
166 echo "responses-api-proxy did not write server info" >&2
167 exit 1
168 fi
169
170 # This step has an output named `port`.
171 - name: Read server info
172 id: read_server_info
173 if: ${{ inputs['openai-api-key'] != '' || inputs.prompt != '' || inputs['prompt-file'] != '' }}
174 shell: bash
175 run: node "${{ github.action_path }}/dist/main.js" read-server-info "${{ steps.derive_server_info.outputs.server_info_file }}"
176
177 - name: Write Codex proxy config
178 if: ${{ inputs['openai-api-key'] != '' }}
179 shell: bash
180 run: |
181 node "${{ github.action_path }}/dist/main.js" write-proxy-config \
182 --codex-home "${{ steps.resolve_home.outputs.codex-home }}" \
183 --port "${{ steps.read_server_info.outputs.port }}"
184
185 - name: Drop sudo privilege, if appropriate
186 if: ${{ inputs['safety-strategy'] == 'drop-sudo' && inputs['openai-api-key'] != '' }}
187 shell: bash
188 run: |
189 case "${RUNNER_OS}" in
190 Linux)
191 node "${{ github.action_path }}/dist/main.js" drop-sudo --user runner --group sudo
192 ;;
193 macOS)
194 node "${{ github.action_path }}/dist/main.js" drop-sudo --user runner --group admin
195 ;;
196 *)
197 echo "Unsupported OS for drop-sudo: ${RUNNER_OS}" >&2
198 exit 1
199 ;;
200 esac
201
202 - name: Verify sudo privilege removed
203 if: ${{ inputs['safety-strategy'] == 'drop-sudo' && inputs['openai-api-key'] != '' }}
204 shell: bash
205 run: |
206 if sudo -n true 2>/dev/null; then
207 echo "Expected sudo to be disabled, but sudo succeeded." >&2
208 exit 1
209 fi
210 echo "Confirmed sudo privilege is disabled."
211
212 - name: Run codex exec
213 id: run_codex
214 if: ${{ inputs.prompt != '' || inputs['prompt-file'] != '' }}
215 env:
216 CODEX_PROMPT: ${{ inputs.prompt }}
217 CODEX_PROMPT_FILE: ${{ inputs['prompt-file'] }}
218 CODEX_OUTPUT_FILE: ${{ inputs['output-file'] }}
219 CODEX_HOME: ${{ steps.resolve_home.outputs.codex-home }}
220 CODEX_WORKING_DIRECTORY: ${{ inputs['working-directory'] || github.workspace }}
221 CODEX_SANDBOX: ${{ inputs.sandbox }}
222 CODEX_ARGS: ${{ inputs['codex-args'] }}
223 CODEX_OUTPUT_SCHEMA: ${{ inputs['output-schema'] }}
224 CODEX_OUTPUT_SCHEMA_FILE: ${{ inputs['output-schema-file'] }}
225 CODEX_MODEL: ${{ inputs.model }}
226 CODEX_SAFETY_STRATEGY: ${{ inputs['safety-strategy'] }}
227 CODEX_USER: ${{ inputs['codex-user'] }}
228 FORCE_COLOR: 1
229 shell: bash
230 run: |
231 node "${{ github.action_path }}/dist/main.js" run-codex-exec \
232 --prompt "${CODEX_PROMPT}" \
233 --prompt-file "${CODEX_PROMPT_FILE}" \
234 --output-file "$CODEX_OUTPUT_FILE" \
235 --codex-home "$CODEX_HOME" \
236 --cd "$CODEX_WORKING_DIRECTORY" \
237 --extra-args "$CODEX_ARGS" \
238 --output-schema "$CODEX_OUTPUT_SCHEMA" \
239 --output-schema-file "$CODEX_OUTPUT_SCHEMA_FILE" \
240 --sandbox "$CODEX_SANDBOX" \
241 --model "$CODEX_MODEL" \
242 --safety-strategy "$CODEX_SAFETY_STRATEGY" \
243 --codex-user "$CODEX_USER"
244