add codex PR review workflow (#8626)
* feat: add codex PR review workflow * refactor: simplify codex PR review comments * chore: use ubicloud for codex review * fix: harden codex review workflow * chore: use chatgpt auth for codex review
This commit is contained in:
23
.github/codex/pr-review.prompt.md
vendored
Normal file
23
.github/codex/pr-review.prompt.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
You are reviewing a GitHub pull request for this repository.
|
||||||
|
|
||||||
|
Review policy:
|
||||||
|
- Read `CLAUDE.md` before reviewing code.
|
||||||
|
- Only report issues you are confident are real and introduced by this pull request.
|
||||||
|
- Focus on bugs, security problems, and clear `CLAUDE.md` violations.
|
||||||
|
- Do not report style nits, speculative concerns, pre-existing issues, or problems that a normal linter/typechecker would obviously catch.
|
||||||
|
- Keep the review high signal. If there is no clear issue, return no findings.
|
||||||
|
|
||||||
|
Repository context:
|
||||||
|
- Read `./.github/codex/pr-review-context.md` for the PR metadata and the exact diff commands to use.
|
||||||
|
- Review only the changes introduced by this PR.
|
||||||
|
- Read additional files only when the diff is not enough to validate a finding.
|
||||||
|
- Do not modify any files.
|
||||||
|
|
||||||
|
Output requirements:
|
||||||
|
- Return a GitHub PR comment in markdown, not JSON.
|
||||||
|
- Start with `## Codex Review`.
|
||||||
|
- Give a short overall summary first.
|
||||||
|
- If you found high-signal issues, list them in a short numbered list with file paths and line numbers when you know them confidently.
|
||||||
|
- If you found no high-signal issues, say that explicitly.
|
||||||
|
- End with a `### Reproduction instructions` section containing a short descriptive paragraph for a tester explaining how to navigate the app to observe the change. Do not make it a numbered list. If the diff is not enough to infer this safely, say that plainly.
|
||||||
|
- Prefer at most 10 findings.
|
||||||
145
.github/workflows/codex-pr-review.yml
vendored
Normal file
145
.github/workflows/codex-pr-review.yml
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
name: Codex Auto Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [ready_for_review, opened]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: codex-review-${{ github.event.pull_request.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
codex-review:
|
||||||
|
runs-on: ubicloud-standard-2
|
||||||
|
timeout-minutes: 30
|
||||||
|
if: github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
steps:
|
||||||
|
- name: Check Codex configuration
|
||||||
|
id: codex_config
|
||||||
|
env:
|
||||||
|
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$CODEX_AUTH_JSON" ]; then
|
||||||
|
echo "enabled=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "enabled=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "CODEX_AUTH_JSON is not configured; skipping Codex review."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Install Codex CLI
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
run: npm install --global @openai/codex@0.117.0
|
||||||
|
|
||||||
|
- name: Configure file-backed Codex auth
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
env:
|
||||||
|
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
|
||||||
|
run: |
|
||||||
|
CODEX_HOME="$HOME/.codex"
|
||||||
|
echo "CODEX_HOME=$CODEX_HOME" >> "$GITHUB_ENV"
|
||||||
|
mkdir -p "$CODEX_HOME"
|
||||||
|
chmod 700 "$CODEX_HOME"
|
||||||
|
cat > "$CODEX_HOME/config.toml" <<'EOF'
|
||||||
|
cli_auth_credentials_store = "file"
|
||||||
|
EOF
|
||||||
|
printf '%s' "$CODEX_AUTH_JSON" > "$CODEX_HOME/auth.json"
|
||||||
|
chmod 600 "$CODEX_HOME/auth.json"
|
||||||
|
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"))' "$CODEX_HOME/auth.json"
|
||||||
|
|
||||||
|
- name: Pre-fetch base and head refs for the PR
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
env:
|
||||||
|
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: |
|
||||||
|
git fetch --no-tags origin \
|
||||||
|
"$PR_BASE_REF" \
|
||||||
|
"+refs/pull/$PR_NUMBER/head"
|
||||||
|
|
||||||
|
- name: Write Codex review context
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
env:
|
||||||
|
PR_REPOSITORY: ${{ github.repository }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||||
|
PR_BODY: ${{ github.event.pull_request.body || '' }}
|
||||||
|
run: |
|
||||||
|
mkdir -p .github/codex
|
||||||
|
node <<'NODE'
|
||||||
|
const fs = require('fs');
|
||||||
|
const lines = [
|
||||||
|
`Repository: ${process.env.PR_REPOSITORY}`,
|
||||||
|
`PR number: ${process.env.PR_NUMBER}`,
|
||||||
|
`Base SHA: ${process.env.PR_BASE_SHA}`,
|
||||||
|
`Head SHA: ${process.env.PR_HEAD_SHA}`,
|
||||||
|
'',
|
||||||
|
'PR title:',
|
||||||
|
process.env.PR_TITLE || '(empty)',
|
||||||
|
'',
|
||||||
|
'PR body:',
|
||||||
|
process.env.PR_BODY || '(empty)',
|
||||||
|
'',
|
||||||
|
'Changed commits command:',
|
||||||
|
`git log --oneline ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
|
||||||
|
'',
|
||||||
|
'Changed files command:',
|
||||||
|
`git diff --stat ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`,
|
||||||
|
'',
|
||||||
|
'Full review diff command:',
|
||||||
|
`git diff --unified=0 ${process.env.PR_BASE_SHA}...${process.env.PR_HEAD_SHA}`
|
||||||
|
];
|
||||||
|
fs.writeFileSync('.github/codex/pr-review-context.md', `${lines.join('\n')}\n`);
|
||||||
|
NODE
|
||||||
|
|
||||||
|
- name: Run Codex review
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
run: |
|
||||||
|
codex exec \
|
||||||
|
-C "$GITHUB_WORKSPACE" \
|
||||||
|
-m gpt-5.4 \
|
||||||
|
-c 'model_reasoning_effort="xhigh"' \
|
||||||
|
-s read-only \
|
||||||
|
-o codex-final-message.md \
|
||||||
|
- < .github/codex/pr-review.prompt.md
|
||||||
|
|
||||||
|
- name: Post Codex review comment
|
||||||
|
if: steps.codex_config.outputs.enabled == 'true'
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = `${process.env.GITHUB_WORKSPACE}/codex-final-message.md`;
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
core.info('Codex did not produce a final message; skipping PR comment.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const body = fs.readFileSync(path, 'utf8').trim();
|
||||||
|
if (!body) {
|
||||||
|
core.info('Codex final message was empty; skipping PR comment.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.pull_request.number,
|
||||||
|
body,
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user