{"id":47702463,"url":"https://github.com/icoretech/codex-action","last_synced_at":"2026-06-03T12:00:32.062Z","repository":{"id":344860794,"uuid":"1183456432","full_name":"icoretech/codex-action","owner":"icoretech","description":"🤖 Run OpenAI Codex CLI non-interactively in GitHub Actions workflows","archived":false,"fork":false,"pushed_at":"2026-05-29T06:28:58.000Z","size":86,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T07:23:50.067Z","etag":null,"topics":["automation","ci","codex","codex-cli","docker","github-actions","openai"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/icoretech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-16T16:16:16.000Z","updated_at":"2026-05-29T06:28:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/icoretech/codex-action","commit_stats":null,"previous_names":["icoretech/codex-action"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/icoretech/codex-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/icoretech","download_url":"https://codeload.github.com/icoretech/codex-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33863264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","ci","codex","codex-cli","docker","github-actions","openai"],"created_at":"2026-04-02T17:39:22.224Z","updated_at":"2026-06-03T12:00:32.002Z","avatar_url":"https://github.com/icoretech.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Codex Action\n\n[![Tests](https://github.com/icoretech/codex-action/actions/workflows/test.yml/badge.svg)](https://github.com/icoretech/codex-action/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nRun OpenAI Codex CLI non-interactively in GitHub Actions workflows via [codex-docker](https://github.com/icoretech/codex-docker).\n\n---\n\n## Quick Start\n\n```yaml\n- name: Run Codex\n  id: codex\n  uses: icoretech/codex-action@v0\n  with:\n    prompt: \"Summarize these changes for operators\"\n    openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n\n- name: Use result\n  run: echo \"${{ steps.codex.outputs.result }}\"\n```\n\n---\n\n## Authentication Setup\n\nYou must provide exactly one of `openai_api_key` or `codex_config`. Providing both or neither will cause the action to fail immediately.\n\n### Option A: API Key\n\n1. Get an API key from [platform.openai.com/api-keys](https://platform.openai.com/api-keys).\n2. Add it as a repository secret named `OPENAI_API_KEY`:\n   **Settings → Secrets and variables → Actions → New repository secret**\n3. Reference it in your workflow:\n\n   ```yaml\n   openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n   ```\n\n---\n\n### Option B: OAuth / Device Auth (`codex_config`)\n\nAuthenticate via device auth and store the resulting `auth.json` as a secret. This is useful for ChatGPT Pro/Plus subscribers who use Codex through their OpenAI account rather than an API key.\n\n**How it works:** The device-auth flow produces an `auth.json` file containing OAuth tokens (access token, refresh token, account ID). Codex uses the access token to authenticate with OpenAI's API. When the access token expires, Codex automatically refreshes it using the refresh token — no keychain or browser required.\n\n1. Pull the codex-docker image:\n\n   ```bash\n   docker pull ghcr.io/icoretech/codex-docker:0.136.0\n   ```\n\n2. Run the device auth flow (the `codex-bootstrap` helper forces file-based credential storage, which is required for CI):\n\n   ```bash\n   mkdir -p .codex\n   docker run --rm -it \\\n     -v \"$PWD/.codex:/home/codex/.codex\" \\\n     ghcr.io/icoretech/codex-docker:0.136.0 \\\n     codex-bootstrap device-auth\n   ```\n\n3. Follow the browser prompt to complete authentication.\n\n4. Verify the credentials were written:\n\n   ```bash\n   # You should see auth.json with OAuth tokens\n   cat .codex/auth.json | python3 -c \"import json,sys; d=json.load(sys.stdin); print('auth_mode:', d['auth_mode'])\"\n   ```\n\n5. Encode the credentials file:\n\n   ```bash\n   # Linux\n   base64 -w0 .codex/auth.json\n\n   # macOS\n   base64 -i .codex/auth.json\n   ```\n\n6. Store the output as a repository secret named `CODEX_CONFIG_B64`:\n   **Settings → Secrets and variables → Actions → New repository secret**\n\n7. Reference it in your workflow:\n\n   ```yaml\n   codex_config: ${{ secrets.CODEX_CONFIG_B64 }}\n   ```\n\n\u003e **Token lifetime:** The access token expires frequently but is refreshed automatically using the refresh token. The refresh token itself eventually expires (typically weeks to months). When authentication starts failing, repeat steps 2–6 to obtain fresh tokens.\n\n---\n\n### Authentication Comparison\n\n| | API Key | OAuth (Device Auth) |\n|---|---|---|\n| **Setup** | Simple (paste key) | Requires device-auth flow via Docker |\n| **Token refresh** | Never expires (until revoked) | Auto-refreshes; refresh token expires after weeks/months |\n| **Best for** | CI/CD with platform API access | ChatGPT Pro/Plus subscribers without a separate API key |\n| **Credential file** | N/A (action runs `codex-bootstrap`) | `auth.json` with OAuth tokens |\n\n---\n\n### Optional: Custom Preferences (`codex_config_toml`)\n\nYou can pass a base64-encoded `config.toml` to customize Codex behavior (model defaults, personality, sandbox mode, etc.). This works with either authentication method.\n\n1. Create a `config.toml` with your preferences:\n\n   ```toml\n   model = \"o4-mini\"\n   sandbox_mode = \"off\"\n   ```\n\n2. Encode it:\n\n   ```bash\n   # Linux\n   base64 -w0 config.toml\n\n   # macOS\n   base64 -i config.toml\n   ```\n\n3. Store the output as a repository secret (e.g., `CODEX_CONFIG_TOML_B64`) and reference it:\n\n   ```yaml\n   codex_config_toml: ${{ secrets.CODEX_CONFIG_TOML_B64 }}\n   ```\n\n\u003e Note: The `model` and `reasoning_effort` action inputs take precedence over values in `config.toml` when both are provided.\n\n---\n\n## Inputs\n\n| Input | Required | Default | Description |\n|---|---|---|---|\n| `prompt` | Yes | — | Instructions for Codex (e.g., `\"Summarize these changes for operators\"`). |\n| `input_text` | No | `\"\"` | Data to process (e.g., changelog content). Appended after the prompt with a `---` separator when provided. |\n| `openai_api_key` | No | `\"\"` | OpenAI API key. Mutually exclusive with `codex_config`. |\n| `codex_config` | No | `\"\"` | Base64-encoded `auth.json` from a prior device-auth session. Mutually exclusive with `openai_api_key`. |\n| `codex_config_toml` | No | `\"\"` | Base64-encoded `config.toml` with Codex preferences (model, personality, etc.). Works with either auth method. |\n| `image_version` | No | `0.136.0` | codex-docker image version tag used for the container. |\n| `model` | No | `\"\"` | Model override passed to `codex exec --model`. When omitted, the model configured in your Codex config is used. |\n| `reasoning_effort` | No | `\"\"` | Reasoning effort level (`minimal`, `low`, `medium`, `high`, `xhigh`). Passed as `model_reasoning_effort` config override. |\n| `network_access` | No | `false` | Allow Codex to make network requests (`curl`, `wget`, etc.) during execution. When `false`, a prompt-level policy instructs the model not to use networking tools. |\n| `sandbox` | No | `full-auto` | Sandbox mode for Codex execution. `full-auto` uses bubblewrap isolation (default). `danger-full-access` disables the sandbox — recommended for CI/Docker where the container is already an isolation boundary. Fixes `bwrap: No permissions to create a new namespace` errors on some runners. |\n| `quiet` | No | `true` | Suppress verbose Codex output (tool calls, grep results, file reads) from workflow logs. Prevents source code leakage in CI logs. Set to `false` for debugging. |\n| `timeout` | No | `300` | Maximum seconds allowed for Codex execution before the step is killed. |\n\n---\n\n## Outputs\n\n| Output | Description |\n|---|---|\n| `result` | Text output produced by Codex. |\n\n---\n\n## Examples\n\n### Changelog Summarization with git-cliff\n\nGenerate a changelog with [git-cliff](https://github.com/orhun/git-cliff), pass it to Codex for operator-friendly summarization, then use the result as a pull request body.\n\n```yaml\nname: Release Summary\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  summarize:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      pull-requests: write\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Generate changelog with git-cliff\n        id: cliff\n        run: |\n          pip install git-cliff\n          CHANGELOG=$(git cliff --latest --strip all)\n          echo \"changelog\u003c\u003cEOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"$CHANGELOG\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"EOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n\n      - name: Summarize changelog with Codex\n        id: codex\n        uses: icoretech/codex-action@v0\n        with:\n          prompt: |\n            You are a technical writer. Summarize the following changelog\n            into a concise, human-readable release summary suitable for\n            an operator audience. Focus on user impact, not implementation\n            details. Use bullet points.\n          input_text: ${{ steps.cliff.outputs.changelog }}\n          openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n\n      - name: Open release PR with summary\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SUMMARY: ${{ steps.codex.outputs.result }}\n        run: |\n          gh pr create \\\n            --title \"Release ${{ github.ref_name }}\" \\\n            --body \"$SUMMARY\" \\\n            --base main \\\n            --head \"${{ github.ref_name }}\"\n```\n\n---\n\n### PR Description Generation\n\nAutomatically generate a pull request description by diffing the branch against the base.\n\n```yaml\nname: Generate PR Description\n\non:\n  pull_request:\n    types: [opened]\n\njobs:\n  describe:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      pull-requests: write\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Get diff\n        id: diff\n        run: |\n          DIFF=$(git diff origin/${{ github.base_ref }}...HEAD -- . ':(exclude)*.lock')\n          echo \"diff\u003c\u003cEOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"$DIFF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"EOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n\n      - name: Generate description with Codex\n        id: codex\n        uses: icoretech/codex-action@v0\n        with:\n          prompt: |\n            You are a senior engineer reviewing a pull request. Given the\n            following git diff, write a clear PR description with these\n            sections: Summary, Changes, and Testing Notes.\n          input_text: ${{ steps.diff.outputs.diff }}\n          openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n\n      - name: Update PR body\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          DESCRIPTION: ${{ steps.codex.outputs.result }}\n        run: |\n          gh pr edit ${{ github.event.pull_request.number }} \\\n            --body \"$DESCRIPTION\"\n```\n\n---\n\n### Code Review Summary\n\nRun an automated code review on every pull request and post the result as a comment.\n\n```yaml\nname: Code Review\n\non:\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  review:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      pull-requests: write\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Get diff\n        id: diff\n        run: |\n          DIFF=$(git diff origin/${{ github.base_ref }}...HEAD)\n          echo \"diff\u003c\u003cEOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"$DIFF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"EOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n\n      - name: Review with Codex\n        id: codex\n        uses: icoretech/codex-action@v0\n        with:\n          prompt: |\n            You are an experienced software engineer performing a code review.\n            Analyze the following diff and provide:\n            - A brief summary of what changed\n            - Any potential bugs or logic errors\n            - Security concerns if applicable\n            - Suggestions for improvement\n            Be concise and constructive.\n          input_text: ${{ steps.diff.outputs.diff }}\n          openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n\n      - name: Post review comment\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          REVIEW: ${{ steps.codex.outputs.result }}\n        run: |\n          gh pr comment ${{ github.event.pull_request.number }} \\\n            --body \"## Automated Code Review\n\n          $REVIEW\n\n          ---\n          *Generated by [codex-action](https://github.com/icoretech/codex-action)*\"\n```\n\n---\n\n### Issue Triage with Cross-Repo Analysis\n\nAutomatically analyze new issues by cloning relevant repositories and posting an implementation plan as a comment. Codex explores the actual source code, references specific files and line numbers, and produces a grounded technical plan.\n\nThis recipe demonstrates:\n- Fetching rich issue metadata (labels, comments, timeline, project board fields)\n- Resolving issue signals (labels, title brackets, body mentions) to repository names\n- Cloning matched repos so Codex can read the source code\n- One-shot analysis with structured output and a bail-out mechanism\n- Auto-labeling based on Codex's analysis\n- Comment upsert (update existing comment on re-run instead of appending)\n\n```yaml\nname: Issue Triage\n\non:\n  issues:\n    types: [opened]\n  workflow_dispatch:\n    inputs:\n      issue_number:\n        description: 'Issue number to analyze'\n        required: true\n        type: number\n\nconcurrency:\n  group: issue-triage-${{ github.event.issue.number || inputs.issue_number }}\n  cancel-in-progress: true\n\njobs:\n  triage:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    env:\n      ISSUE_NUMBER: ${{ github.event.issue.number || inputs.issue_number }}\n    steps:\n      # Wait for the author to finish editing (skip on manual dispatch)\n      - name: Wait for issue to settle\n        if: github.event_name == 'issues'\n        run: sleep 300\n\n      - name: Fetch issue details\n        id: issue\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          repo=\"${{ github.repository }}\"\n          state=$(gh api \"repos/${repo}/issues/${ISSUE_NUMBER}\" --jq '.state')\n          if [ \"$state\" != \"open\" ]; then\n            echo \"skip=true\" \u003e\u003e \"$GITHUB_OUTPUT\"\n            exit 0\n          fi\n          echo \"skip=false\" \u003e\u003e \"$GITHUB_OUTPUT\"\n\n          gh api \"repos/${repo}/issues/${ISSUE_NUMBER}\" \\\n            --jq '{number, title, body, state, labels: [.labels[].name],\n                   assignees: [.assignees[].login], user: .user.login,\n                   created_at, comment_count: .comments}' \u003e /tmp/issue.json\n\n      # Clone repos mentioned in the issue (needs a PAT for private repos)\n      - name: Clone relevant repos\n        if: steps.issue.outputs.skip != 'true'\n        env:\n          GH_TOKEN: ${{ secrets.ORG_PAT }}\n        run: |\n          mkdir -p \"$GITHUB_WORKSPACE/repos\"\n          # Extract repo names from labels, title brackets, body mentions\n          # (your resolution logic here)\n          for repo_name in $REPOS; do\n            gh repo clone \"your-org/$repo_name\" \\\n              \"$GITHUB_WORKSPACE/repos/$repo_name\" \\\n              -- --depth=1 --no-single-branch 2\u003e/dev/null || true\n          done\n\n      # Assemble context file for Codex\n      - name: Prepare context\n        if: steps.issue.outputs.skip != 'true'\n        run: |\n          mkdir -p \"$GITHUB_WORKSPACE/repos\"\n          {\n            echo \"=== ISSUE ===\"\n            cat /tmp/issue.json\n            echo \"\"\n            echo \"=== CLONED REPOS ===\"\n            for d in \"$GITHUB_WORKSPACE/repos\"/*/; do\n              repo=$(basename \"$d\")\n              echo \"--- $repo ---\"\n              find \"$d\" -maxdepth 3 -not -path '*/.git/*' \\\n                -not -path '*/node_modules/*' | head -200\n            done\n          } \u003e \"$GITHUB_WORKSPACE/context.txt\"\n\n      - name: Analyze with Codex\n        if: steps.issue.outputs.skip != 'true'\n        id: analysis\n        uses: icoretech/codex-action@v0\n        with:\n          prompt: |\n            You are a senior engineering triage assistant. This is a ONE-SHOT\n            analysis — do NOT ask questions or defer decisions.\n\n            ## Execution environment\n            You are running inside a read-only GitHub Actions workflow. Do NOT\n            attempt git push, commit, or any state-modifying operations. Your\n            purpose is to examine code and produce a written technical plan.\n\n            ## Available tools\n            Only: bash, git, grep, ripgrep (rg), sed, awk, find, cat, jq, curl.\n            NO npm, node, python, or other runtimes are installed.\n\n            ## Context\n            Read /workspace/context.txt for issue details and repo listings.\n            Source code is under /workspace/repos/ — explore it thoroughly.\n\n            When linking to files, use GitHub URLs:\n            https://github.com/your-org/{repo}/blob/{branch}/{path}#L{line}\n\n            ## Output format\n            1. **Repos involved** — which repo(s) and why\n            2. **Analysis** — what the issue asks for, grounded in actual code\n            3. **Implementation plan** — numbered steps with effort estimates\n            4. **Risks and dependencies**\n            5. **Assumptions**\n\n            ## Auto-labeling\n            At the very end, on a separate line:\n            \u003c!-- CODEX_LABELS: repo1,repo2,repo3 --\u003e\n\n            ## Bail-out\n            If the issue is too vague, already resolved, or not code-related,\n            respond with ONLY: SKIP\n          openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n          network_access: 'false'\n          timeout: '1800'\n\n      - name: Post analysis comment\n        if: \u003e-\n          steps.issue.outputs.skip != 'true'\n          \u0026\u0026 steps.analysis.outputs.result != ''\n          \u0026\u0026 steps.analysis.outputs.result != 'SKIP'\n        env:\n          GH_TOKEN: ${{ github.token }}\n          CODEX_RESULT: ${{ steps.analysis.outputs.result }}\n        run: |\n          clean_result=$(printf '%s\\n' \"$CODEX_RESULT\" \\\n            | sed '/\u003c!-- CODEX_LABELS:.*--\u003e/d')\n          {\n            echo \"### Codex Triage Analysis\"\n            echo \"\"\n            echo \"\u003e [!WARNING]\"\n            echo \"\u003e Automated preliminary analysis — may contain inaccuracies.\"\n            echo \"\"\n            printf '%s\\n' \"$clean_result\"\n          } \u003e /tmp/comment.md\n\n          # Upsert: update existing comment or create new\n          existing=$(gh api \\\n            \"repos/${{ github.repository }}/issues/${ISSUE_NUMBER}/comments?per_page=100\" \\\n            --jq '[.[] | select(.body | contains(\"Codex Triage\"))] | last | .id // empty')\n          if [ -n \"$existing\" ]; then\n            gh api \"repos/${{ github.repository }}/issues/comments/${existing}\" \\\n              -X PATCH -F \"body=@/tmp/comment.md\"\n          else\n            gh issue comment \"$ISSUE_NUMBER\" \\\n              --repo \"${{ github.repository }}\" --body-file /tmp/comment.md\n          fi\n\n      - name: Apply repo labels\n        if: \u003e-\n          steps.issue.outputs.skip != 'true'\n          \u0026\u0026 steps.analysis.outputs.result != ''\n          \u0026\u0026 steps.analysis.outputs.result != 'SKIP'\n        env:\n          GH_TOKEN: ${{ github.token }}\n          CODEX_RESULT: ${{ steps.analysis.outputs.result }}\n        run: |\n          labels=$(printf '%s\\n' \"$CODEX_RESULT\" \\\n            | grep -o 'CODEX_LABELS: [^ ]*' | cut -d' ' -f2 || true)\n          [ -z \"$labels\" ] \u0026\u0026 exit 0\n          IFS=',' read -ra REPO_LABELS \u003c\u003c\u003c \"$labels\"\n          for label in \"${REPO_LABELS[@]}\"; do\n            gh api \"repos/${{ github.repository }}/issues/${ISSUE_NUMBER}/labels\" \\\n              -X POST -f \"labels[]=$label\" 2\u003e/dev/null || true\n          done\n```\n\n**Key implementation notes:**\n\n- **`safe.directory`**: codex-action automatically configures `GIT_CONFIG_GLOBAL` inside the Docker container, so Codex can run git commands on repos cloned by the runner without ownership errors.\n- **`--no-single-branch`**: Cloning with this flag lets Codex check out non-default branches (e.g., `develop`, feature branches) when the issue refers to a specific environment.\n- **Prompt engineering**: The prompt explicitly lists available tools (preventing wasted tokens on `npm: not found`), enforces read-only behavior, and includes a `SKIP` bail-out for non-code issues.\n- **Comment upsert**: On re-runs, the workflow updates the existing triage comment instead of appending a new one.\n- **Auto-labeling**: Codex outputs a hidden HTML comment with repo names; the workflow parses it and applies them as issue labels.\n\n---\n\n### Custom Analysis with Model Override\n\nUse the `model` input to target a specific model for a particular task.\n\n```yaml\nname: Deep Analysis\n\non:\n  workflow_dispatch:\n    inputs:\n      target_file:\n        description: File to analyze\n        required: true\n\njobs:\n  analyze:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Read target file\n        id: content\n        run: |\n          CONTENT=$(cat \"${{ github.event.inputs.target_file }}\")\n          echo \"content\u003c\u003cEOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"$CONTENT\" \u003e\u003e \"$GITHUB_OUTPUT\"\n          echo \"EOF\" \u003e\u003e \"$GITHUB_OUTPUT\"\n\n      - name: Analyze with Codex\n        id: codex\n        uses: icoretech/codex-action@v0\n        with:\n          prompt: |\n            Perform a thorough security and correctness analysis of the\n            following source file. Identify any vulnerabilities, edge cases,\n            and areas that need hardening.\n          input_text: ${{ steps.content.outputs.content }}\n          openai_api_key: ${{ secrets.OPENAI_API_KEY }}\n          model: o4-mini\n          timeout: \"600\"\n\n      - name: Print analysis\n        run: echo \"${{ steps.codex.outputs.result }}\"\n```\n\n---\n\n## Troubleshooting\n\n### Authentication failure\n\n**Symptoms:** The action fails early with an error referencing `codex-bootstrap api-key-login` or an authentication/authorization error from the Codex CLI.\n\n**Fixes:**\n\n- **API key auth:** Verify the secret `OPENAI_API_KEY` is set correctly in your repository and that the key is active at [platform.openai.com/api-keys](https://platform.openai.com/api-keys).\n- **Config auth:** The OAuth token embedded in `auth.json` may have expired. Re-run the device-auth flow, re-encode the file, and update the `CODEX_CONFIG_B64` secret.\n\n---\n\n### Docker image pull failure\n\n**Symptoms:** The action fails with a message like `Unable to find image 'ghcr.io/icoretech/codex-docker:...'` or an HTTP 429 / rate-limit error.\n\n**Fixes:**\n\n- Confirm the `image_version` input matches an available tag on [ghcr.io/icoretech/codex-docker](https://github.com/icoretech/codex-docker/pkgs/container/codex-docker).\n- If you are hitting anonymous pull rate limits, authenticate your runner to GHCR by adding a `docker login` step before the action.\n\n---\n\n### Timeout\n\n**Symptoms:** The step is killed after 300 seconds (the default) with a non-zero exit code.\n\n**Fix:** Increase the `timeout` input:\n\n```yaml\nwith:\n  timeout: \"600\"\n```\n\n---\n\n### Empty output\n\n**Symptoms:** `steps.codex.outputs.result` is an empty string even though the step succeeded.\n\n**Fixes:**\n\n- Review your `prompt` — vague instructions can lead to empty or minimal responses.\n- Check your OpenAI API status at [platform.openai.com/usage](https://platform.openai.com/usage).\n- If using `input_text`, verify the input is not empty before the action runs.\n\n---\n\n### \"Exactly one of openai_api_key or codex_config\" error\n\n**Symptoms:** The action fails immediately with:\n\n```text\nExactly one of openai_api_key or codex_config must be provided, got both\n```\n\nor\n\n```text\nExactly one of openai_api_key or codex_config must be provided, got neither\n```\n\n**Fix:** Provide exactly one authentication method. Remove the unused input or ensure the referenced secret is not empty. Both inputs default to `\"\"`, so an unset secret resolves to an empty string and is treated as \"not provided\".\n\n---\n\n## Development\n\n### Prerequisites\n\n- [bats-core](https://github.com/bats-core/bats-core) for running the test suite\n- [shellcheck](https://www.shellcheck.net/) for static analysis of the shell script\n\n### Running tests\n\n```bash\nbats tests/entrypoint.bats\n```\n\nThe test suite uses a mock `docker` binary (loaded from `tests/test_helper/mocks.bash`) so no real Docker daemon or network access is required.\n\n### Running shellcheck\n\n```bash\nshellcheck entrypoint.sh\n```\n\n### How releases work\n\nThis repository uses [release-please](https://github.com/googleapis/release-please) with the `simple` release type. Merging a conventional-commit PR into `main` triggers release-please to open a release PR. When that release PR is merged:\n\n1. A new semver tag (e.g., `v0.2.0`) is created automatically.\n2. The `update-major-tag` job force-updates the corresponding major tag (e.g., `v0`) to point at the new release.\n\nUsers pinning to a major tag (e.g., `uses: icoretech/codex-action@v0`) always receive the latest patch and minor releases within that major automatically.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficoretech%2Fcodex-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficoretech%2Fcodex-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficoretech%2Fcodex-action/lists"}