github-actions[bot] commented on code in PR #66119:
URL: https://github.com/apache/doris/pull/66119#discussion_r3670813244
##########
.github/workflows/code-review-runner.yml:
##########
@@ -487,61 +680,399 @@ jobs:
HEAD_SHA: ${{ inputs.head_sha }}
BASE_SHA: ${{ inputs.base_sha }}
+ - name: Grant isolated Codex user access to review context
+ run: |
+ sudo chown -R "$CODEX_REVIEW_USER:$CODEX_REVIEW_USER"
"$REVIEW_CONTEXT_DIR"
+
- name: Run automated code review
id: review
timeout-minutes: 115
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OSS_AK: ${{ secrets.OSS_AK }}
+ OSS_SK: ${{ secrets.OSS_SK }}
+ OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
+ OSS_CODEX_AUTH_STATUS_OBJECT:
oss://doris-community-ci/codex/auth-status.json
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ AUTH_UNAVAILABLE: ${{ steps.configure_auth.outputs.auth_unavailable
}}
+ CONFIGURED_ALL_QUOTA_EXHAUSTED: ${{
steps.configure_auth.outputs.all_quota_exhausted }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
HEAD_SHA: ${{ inputs.head_sha }}
+ BASE_SHA: ${{ inputs.base_sha }}
+ REVIEW_FOCUS: ${{ inputs.review_focus }}
+ DEFERRED_REQUEST_ID: ${{ inputs.deferred_request_id }}
+ DEFERRED_CLAIM_ID: ${{ inputs.deferred_claim_id }}
run: |
- GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
- review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ write_review_output() {
+ local key="$1"
+ local value="$2"
+ {
+ echo "${key}<<EOF"
+ printf '%s\n' "$value"
+ echo "EOF"
+ } >> "$GITHUB_OUTPUT"
+ }
+
+ defer_review() {
+ local retry_at="$1" request_id
+ request_id="${DEFERRED_REQUEST_ID:-$GITHUB_RUN_ID}"
+ local request_file
+ request_file="$(mktemp "$RUNNER_TEMP/codex-review-pending.XXXXXX")"
+ jq -n \
+ --arg pr_number "$PR_NUMBER" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg base_sha "$BASE_SHA" \
+ --arg review_focus "$REVIEW_FOCUS" \
+ --arg not_before "$retry_at" \
+ --arg request_id "$request_id" \
+ --arg lease_id '' \
+ --arg lease_expires_at '' \
+ '{pr_number: $pr_number, head_sha: $head_sha, base_sha:
$base_sha,
+ review_focus: $review_focus, not_before: $not_before,
request_id: $request_id,
+ lease_id: $lease_id, lease_expires_at: $lease_expires_at}' >
"$request_file"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$request_file"
"$OSS_CODEX_REVIEW_PENDING_PREFIX/${PR_NUMBER}-${HEAD_SHA}-${request_id}.json"
+ rm -f "$request_file"
+ write_review_output "deferred_review" "true"
+ }
+
+ auth_object_args=()
+ known_auth_object_args=()
+ while IFS= read -r auth_object; do
+ auth_object_args+=(--auth-object "$auth_object")
+ known_auth_object_args+=(--known-auth-object "$auth_object")
+ done < "$CODEX_AUTH_OBJECTS_FILE"
+ if [ "${#auth_object_args[@]}" -eq 0 ]; then
+ write_review_output "all_quota_exhausted" "false"
+ write_review_output "failure_reason" "No valid Codex auth account
is available for review."
+ exit 1
+ fi
- set +e
- # GitHub-hosted runners are ephemeral. Avoid workspace-write here
because
- # Codex uses bubblewrap for that mode and uid maps can be
unavailable.
- codex exec --goal "$GOAL_PROMPT" \
- --cd "$GITHUB_WORKSPACE" \
- --model "gpt-5.6-sol" \
- --config "model_reasoning_effort=xhigh" \
- --sandbox danger-full-access \
- --color never \
- --json \
- --output-last-message
"$REVIEW_CONTEXT_DIR/codex-final-message.txt" \
- > "$REVIEW_CONTEXT_DIR/codex-events.jsonl" \
- 2> >(tee "$REVIEW_CONTEXT_DIR/codex-stderr.log" >&2)
- status=$?
- set -e
+ sync_auth_state() {
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_STATE_FILE" "$OSS_CODEX_AUTH_STATUS_OBJECT"
+ }
+
+ record_auth_result() {
+ local result="$1"
+ local http_status="$2"
+ local retry_after="$3"
+ local record_args=(
+ record
+ --state "$CODEX_AUTH_STATE_FILE"
+ --auth-object "$CODEX_AUTH_STATE_KEY"
+ "${known_auth_object_args[@]}"
+ --result "$result"
+ )
+ if [ -n "$http_status" ]; then
+ record_args+=(--http-status "$http_status")
+ fi
+ if [ -n "$retry_after" ]; then
+ record_args+=(--retry-after "$retry_after")
+ fi
+ "$CODEX_AUTH_STATE_HELPER" "${record_args[@]}"
+ }
+
+ install_selected_auth() {
+ local candidate_auth_file
+ candidate_auth_file="$(mktemp "$RUNNER_TEMP/codex-auth.XXXXXX")"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_OSS_OBJECT" "$candidate_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" validate --auth "$candidate_auth_file"
+ sudo install -o "$CODEX_REVIEW_USER" -g "$CODEX_REVIEW_USER" -m
600 \
+ "$candidate_auth_file" "$CODEX_HOME/auth.json"
+ install -m 600 "$candidate_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ rm -f "$candidate_auth_file"
+ }
+
+ sync_refreshed_auth() {
+ local refreshed_auth_file promoted_auth_file
+ refreshed_auth_file="$(mktemp
"$RUNNER_TEMP/codex-refreshed-auth.XXXXXX")"
+ promoted_auth_file="$(mktemp
"$RUNNER_TEMP/codex-promoted-auth.XXXXXX")"
+ sudo -u "$CODEX_REVIEW_USER" cat "$CODEX_HOME/auth.json" >
"$refreshed_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" promote \
+ --baseline "$CODEX_AUTH_SNAPSHOT" \
+ --candidate "$refreshed_auth_file" \
+ --output "$promoted_auth_file"
+ if ! cmp -s "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"; then
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$promoted_auth_file" "$CODEX_AUTH_OSS_OBJECT"
+ install -m 600 "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ refreshed_digest="$(sha256sum "$promoted_auth_file" | awk
'{print $1}')"
+ "$CODEX_AUTH_STATE_HELPER" set-digest \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ --auth-object "$CODEX_AUTH_STATE_KEY" \
+ --content-digest "$refreshed_digest"
+ sync_auth_state
+ fi
+ rm -f "$refreshed_auth_file" "$promoted_auth_file"
+ }
+
+ review_sync_dir="$RUNNER_TEMP/codex-review-sync"
+ review_actor="$(gh api user --jq .login)"
+ install -m 700 -d "$review_sync_dir"
+
+ snapshot_review_artifacts() {
+ gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" >
"$review_sync_dir/reviews-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" >
"$review_sync_dir/inline-comments-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" >
"$review_sync_dir/issue-comments-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/reviews-before.json" >
"$review_sync_dir/review-ids-before.json"
Review Comment:
[P1] Flatten the paginated artifact pages only once
`gh api --paginate --slurp` returns `[page_array, ...]`. On any non-empty
page, `map(add // [])` merges that page's artifact objects into one object, so
the following `add | map(.id)` indexes scalar fields and exits with `Cannot
index number with string "id"`. The live PR currently has non-empty reviews,
inline comments, and issue comments, and the exact filter fails, so
`snapshot_review_artifacts` aborts before Codex starts. The same double flatten
at lines 844/858/871 is worse during retry reconciliation: jq errors are
treated like a valid false predicate, so landed side effects can be missed. Use
`add // []` once, distinguish jq execution errors from `false`, and add
empty/single-page/multi-page fixtures.
##########
.github/workflows/code-review-retry.yml:
##########
@@ -0,0 +1,183 @@
+name: Retry Deferred Code Reviews
+
+on:
+ schedule:
+ - cron: '*/15 * * * *'
+ workflow_dispatch:
+
+permissions:
+ actions: write
+ contents: read
+ pull-requests: read
+ statuses: write
+
+concurrency:
+ group: code-review-deferred-dispatch
+ cancel-in-progress: false
+
+jobs:
+ dispatch-deferred-reviews:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install ossutil
+ run: |
+ tmp_dir="$(mktemp -d)"
+ trap 'rm -rf "$tmp_dir"' EXIT
+ curl -fsSL -o "$tmp_dir/ossutil.zip"
https://gosspublic.alicdn.com/ossutil/1.7.19/ossutil-v1.7.19-linux-amd64.zip
+ unzip -q "$tmp_dir/ossutil.zip" -d "$tmp_dir"
+ sudo install -m 0755 "$tmp_dir/ossutil-v1.7.19-linux-amd64/ossutil"
/usr/local/bin/ossutil
+
+ - name: Dispatch reviews that have reached their retry time
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OSS_AK: ${{ secrets.OSS_AK }}
+ OSS_SK: ${{ secrets.OSS_SK }}
+ OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ REPO: ${{ github.repository }}
+ run: |
+ post_status() {
+ local head_sha="$1" state="$2" description="$3"
+ gh api "repos/${REPO}/statuses/${head_sha}" \
+ -X POST \
+ -f state="$state" \
+ -f context='code-review' \
+ -f description="$description" \
+ -f target_url="${{ github.server_url }}/${{ github.repository
}}/actions/runs/${GITHUB_RUN_ID}"
+ }
+
+ validate_request() {
+ jq -e '
+ (.pr_number | type == "string" and test("^[0-9]+$"))
+ and (.head_sha | type == "string" and length > 0)
+ and (.base_sha | type == "string" and length > 0)
+ and (.review_focus | type == "string")
+ and (.not_before | type == "string")
+ and (.request_id | type == "string" and test("^[0-9]+$"))
+ and ((.lease_id // "") | type == "string")
+ and ((.lease_expires_at // "") | type == "string")
+ ' "$1" >/dev/null
+ }
+
+ requeue_expired_claims() {
+ local claim_listing claim_object claim_file pending_object
lease_expires_at
+ claim_listing="$RUNNER_TEMP/codex-review-claimed.list"
+ if ! ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" ls \
+ "$OSS_CODEX_REVIEW_CLAIMED_PREFIX/" -s > "$claim_listing"; then
+ echo 'Failed to list claimed Codex review requests.' >&2
+ exit 1
+ fi
+ while IFS= read -r claim_object; do
+ claim_file="$(mktemp "$RUNNER_TEMP/codex-review-claimed.XXXXXX")"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f
"$claim_object" "$claim_file"
+ if ! validate_request "$claim_file"; then
+ echo "Removing malformed claimed review request:
${claim_object##*/}"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" rm -f
"$claim_object"
+ rm -f "$claim_file"
+ continue
+ fi
+ lease_expires_at="$(jq -r '.lease_expires_at // ""'
"$claim_file")"
Review Comment:
[P1] Keep the claim live for the runner's entire lifetime
This expiry is measured from dispatch, but every runner shares a global
concurrency group and may execute for another 120 minutes. A deferred run can
wait almost two hours, pass the one-time lease check with about one hour left,
and still be inside the 115-minute Codex step when this scheduler moves its
“expired” claim back to pending. That permits a second full review while the
original still has GitHub write access. Renew/acquire the lease when execution
actually starts, heartbeat it while active, and make recovery verify the bound
workflow generation is no longer running before requeueing.
##########
.github/workflows/code-review-runner.yml:
##########
@@ -616,61 +1177,61 @@ jobs:
--verify-sleep-seconds 5 \
--verify
- - name: Sync Codex sessions back to OSS
+ - name: Acknowledge terminal deferred review request
if: ${{ always() }}
- continue-on-error: true
- run: |
- if [ ! -d "$CODEX_HOME/sessions" ]; then
- echo "No Codex sessions directory found; skipping session sync."
- exit 0
- fi
-
- uploaded=0
- skipped=0
- while IFS= read -r -d '' session_file; do
- rel="${session_file#"$CODEX_HOME/sessions/"}"
- remote="${OSS_CODEX_SESSION_PREFIX%/}/$rel"
- if ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" stat
"$remote" >/dev/null 2>&1; then
- skipped=$((skipped + 1))
- continue
- fi
- ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f
"$session_file" "$remote"
- uploaded=$((uploaded + 1))
- done < <(find "$CODEX_HOME/sessions" -type f -name '*.jsonl' -print0)
-
- echo "Uploaded $uploaded new Codex session file(s); skipped $skipped
existing file(s)."
env:
OSS_AK: ${{ secrets.OSS_AK }}
OSS_SK: ${{ secrets.OSS_SK }}
OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
- OSS_CODEX_SESSION_PREFIX: oss://doris-community-ci/session
-
- - name: Sync Codex auth back to OSS
- if: ${{ always() }}
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ DEFERRED_REQUEST_ID: ${{ inputs.deferred_request_id }}
+ CONFIGURED_DEFERRED: ${{
steps.configure_auth.outputs.deferred_review }}
+ REVIEW_DEFERRED: ${{ steps.review.outputs.deferred_review }}
run: |
- if [ -z "$CODEX_AUTH_OSS_OBJECT" ]; then
- echo "No selected Codex auth object found; skipping OSS auth sync."
+ if [ -z "$DEFERRED_REQUEST_ID" ] || [ -z
"${CODEX_REVIEW_REQUEST_OBJECT:-}" ]; then
exit 0
fi
-
- if [ ! -s "$CODEX_HOME/auth.json" ]; then
- echo "No Codex auth file found; skipping OSS auth sync."
+ if [[ "$CODEX_REVIEW_REQUEST_OBJECT" ==
"$OSS_CODEX_REVIEW_CLAIMED_PREFIX/"* ]]; then
Review Comment:
[P1] Acknowledge only after the successor or terminal state is durable
This cleanup destroys the only recoverable request in two partial-failure
windows. Both `defer_review` helpers set their output only after uploading the
pending successor; if that upload fails, the step fails with
`deferred_review=false`, so this `always()` branch deletes the original claim
anyway. On a completed review, it also deletes the request before the next step
publishes the terminal commit status; if that API call fails, the original
`code-review` status remains pending and the scheduler has no object left to
repair it. Preserve the claim on failed re-deferral, persist a
terminal/completed state, and delete only after the pending successor or
terminal status has been verified.
##########
.github/workflows/code-review-runner.yml:
##########
@@ -487,61 +680,399 @@ jobs:
HEAD_SHA: ${{ inputs.head_sha }}
BASE_SHA: ${{ inputs.base_sha }}
+ - name: Grant isolated Codex user access to review context
+ run: |
+ sudo chown -R "$CODEX_REVIEW_USER:$CODEX_REVIEW_USER"
"$REVIEW_CONTEXT_DIR"
+
- name: Run automated code review
id: review
timeout-minutes: 115
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OSS_AK: ${{ secrets.OSS_AK }}
+ OSS_SK: ${{ secrets.OSS_SK }}
+ OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
+ OSS_CODEX_AUTH_STATUS_OBJECT:
oss://doris-community-ci/codex/auth-status.json
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ AUTH_UNAVAILABLE: ${{ steps.configure_auth.outputs.auth_unavailable
}}
+ CONFIGURED_ALL_QUOTA_EXHAUSTED: ${{
steps.configure_auth.outputs.all_quota_exhausted }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
HEAD_SHA: ${{ inputs.head_sha }}
+ BASE_SHA: ${{ inputs.base_sha }}
+ REVIEW_FOCUS: ${{ inputs.review_focus }}
+ DEFERRED_REQUEST_ID: ${{ inputs.deferred_request_id }}
+ DEFERRED_CLAIM_ID: ${{ inputs.deferred_claim_id }}
run: |
- GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
- review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ write_review_output() {
+ local key="$1"
+ local value="$2"
+ {
+ echo "${key}<<EOF"
+ printf '%s\n' "$value"
+ echo "EOF"
+ } >> "$GITHUB_OUTPUT"
+ }
+
+ defer_review() {
+ local retry_at="$1" request_id
+ request_id="${DEFERRED_REQUEST_ID:-$GITHUB_RUN_ID}"
+ local request_file
+ request_file="$(mktemp "$RUNNER_TEMP/codex-review-pending.XXXXXX")"
+ jq -n \
+ --arg pr_number "$PR_NUMBER" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg base_sha "$BASE_SHA" \
+ --arg review_focus "$REVIEW_FOCUS" \
+ --arg not_before "$retry_at" \
+ --arg request_id "$request_id" \
+ --arg lease_id '' \
+ --arg lease_expires_at '' \
+ '{pr_number: $pr_number, head_sha: $head_sha, base_sha:
$base_sha,
+ review_focus: $review_focus, not_before: $not_before,
request_id: $request_id,
+ lease_id: $lease_id, lease_expires_at: $lease_expires_at}' >
"$request_file"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$request_file"
"$OSS_CODEX_REVIEW_PENDING_PREFIX/${PR_NUMBER}-${HEAD_SHA}-${request_id}.json"
+ rm -f "$request_file"
+ write_review_output "deferred_review" "true"
+ }
+
+ auth_object_args=()
+ known_auth_object_args=()
+ while IFS= read -r auth_object; do
+ auth_object_args+=(--auth-object "$auth_object")
+ known_auth_object_args+=(--known-auth-object "$auth_object")
+ done < "$CODEX_AUTH_OBJECTS_FILE"
+ if [ "${#auth_object_args[@]}" -eq 0 ]; then
+ write_review_output "all_quota_exhausted" "false"
+ write_review_output "failure_reason" "No valid Codex auth account
is available for review."
+ exit 1
+ fi
- set +e
- # GitHub-hosted runners are ephemeral. Avoid workspace-write here
because
- # Codex uses bubblewrap for that mode and uid maps can be
unavailable.
- codex exec --goal "$GOAL_PROMPT" \
- --cd "$GITHUB_WORKSPACE" \
- --model "gpt-5.6-sol" \
- --config "model_reasoning_effort=xhigh" \
- --sandbox danger-full-access \
- --color never \
- --json \
- --output-last-message
"$REVIEW_CONTEXT_DIR/codex-final-message.txt" \
- > "$REVIEW_CONTEXT_DIR/codex-events.jsonl" \
- 2> >(tee "$REVIEW_CONTEXT_DIR/codex-stderr.log" >&2)
- status=$?
- set -e
+ sync_auth_state() {
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_STATE_FILE" "$OSS_CODEX_AUTH_STATUS_OBJECT"
+ }
+
+ record_auth_result() {
+ local result="$1"
+ local http_status="$2"
+ local retry_after="$3"
+ local record_args=(
+ record
+ --state "$CODEX_AUTH_STATE_FILE"
+ --auth-object "$CODEX_AUTH_STATE_KEY"
+ "${known_auth_object_args[@]}"
+ --result "$result"
+ )
+ if [ -n "$http_status" ]; then
+ record_args+=(--http-status "$http_status")
+ fi
+ if [ -n "$retry_after" ]; then
+ record_args+=(--retry-after "$retry_after")
+ fi
+ "$CODEX_AUTH_STATE_HELPER" "${record_args[@]}"
+ }
+
+ install_selected_auth() {
+ local candidate_auth_file
+ candidate_auth_file="$(mktemp "$RUNNER_TEMP/codex-auth.XXXXXX")"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_OSS_OBJECT" "$candidate_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" validate --auth "$candidate_auth_file"
+ sudo install -o "$CODEX_REVIEW_USER" -g "$CODEX_REVIEW_USER" -m
600 \
+ "$candidate_auth_file" "$CODEX_HOME/auth.json"
+ install -m 600 "$candidate_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ rm -f "$candidate_auth_file"
+ }
+
+ sync_refreshed_auth() {
+ local refreshed_auth_file promoted_auth_file
+ refreshed_auth_file="$(mktemp
"$RUNNER_TEMP/codex-refreshed-auth.XXXXXX")"
+ promoted_auth_file="$(mktemp
"$RUNNER_TEMP/codex-promoted-auth.XXXXXX")"
+ sudo -u "$CODEX_REVIEW_USER" cat "$CODEX_HOME/auth.json" >
"$refreshed_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" promote \
+ --baseline "$CODEX_AUTH_SNAPSHOT" \
+ --candidate "$refreshed_auth_file" \
+ --output "$promoted_auth_file"
+ if ! cmp -s "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"; then
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$promoted_auth_file" "$CODEX_AUTH_OSS_OBJECT"
+ install -m 600 "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ refreshed_digest="$(sha256sum "$promoted_auth_file" | awk
'{print $1}')"
+ "$CODEX_AUTH_STATE_HELPER" set-digest \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ --auth-object "$CODEX_AUTH_STATE_KEY" \
+ --content-digest "$refreshed_digest"
+ sync_auth_state
+ fi
+ rm -f "$refreshed_auth_file" "$promoted_auth_file"
+ }
+
+ review_sync_dir="$RUNNER_TEMP/codex-review-sync"
+ review_actor="$(gh api user --jq .login)"
+ install -m 700 -d "$review_sync_dir"
+
+ snapshot_review_artifacts() {
+ gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" >
"$review_sync_dir/reviews-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" >
"$review_sync_dir/inline-comments-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" >
"$review_sync_dir/issue-comments-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/reviews-before.json" >
"$review_sync_dir/review-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/inline-comments-before.json" >
"$review_sync_dir/inline-comment-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/issue-comments-before.json" >
"$review_sync_dir/issue-comment-ids-before.json"
+ }
+
+ review_side_effects_present() {
+ local reviews_file="$review_sync_dir/reviews.json"
+ local inline_comments_file="$review_sync_dir/inline-comments.json"
+ local issue_comments_file="$review_sync_dir/issue-comments.json"
+ for attempt in 1 2 3 4 5 6; do
+ if ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" > "$reviews_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" > "$inline_comments_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" > "$issue_comments_file"; then
+ return 2
+ fi
+ if jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known "$review_sync_dir/review-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids | index($id)
| not))
+ and ((.submitted_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$reviews_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/inline-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$inline_comments_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/issue-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$issue_comments_file" >/dev/null; then
+ return 0
+ fi
+ sleep 5
+ done
+ return 1
+ }
+
+ select_next_auth() {
+ local selection_json
+ selection_json="$("$CODEX_AUTH_STATE_HELPER" select \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ "${auth_object_args[@]}")"
+ sync_auth_state
+ selected_auth_state_key="$(jq -r '.auth_object // empty'
<<<"$selection_json")"
+ selected_auth_object="${selected_auth_state_key%%#*}"
+ all_quota_exhausted="$(jq -r '.all_quota_exhausted'
<<<"$selection_json")"
+ next_retry_at="$(jq -r '.next_retry_at // empty'
<<<"$selection_json")"
+ }
+
+ if [ "$AUTH_UNAVAILABLE" = "true" ]; then
+ write_review_output "all_quota_exhausted"
"$CONFIGURED_ALL_QUOTA_EXHAUSTED"
+ write_review_output "failure_reason" "No Codex auth account is
currently eligible for review."
+ exit 1
+ fi
+
+ GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
failure_reason=""
- if [ "$status" -ne 0 ]; then
- if [ -s "$REVIEW_CONTEXT_DIR/codex-events.jsonl" ]; then
- failure_reason="$(jq -r 'select(.type == "turn.failed") |
.error.message // empty' "$REVIEW_CONTEXT_DIR/codex-events.jsonl" | tail -n 1)"
+ all_quota_exhausted="${CONFIGURED_ALL_QUOTA_EXHAUSTED:-false}"
+ transient_attempt=0
+ result_dir="$RUNNER_TEMP/codex-review-result"
+ install -m 700 -d "$result_dir"
+ events_file_all_attempts="$result_dir/codex-events.jsonl"
+ : > "$events_file_all_attempts"
+ printf 'CODEX_REVIEW_EVENTS_FILE=%s\n' "$events_file_all_attempts"
>> "$GITHUB_ENV"
+ if ! snapshot_review_artifacts; then
+ write_review_output "failure_reason" "Could not snapshot pull
request artifacts before running Codex."
+ exit 1
+ fi
+ review_attempt=0
+ while :; do
+ review_attempt=$((review_attempt + 1))
+ review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ review_attempt_marker="<!--
doris-codex-review:${GITHUB_RUN_ID}:${review_attempt} -->"
+ attempt_goal_prompt="${GOAL_PROMPT}"$'\n\n'"Every GitHub review,
inline comment, or pull request comment created by this invocation must include
this exact ownership marker: ${review_attempt_marker}"
+
events_file="$result_dir/codex-events-attempt-${review_attempt}.jsonl"
+
stderr_file="$result_dir/codex-stderr-attempt-${review_attempt}.log"
+
+ set +e
+ # GitHub-hosted runners may not support Codex's workspace-write
user namespace.
+ sudo -u "$CODEX_REVIEW_USER" env -i \
+ HOME="$(getent passwd "$CODEX_REVIEW_USER" | cut -d: -f6)" \
+ PATH="$PATH" \
+ GH_TOKEN="$GH_TOKEN" \
+ CODEX_HOME="$CODEX_HOME" \
+ GIT_CONFIG_COUNT=1 \
+ GIT_CONFIG_KEY_0=safe.directory \
+ GIT_CONFIG_VALUE_0="$GITHUB_WORKSPACE" \
+ codex exec --goal "$attempt_goal_prompt" \
+ --cd "$GITHUB_WORKSPACE" \
+ --model "gpt-5.6-sol" \
+ --config "model_reasoning_effort=xhigh" \
+ --sandbox danger-full-access \
+ --color never \
+ --json \
+ --output-last-message
"$REVIEW_CONTEXT_DIR/codex-final-message.txt" \
+ > "$events_file" \
+ 2> >(tee "$stderr_file" >&2)
+ status=$?
+ set -e
+
+ jq -nc \
+ --arg attempt "$review_attempt" \
+ --arg auth_object "${CODEX_AUTH_OSS_OBJECT##*/}" \
+ --arg marker "$review_attempt_marker" \
+ '{type: "review.attempt", metadata: {attempt: $attempt,
auth_object: $auth_object, marker: $marker}}' \
+ >> "$events_file_all_attempts"
+ cat "$events_file" >> "$events_file_all_attempts"
+
+ if ! sync_refreshed_auth; then
Review Comment:
[P1] Propagate refreshed-auth validation failures
Because `sync_refreshed_auth` is invoked as an `if !` condition, Bash
disables `errexit` for commands inside that function. If `promote` rejects the
agent-owned `auth.json`, the pre-created output remains empty, but execution
continues: `cmp` treats it as changed, `ossutil cp -f` can replace the shared
credential with the empty file, the empty digest/state is persisted, and the
final `rm` makes the function return success. Explicitly check and return on
every read, validation, upload, and state-update failure, create the promoted
output only after validation, and add a shell-level rejection test.
##########
.github/workflows/code-review-runner.yml:
##########
@@ -487,61 +680,399 @@ jobs:
HEAD_SHA: ${{ inputs.head_sha }}
BASE_SHA: ${{ inputs.base_sha }}
+ - name: Grant isolated Codex user access to review context
+ run: |
+ sudo chown -R "$CODEX_REVIEW_USER:$CODEX_REVIEW_USER"
"$REVIEW_CONTEXT_DIR"
+
- name: Run automated code review
id: review
timeout-minutes: 115
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OSS_AK: ${{ secrets.OSS_AK }}
+ OSS_SK: ${{ secrets.OSS_SK }}
+ OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
+ OSS_CODEX_AUTH_STATUS_OBJECT:
oss://doris-community-ci/codex/auth-status.json
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ AUTH_UNAVAILABLE: ${{ steps.configure_auth.outputs.auth_unavailable
}}
+ CONFIGURED_ALL_QUOTA_EXHAUSTED: ${{
steps.configure_auth.outputs.all_quota_exhausted }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
HEAD_SHA: ${{ inputs.head_sha }}
+ BASE_SHA: ${{ inputs.base_sha }}
+ REVIEW_FOCUS: ${{ inputs.review_focus }}
+ DEFERRED_REQUEST_ID: ${{ inputs.deferred_request_id }}
+ DEFERRED_CLAIM_ID: ${{ inputs.deferred_claim_id }}
run: |
- GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
- review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ write_review_output() {
+ local key="$1"
+ local value="$2"
+ {
+ echo "${key}<<EOF"
+ printf '%s\n' "$value"
+ echo "EOF"
+ } >> "$GITHUB_OUTPUT"
+ }
+
+ defer_review() {
+ local retry_at="$1" request_id
+ request_id="${DEFERRED_REQUEST_ID:-$GITHUB_RUN_ID}"
+ local request_file
+ request_file="$(mktemp "$RUNNER_TEMP/codex-review-pending.XXXXXX")"
+ jq -n \
+ --arg pr_number "$PR_NUMBER" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg base_sha "$BASE_SHA" \
+ --arg review_focus "$REVIEW_FOCUS" \
+ --arg not_before "$retry_at" \
+ --arg request_id "$request_id" \
+ --arg lease_id '' \
+ --arg lease_expires_at '' \
+ '{pr_number: $pr_number, head_sha: $head_sha, base_sha:
$base_sha,
+ review_focus: $review_focus, not_before: $not_before,
request_id: $request_id,
+ lease_id: $lease_id, lease_expires_at: $lease_expires_at}' >
"$request_file"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$request_file"
"$OSS_CODEX_REVIEW_PENDING_PREFIX/${PR_NUMBER}-${HEAD_SHA}-${request_id}.json"
+ rm -f "$request_file"
+ write_review_output "deferred_review" "true"
+ }
+
+ auth_object_args=()
+ known_auth_object_args=()
+ while IFS= read -r auth_object; do
+ auth_object_args+=(--auth-object "$auth_object")
+ known_auth_object_args+=(--known-auth-object "$auth_object")
+ done < "$CODEX_AUTH_OBJECTS_FILE"
+ if [ "${#auth_object_args[@]}" -eq 0 ]; then
+ write_review_output "all_quota_exhausted" "false"
+ write_review_output "failure_reason" "No valid Codex auth account
is available for review."
+ exit 1
+ fi
- set +e
- # GitHub-hosted runners are ephemeral. Avoid workspace-write here
because
- # Codex uses bubblewrap for that mode and uid maps can be
unavailable.
- codex exec --goal "$GOAL_PROMPT" \
- --cd "$GITHUB_WORKSPACE" \
- --model "gpt-5.6-sol" \
- --config "model_reasoning_effort=xhigh" \
- --sandbox danger-full-access \
- --color never \
- --json \
- --output-last-message
"$REVIEW_CONTEXT_DIR/codex-final-message.txt" \
- > "$REVIEW_CONTEXT_DIR/codex-events.jsonl" \
- 2> >(tee "$REVIEW_CONTEXT_DIR/codex-stderr.log" >&2)
- status=$?
- set -e
+ sync_auth_state() {
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_STATE_FILE" "$OSS_CODEX_AUTH_STATUS_OBJECT"
+ }
+
+ record_auth_result() {
+ local result="$1"
+ local http_status="$2"
+ local retry_after="$3"
+ local record_args=(
+ record
+ --state "$CODEX_AUTH_STATE_FILE"
+ --auth-object "$CODEX_AUTH_STATE_KEY"
+ "${known_auth_object_args[@]}"
+ --result "$result"
+ )
+ if [ -n "$http_status" ]; then
+ record_args+=(--http-status "$http_status")
+ fi
+ if [ -n "$retry_after" ]; then
+ record_args+=(--retry-after "$retry_after")
+ fi
+ "$CODEX_AUTH_STATE_HELPER" "${record_args[@]}"
+ }
+
+ install_selected_auth() {
+ local candidate_auth_file
+ candidate_auth_file="$(mktemp "$RUNNER_TEMP/codex-auth.XXXXXX")"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_OSS_OBJECT" "$candidate_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" validate --auth "$candidate_auth_file"
+ sudo install -o "$CODEX_REVIEW_USER" -g "$CODEX_REVIEW_USER" -m
600 \
+ "$candidate_auth_file" "$CODEX_HOME/auth.json"
+ install -m 600 "$candidate_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ rm -f "$candidate_auth_file"
+ }
+
+ sync_refreshed_auth() {
+ local refreshed_auth_file promoted_auth_file
+ refreshed_auth_file="$(mktemp
"$RUNNER_TEMP/codex-refreshed-auth.XXXXXX")"
+ promoted_auth_file="$(mktemp
"$RUNNER_TEMP/codex-promoted-auth.XXXXXX")"
+ sudo -u "$CODEX_REVIEW_USER" cat "$CODEX_HOME/auth.json" >
"$refreshed_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" promote \
+ --baseline "$CODEX_AUTH_SNAPSHOT" \
+ --candidate "$refreshed_auth_file" \
+ --output "$promoted_auth_file"
+ if ! cmp -s "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"; then
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$promoted_auth_file" "$CODEX_AUTH_OSS_OBJECT"
+ install -m 600 "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ refreshed_digest="$(sha256sum "$promoted_auth_file" | awk
'{print $1}')"
+ "$CODEX_AUTH_STATE_HELPER" set-digest \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ --auth-object "$CODEX_AUTH_STATE_KEY" \
+ --content-digest "$refreshed_digest"
+ sync_auth_state
+ fi
+ rm -f "$refreshed_auth_file" "$promoted_auth_file"
+ }
+
+ review_sync_dir="$RUNNER_TEMP/codex-review-sync"
+ review_actor="$(gh api user --jq .login)"
+ install -m 700 -d "$review_sync_dir"
+
+ snapshot_review_artifacts() {
+ gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" >
"$review_sync_dir/reviews-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" >
"$review_sync_dir/inline-comments-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" >
"$review_sync_dir/issue-comments-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/reviews-before.json" >
"$review_sync_dir/review-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/inline-comments-before.json" >
"$review_sync_dir/inline-comment-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/issue-comments-before.json" >
"$review_sync_dir/issue-comment-ids-before.json"
+ }
+
+ review_side_effects_present() {
+ local reviews_file="$review_sync_dir/reviews.json"
+ local inline_comments_file="$review_sync_dir/inline-comments.json"
+ local issue_comments_file="$review_sync_dir/issue-comments.json"
+ for attempt in 1 2 3 4 5 6; do
+ if ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" > "$reviews_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" > "$inline_comments_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" > "$issue_comments_file"; then
+ return 2
+ fi
+ if jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known "$review_sync_dir/review-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids | index($id)
| not))
+ and ((.submitted_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$reviews_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/inline-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$inline_comments_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/issue-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$issue_comments_file" >/dev/null; then
+ return 0
+ fi
+ sleep 5
+ done
+ return 1
+ }
+
+ select_next_auth() {
+ local selection_json
+ selection_json="$("$CODEX_AUTH_STATE_HELPER" select \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ "${auth_object_args[@]}")"
+ sync_auth_state
+ selected_auth_state_key="$(jq -r '.auth_object // empty'
<<<"$selection_json")"
+ selected_auth_object="${selected_auth_state_key%%#*}"
+ all_quota_exhausted="$(jq -r '.all_quota_exhausted'
<<<"$selection_json")"
+ next_retry_at="$(jq -r '.next_retry_at // empty'
<<<"$selection_json")"
+ }
+
+ if [ "$AUTH_UNAVAILABLE" = "true" ]; then
+ write_review_output "all_quota_exhausted"
"$CONFIGURED_ALL_QUOTA_EXHAUSTED"
+ write_review_output "failure_reason" "No Codex auth account is
currently eligible for review."
+ exit 1
+ fi
+
+ GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
failure_reason=""
- if [ "$status" -ne 0 ]; then
- if [ -s "$REVIEW_CONTEXT_DIR/codex-events.jsonl" ]; then
- failure_reason="$(jq -r 'select(.type == "turn.failed") |
.error.message // empty' "$REVIEW_CONTEXT_DIR/codex-events.jsonl" | tail -n 1)"
+ all_quota_exhausted="${CONFIGURED_ALL_QUOTA_EXHAUSTED:-false}"
+ transient_attempt=0
+ result_dir="$RUNNER_TEMP/codex-review-result"
+ install -m 700 -d "$result_dir"
+ events_file_all_attempts="$result_dir/codex-events.jsonl"
+ : > "$events_file_all_attempts"
+ printf 'CODEX_REVIEW_EVENTS_FILE=%s\n' "$events_file_all_attempts"
>> "$GITHUB_ENV"
+ if ! snapshot_review_artifacts; then
+ write_review_output "failure_reason" "Could not snapshot pull
request artifacts before running Codex."
+ exit 1
+ fi
+ review_attempt=0
+ while :; do
+ review_attempt=$((review_attempt + 1))
+ review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ review_attempt_marker="<!--
doris-codex-review:${GITHUB_RUN_ID}:${review_attempt} -->"
+ attempt_goal_prompt="${GOAL_PROMPT}"$'\n\n'"Every GitHub review,
inline comment, or pull request comment created by this invocation must include
this exact ownership marker: ${review_attempt_marker}"
+
events_file="$result_dir/codex-events-attempt-${review_attempt}.jsonl"
+
stderr_file="$result_dir/codex-stderr-attempt-${review_attempt}.log"
+
+ set +e
+ # GitHub-hosted runners may not support Codex's workspace-write
user namespace.
+ sudo -u "$CODEX_REVIEW_USER" env -i \
+ HOME="$(getent passwd "$CODEX_REVIEW_USER" | cut -d: -f6)" \
+ PATH="$PATH" \
Review Comment:
[P1] Do not inherit an agent-writable executable search path
On this hosted runner, the inherited `PATH` places mode-0777 directories
such as `/opt/pipx_bin` and `/usr/local/bin` before the system directories. The
low-privilege Codex process can create an `ossutil` shim there; when this
parent shell resumes, `sync_refreshed_auth`/`sync_auth_state` invoke
unqualified `ossutil` with `OSS_AK` and `OSS_SK`, handing those secrets to the
shim. This is distinct from the existing mutable-helper issue: the
helper/control directory is now protected, but executable resolution still
crosses the UID boundary. Give Codex a fixed root-owned, non-writable PATH and
use absolute, trusted command paths for every post-agent secret-bearing
operation (or move them to a fresh isolated job).
##########
.github/workflows/code-review-runner.yml:
##########
@@ -487,61 +680,399 @@ jobs:
HEAD_SHA: ${{ inputs.head_sha }}
BASE_SHA: ${{ inputs.base_sha }}
+ - name: Grant isolated Codex user access to review context
+ run: |
+ sudo chown -R "$CODEX_REVIEW_USER:$CODEX_REVIEW_USER"
"$REVIEW_CONTEXT_DIR"
+
- name: Run automated code review
id: review
timeout-minutes: 115
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OSS_AK: ${{ secrets.OSS_AK }}
+ OSS_SK: ${{ secrets.OSS_SK }}
+ OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
+ OSS_CODEX_AUTH_STATUS_OBJECT:
oss://doris-community-ci/codex/auth-status.json
+ OSS_CODEX_REVIEW_PENDING_PREFIX:
oss://doris-community-ci/codex/review-pending
+ OSS_CODEX_REVIEW_CLAIMED_PREFIX:
oss://doris-community-ci/codex/review-claimed
+ AUTH_UNAVAILABLE: ${{ steps.configure_auth.outputs.auth_unavailable
}}
+ CONFIGURED_ALL_QUOTA_EXHAUSTED: ${{
steps.configure_auth.outputs.all_quota_exhausted }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
HEAD_SHA: ${{ inputs.head_sha }}
+ BASE_SHA: ${{ inputs.base_sha }}
+ REVIEW_FOCUS: ${{ inputs.review_focus }}
+ DEFERRED_REQUEST_ID: ${{ inputs.deferred_request_id }}
+ DEFERRED_CLAIM_ID: ${{ inputs.deferred_claim_id }}
run: |
- GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
- review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ write_review_output() {
+ local key="$1"
+ local value="$2"
+ {
+ echo "${key}<<EOF"
+ printf '%s\n' "$value"
+ echo "EOF"
+ } >> "$GITHUB_OUTPUT"
+ }
+
+ defer_review() {
+ local retry_at="$1" request_id
+ request_id="${DEFERRED_REQUEST_ID:-$GITHUB_RUN_ID}"
+ local request_file
+ request_file="$(mktemp "$RUNNER_TEMP/codex-review-pending.XXXXXX")"
+ jq -n \
+ --arg pr_number "$PR_NUMBER" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg base_sha "$BASE_SHA" \
+ --arg review_focus "$REVIEW_FOCUS" \
+ --arg not_before "$retry_at" \
+ --arg request_id "$request_id" \
+ --arg lease_id '' \
+ --arg lease_expires_at '' \
+ '{pr_number: $pr_number, head_sha: $head_sha, base_sha:
$base_sha,
+ review_focus: $review_focus, not_before: $not_before,
request_id: $request_id,
+ lease_id: $lease_id, lease_expires_at: $lease_expires_at}' >
"$request_file"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$request_file"
"$OSS_CODEX_REVIEW_PENDING_PREFIX/${PR_NUMBER}-${HEAD_SHA}-${request_id}.json"
+ rm -f "$request_file"
+ write_review_output "deferred_review" "true"
+ }
+
+ auth_object_args=()
+ known_auth_object_args=()
+ while IFS= read -r auth_object; do
+ auth_object_args+=(--auth-object "$auth_object")
+ known_auth_object_args+=(--known-auth-object "$auth_object")
+ done < "$CODEX_AUTH_OBJECTS_FILE"
+ if [ "${#auth_object_args[@]}" -eq 0 ]; then
+ write_review_output "all_quota_exhausted" "false"
+ write_review_output "failure_reason" "No valid Codex auth account
is available for review."
+ exit 1
+ fi
- set +e
- # GitHub-hosted runners are ephemeral. Avoid workspace-write here
because
- # Codex uses bubblewrap for that mode and uid maps can be
unavailable.
- codex exec --goal "$GOAL_PROMPT" \
- --cd "$GITHUB_WORKSPACE" \
- --model "gpt-5.6-sol" \
- --config "model_reasoning_effort=xhigh" \
- --sandbox danger-full-access \
- --color never \
- --json \
- --output-last-message
"$REVIEW_CONTEXT_DIR/codex-final-message.txt" \
- > "$REVIEW_CONTEXT_DIR/codex-events.jsonl" \
- 2> >(tee "$REVIEW_CONTEXT_DIR/codex-stderr.log" >&2)
- status=$?
- set -e
+ sync_auth_state() {
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_STATE_FILE" "$OSS_CODEX_AUTH_STATUS_OBJECT"
+ }
+
+ record_auth_result() {
+ local result="$1"
+ local http_status="$2"
+ local retry_after="$3"
+ local record_args=(
+ record
+ --state "$CODEX_AUTH_STATE_FILE"
+ --auth-object "$CODEX_AUTH_STATE_KEY"
+ "${known_auth_object_args[@]}"
+ --result "$result"
+ )
+ if [ -n "$http_status" ]; then
+ record_args+=(--http-status "$http_status")
+ fi
+ if [ -n "$retry_after" ]; then
+ record_args+=(--retry-after "$retry_after")
+ fi
+ "$CODEX_AUTH_STATE_HELPER" "${record_args[@]}"
+ }
+
+ install_selected_auth() {
+ local candidate_auth_file
+ candidate_auth_file="$(mktemp "$RUNNER_TEMP/codex-auth.XXXXXX")"
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$CODEX_AUTH_OSS_OBJECT" "$candidate_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" validate --auth "$candidate_auth_file"
+ sudo install -o "$CODEX_REVIEW_USER" -g "$CODEX_REVIEW_USER" -m
600 \
+ "$candidate_auth_file" "$CODEX_HOME/auth.json"
+ install -m 600 "$candidate_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ rm -f "$candidate_auth_file"
+ }
+
+ sync_refreshed_auth() {
+ local refreshed_auth_file promoted_auth_file
+ refreshed_auth_file="$(mktemp
"$RUNNER_TEMP/codex-refreshed-auth.XXXXXX")"
+ promoted_auth_file="$(mktemp
"$RUNNER_TEMP/codex-promoted-auth.XXXXXX")"
+ sudo -u "$CODEX_REVIEW_USER" cat "$CODEX_HOME/auth.json" >
"$refreshed_auth_file"
+ "$CODEX_AUTH_STATE_HELPER" promote \
+ --baseline "$CODEX_AUTH_SNAPSHOT" \
+ --candidate "$refreshed_auth_file" \
+ --output "$promoted_auth_file"
+ if ! cmp -s "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"; then
+ ossutil -i "$OSS_AK" -k "$OSS_SK" -e "$OSS_ENDPOINT" cp -f \
+ "$promoted_auth_file" "$CODEX_AUTH_OSS_OBJECT"
+ install -m 600 "$promoted_auth_file" "$CODEX_AUTH_SNAPSHOT"
+ refreshed_digest="$(sha256sum "$promoted_auth_file" | awk
'{print $1}')"
+ "$CODEX_AUTH_STATE_HELPER" set-digest \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ --auth-object "$CODEX_AUTH_STATE_KEY" \
+ --content-digest "$refreshed_digest"
+ sync_auth_state
+ fi
+ rm -f "$refreshed_auth_file" "$promoted_auth_file"
+ }
+
+ review_sync_dir="$RUNNER_TEMP/codex-review-sync"
+ review_actor="$(gh api user --jq .login)"
+ install -m 700 -d "$review_sync_dir"
+
+ snapshot_review_artifacts() {
+ gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" >
"$review_sync_dir/reviews-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" >
"$review_sync_dir/inline-comments-before.json" \
+ && gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" >
"$review_sync_dir/issue-comments-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/reviews-before.json" >
"$review_sync_dir/review-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/inline-comments-before.json" >
"$review_sync_dir/inline-comment-ids-before.json"
+ jq 'map(add // []) | add // [] | map(.id) | unique'
"$review_sync_dir/issue-comments-before.json" >
"$review_sync_dir/issue-comment-ids-before.json"
+ }
+
+ review_side_effects_present() {
+ local reviews_file="$review_sync_dir/reviews.json"
+ local inline_comments_file="$review_sync_dir/inline-comments.json"
+ local issue_comments_file="$review_sync_dir/issue-comments.json"
+ for attempt in 1 2 3 4 5 6; do
+ if ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/reviews" > "$reviews_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/pulls/${PR_NUMBER}/comments" > "$inline_comments_file" \
+ || ! gh api --paginate --slurp
"repos/${REPO}/issues/${PR_NUMBER}/comments" > "$issue_comments_file"; then
+ return 2
+ fi
+ if jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known "$review_sync_dir/review-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids | index($id)
| not))
+ and ((.submitted_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$reviews_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg head_sha "$HEAD_SHA" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/inline-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.commit_id == $head_sha)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$inline_comments_file" >/dev/null \
+ || jq -e \
+ --arg started_at "$review_started_at" \
+ --arg actor "$review_actor" \
+ --arg marker "$review_attempt_marker" \
+ --slurpfile known
"$review_sync_dir/issue-comment-ids-before.json" '
+ (map(add // []) | add // []) as $artifacts
+ | ($known[0] // []) as $known_ids
+ | any($artifacts[]?; (.id as $id | ($known_ids |
index($id) | not))
+ and ((.created_at // "") >= $started_at)
+ and (.user.login == $actor)
+ and ((.body // "") | contains($marker)))
+ ' "$issue_comments_file" >/dev/null; then
+ return 0
+ fi
+ sleep 5
+ done
+ return 1
+ }
+
+ select_next_auth() {
+ local selection_json
+ selection_json="$("$CODEX_AUTH_STATE_HELPER" select \
+ --state "$CODEX_AUTH_STATE_FILE" \
+ "${auth_object_args[@]}")"
+ sync_auth_state
+ selected_auth_state_key="$(jq -r '.auth_object // empty'
<<<"$selection_json")"
+ selected_auth_object="${selected_auth_state_key%%#*}"
+ all_quota_exhausted="$(jq -r '.all_quota_exhausted'
<<<"$selection_json")"
+ next_retry_at="$(jq -r '.next_retry_at // empty'
<<<"$selection_json")"
+ }
+
+ if [ "$AUTH_UNAVAILABLE" = "true" ]; then
+ write_review_output "all_quota_exhausted"
"$CONFIGURED_ALL_QUOTA_EXHAUSTED"
+ write_review_output "failure_reason" "No Codex auth account is
currently eligible for review."
+ exit 1
+ fi
+
+ GOAL_PROMPT="$(cat "$REVIEW_CONTEXT_DIR/codex_goal_prompt.txt")"
failure_reason=""
- if [ "$status" -ne 0 ]; then
- if [ -s "$REVIEW_CONTEXT_DIR/codex-events.jsonl" ]; then
- failure_reason="$(jq -r 'select(.type == "turn.failed") |
.error.message // empty' "$REVIEW_CONTEXT_DIR/codex-events.jsonl" | tail -n 1)"
+ all_quota_exhausted="${CONFIGURED_ALL_QUOTA_EXHAUSTED:-false}"
+ transient_attempt=0
+ result_dir="$RUNNER_TEMP/codex-review-result"
+ install -m 700 -d "$result_dir"
+ events_file_all_attempts="$result_dir/codex-events.jsonl"
+ : > "$events_file_all_attempts"
+ printf 'CODEX_REVIEW_EVENTS_FILE=%s\n' "$events_file_all_attempts"
>> "$GITHUB_ENV"
+ if ! snapshot_review_artifacts; then
+ write_review_output "failure_reason" "Could not snapshot pull
request artifacts before running Codex."
+ exit 1
+ fi
+ review_attempt=0
+ while :; do
+ review_attempt=$((review_attempt + 1))
+ review_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
+ review_attempt_marker="<!--
doris-codex-review:${GITHUB_RUN_ID}:${review_attempt} -->"
+ attempt_goal_prompt="${GOAL_PROMPT}"$'\n\n'"Every GitHub review,
inline comment, or pull request comment created by this invocation must include
this exact ownership marker: ${review_attempt_marker}"
+
events_file="$result_dir/codex-events-attempt-${review_attempt}.jsonl"
+
stderr_file="$result_dir/codex-stderr-attempt-${review_attempt}.log"
+
+ set +e
+ # GitHub-hosted runners may not support Codex's workspace-write
user namespace.
+ sudo -u "$CODEX_REVIEW_USER" env -i \
+ HOME="$(getent passwd "$CODEX_REVIEW_USER" | cut -d: -f6)" \
+ PATH="$PATH" \
+ GH_TOKEN="$GH_TOKEN" \
Review Comment:
[P1] Keep commit-status authority out of the agent token
`statuses: write` is job-wide, so the `GITHUB_TOKEN` passed here lets the
prompt-injectable Codex process call the commit-status API itself. It can
publish `code-review=success` for the reviewed SHA and then prevent the parent
status step from running; the ownership marker cannot constrain raw API
authority. Keep terminal status publication in a separate job/workflow with its
own token, and give Codex a narrowly scoped token that can submit the review
but cannot mutate commit statuses.
##########
.github/workflows/code-review-runner.yml:
##########
@@ -31,11 +39,24 @@ on:
required: false
type: string
default: ''
+ deferred_request_id:
+ required: false
+ type: string
+ default: ''
+ deferred_claim_id:
+ required: false
+ type: string
+ default: ''
permissions:
pull-requests: write
contents: read
issues: write
+ statuses: write
+
+concurrency:
Review Comment:
[P1] Preserve every review queued behind the auth lock
`cancel-in-progress: false` protects the running workflow, but GitHub's
default concurrency mode still keeps only one pending run and cancels the
previous pending run when a newer one arrives
([documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)).
Each direct or scheduled request has already received a three-hour OSS lease
before it enters this group, so a canceled pending runner performs no cleanup
and its required status remains pending until lease expiry; under backlog,
requests can be repeatedly canceled and starved. Use a queue that preserves
pending runs (for example `queue: max`) or serialize only the short auth-state
transaction, and do not mark a request leased before its runner starts.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]