openai/codex-action

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
pr7

Branches

Tags

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

Clone

HTTPS

Download ZIP

action.yml

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