GitHub Actions bot that reviews pull requests by calling Codex CLI, leaves inline review comments for problems, approves clean PRs, and can merge them when repository checks are green.
- Triggers on
pull_request_target:opened,synchronize,reopened,ready_for_review. - When a new PR is opened, posts an immediate comment that review has started.
- Fetches changed files and patches from the pull request.
- Asks Codex CLI to review the PR and write a structured JSON result to
.review-result.json:safeToMerge=trueonly when there are no blocking findings.blockingfindings become request-changes reviews.suggestionfindings are included as non-blocking suggestions.
- Supports a lenient pass when a repository administrator comments
/lenient-check. - If no repository administrator has commented or reviewed within the last 24 hours, the lenient trigger automatically broadens to users with
write,maintain, oradminpermission. - A scheduled recheck runs hourly so the 24-hour fallback can take effect even if no new PR event arrives after the window expires.
- The hourly scheduled run also deletes merged branches immediately and deletes branches for closed PRs after 3 days.
- In lenient mode, only blocks dangerous changes, runtime-impacting issues, errors, crashes, broken builds, data-loss risks, and security risks.
- A PR that passes lenient mode is not auto-merged until an eligible reviewer approves the current head commit.
- Eligible reviewers for lenient merge approval are:
- repository administrators when an administrator has commented or reviewed on the PR within the last 24 hours
- otherwise any repository user with
write,maintain, oradminpermission
- If the reviewer detects clearly malicious code, the bot comments with the reason and closes the pull request.
- Posts inline comments only on valid added diff lines.
- Approves clean pull requests.
- Retries transient Codex CLI and GitHub API request failures up to 5 times before giving up.
- Deletes any stale
.review-result.jsonbefore each run and again after each review attempt. - If
AUTO_MERGE=true, merges only after:- the AI reviewer says the PR is safe,
- GitHub reports the PR as mergeable,
- checks/statuses are green when
REQUIRE_CHECKS=true.
Add these repository secrets:
CODEX_API_KEY- Optional for GitHub App identity:
GH_APP_IDGH_APP_PRIVATE_KEYGH_APP_INSTALLATION_ID
Optional repository variables:
CODEX_BASE_URL: defaults tohttps://api.openai.com/v1CODEX_MODEL: defaults togpt-5.4CODEX_REASONING_EFFORT:minimal,low,medium,high, orxhigh, defaults tohighAUTO_MERGE: defaults tofalseMERGE_METHOD:merge,squash, orrebase, defaults tosquashREQUIRE_CHECKS: defaults totrueMAX_PATCH_CHARS: defaults to120000BRANCH_CLEANUP_SKIP_BRANCHES: comma-separated branch patterns to keep, for examplemain,develop,release/*
The workflow automatically receives github.token from GitHub Actions, so you do not need to create a repository secret named GITHUB_TOKEN.
If you do nothing else, ghbot uses that workflow token for comments, reviews, checks, and merges.
If you configure a GitHub App, ghbot prefers the App installation token for those GitHub write operations and falls back to the workflow token if App auth fails.
It installs Codex CLI inside the GitHub Actions runner and uses that CLI for the review itself. The workflow writes an isolated Codex config.toml at runtime with the configured model, provider base URL, and reasoning setting, then asks Codex CLI to persist its final review result into .review-result.json.
You only need this if you want the bot to act as your GitHub App instead of github-actions[bot].
- Create a GitHub App at
Settings -> Developer settings -> GitHub Apps -> New GitHub App - Recommended fields:
GitHub App name: any unique name, for exampleghbotHomepage URL: your repository URL or project URLWebhook: disabled, this workflow-based setup does not need a webhook
- App permissions:
- Repository permissions:
Contents:Read and writePull requests:Read and writeChecks:Read and writeIssues:Read and writeMetadata:Read-only
- Repository permissions:
- Subscribe to no webhook events unless you need them for something else
- Create and download the private key
- Install the App on the target repository
- In the target repository, add these Actions secrets:
GH_APP_ID: the numeric App IDGH_APP_PRIVATE_KEY: the full PEM private key content- Optional
GH_APP_INSTALLATION_ID: the numeric installation ID
Notes:
- GitHub Actions secrets and variables cannot start with
GITHUB_, so use theGH_APP_*names above in repository settings. GH_APP_INSTALLATION_IDis optional. ghbot can resolve it automatically from the repository if the App is installed there.- Keep
CODEX_API_KEYas a separate secret. The App only replaces GitHub write identity; it does not replace Codex authentication. - The repository still gets
github.tokenautomatically from GitHub Actions, and ghbot uses it as a fallback if App authentication is unavailable or broken. - The scheduled branch cleanup skips the repository default branch and any branches matched by
BRANCH_CLEANUP_SKIP_BRANCHES. It also skips protected branches automatically.
This repository now exposes a reusable workflow at:
lezi-fun/ghbot/.github/workflows/review-reusable.yml@main
In another repository, create a thin wrapper workflow such as:
name: PR Review Bot
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
issue_comment:
types: [created]
pull_request_review:
types: [submitted]
permissions:
contents: write
pull-requests: write
checks: write
statuses: read
issues: write
jobs:
review:
if: |
github.event_name == 'pull_request_target' ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null) ||
github.event_name == 'pull_request_review'
uses: lezi-fun/ghbot/.github/workflows/review-reusable.yml@main
secrets:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
GH_APP_INSTALLATION_ID: ${{ secrets.GH_APP_INSTALLATION_ID }}
with:
event_name: ${{ github.event_name }}
event_action: ${{ github.event.action }}
repository_owner: ${{ github.repository_owner }}
repository_name: ${{ github.event.repository.name }}
pull_number: ${{ github.event.pull_request.number || github.event.issue.number }}
commenter_login: ${{ github.event.comment.user.login || '' }}
comment_body: ${{ github.event.comment.body || '' }}
reviewer_login: ${{ github.event.review.user.login || '' }}
review_state: ${{ github.event.review.state || '' }}
review_commit_id: ${{ github.event.review.commit_id || '' }}
bot_name: github-actions[bot]
codex_base_url: ${{ vars.CODEX_BASE_URL || 'https://api.openai.com/v1' }}
codex_model: ${{ vars.CODEX_MODEL || 'gpt-5.4' }}
codex_reasoning_effort: ${{ vars.CODEX_REASONING_EFFORT || 'high' }}
auto_merge: ${{ vars.AUTO_MERGE || 'false' }}
merge_method: ${{ vars.MERGE_METHOD || 'squash' }}
require_checks: ${{ vars.REQUIRE_CHECKS || 'true' }}
max_patch_chars: ${{ vars.MAX_PATCH_CHARS || '120000' }}
branch_cleanup_skip_branches: ${{ vars.BRANCH_CLEANUP_SKIP_BRANCHES || '' }}
log_level: infoThe caller repository still needs to configure:
CODEX_API_KEYas a secret- optional
GH_APP_ID,GH_APP_PRIVATE_KEY, andGH_APP_INSTALLATION_IDsecrets if you want GitHub App identity CODEX_BASE_URL,CODEX_MODEL,CODEX_REASONING_EFFORT,AUTO_MERGE,MERGE_METHOD,REQUIRE_CHECKS, andMAX_PATCH_CHARSas repository variables as neededBRANCH_CLEANUP_SKIP_BRANCHESas a repository variable if you want to keep long-lived or shared branches out of scheduled cleanup
Repository Actions permissions must allow the workflow token to:
contents: writepull-requests: writechecks: writeissues: writestatuses: read
These permissions are declared in .github/workflows/review.yml.
If a strict review leaves findings that are not practical to change, a repository administrator can comment this on the PR:
/lenient-check
That reruns the AI in lenient mode.
If no administrator has commented or reviewed within the last 24 hours on that PR, the command is also accepted from users with write or maintain permission.
If lenient mode passes, the bot still requires an eligible reviewer to approve the current head commit before merge.
Approval eligibility follows the same rule as lenient triggering:
- if a repository administrator has commented or reviewed on that PR within the last 24 hours, only an administrator approval can unlock merge
- otherwise any user with
write,maintain, oradminpermission can unlock merge - an hourly scheduled recheck reevaluates this fallback window for already-approved PRs
npm install
npm run build
npm run typecheckTo simulate the workflow locally, export the same env vars that the GitHub Action uses and run:
node dist/src/actions/runReview.jsYou must also provide:
GITHUB_EVENT_NAMEGITHUB_EVENT_PATH- either
GITHUB_TOKEN, orGH_APP_IDplusGH_APP_PRIVATE_KEY
See .env.example for local testing values.
Important variables:
GITHUB_TOKEN: optional in GitHub Actions becausegithub.tokenis automatic; still useful for local simulation or as a fallbackGH_APP_ID: optional GitHub App IDGH_APP_PRIVATE_KEY: optional GitHub App private keyGH_APP_INSTALLATION_ID: optional GitHub App installation IDCODEX_API_KEY: API key used by Codex CLICODEX_BASE_URL: optional Codex/OpenAI-compatible base URLCODEX_MODEL: defaults togpt-5.4CODEX_REASONING_EFFORT: optional, one ofminimal,low,medium,high,xhighBOT_NAME: defaults toghbot, but the workflow sets it togithub-actions[bot]AUTO_MERGE: defaults tofalseMERGE_METHOD: defaults tosquashREQUIRE_CHECKS: defaults totrueMAX_PATCH_CHARS: defaults to120000BRANCH_CLEANUP_SKIP_BRANCHES: optional comma-separated list of branch patterns skipped by scheduled cleanup
The generated Codex CLI config is intentionally close to a working local setup:
model = "gpt-5.4"
model_provider = "bot"
approvals_reviewer = "user"
model_reasoning_effort = "high"
[model_providers.bot]
name = "bot"
base_url = "https://api.openai.com/v1"
env_key = "CODEX_API_KEY"
wire_api = "responses"The workflow invokes Codex CLI with --dangerously-bypass-approvals-and-sandbox, relies on .review-result.json for the final machine-readable result, and streams Codex CLI stdout/stderr into the GitHub Actions log for debugging.
Start with AUTO_MERGE=false. Let the bot comment and approve first, then enable auto-merge after you trust the behavior on your repositories.
This bot does not execute untrusted PR code. It reviews diffs and checks GitHub check/status results. Keep repository branch protection enabled so required CI and human override rules still apply.