From 4426dde03aca4f3ae9219213367ed524426e5199 Mon Sep 17 00:00:00 2001 From: zhaochangle Date: Fri, 10 Apr 2026 00:01:26 +0800 Subject: [PATCH] [chore](ci) refactor code review workflow to run on PR events with comment-triggered dispatch Split code review workflow into two parts: 1. Main workflow (opencode-review.yml) now triggers on PR events (opened/synchronize/reopened/ready_for_review) as a required check, but only runs actual review when called via workflow_call 2. New comment dispatcher (opencode-review-comment.yml) triggers on /review comments and invokes the main workflow with PR context This ensures the Code Review check is always --- .asf.yaml | 2 +- .claude/skills/code-review/SKILL.md | 10 +- .github/workflows/opencode-review-comment.yml | 75 ++++++ .github/workflows/opencode-review-runner.yml | 223 ++++++++++++++++++ .github/workflows/opencode-review.yml | 173 ++------------ .github/workflows/pr-approve-status.yml | 43 ---- AGENTS.md | 20 +- 7 files changed, 343 insertions(+), 203 deletions(-) create mode 100644 .github/workflows/opencode-review-comment.yml create mode 100644 .github/workflows/opencode-review-runner.yml delete mode 100644 .github/workflows/pr-approve-status.yml diff --git a/.asf.yaml b/.asf.yaml index 6778d7672a5ed6..1ee7798c29a715 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -62,7 +62,7 @@ github: - Build Third Party Libraries (macOS) - Build Third Party Libraries (macOS-arm64) - COMPILE (DORIS_COMPILE) - - Need_2_Approval + - Code Review - Cloud UT (Doris Cloud UT) - performance (Doris Performance) - check_coverage (Coverage) diff --git a/.claude/skills/code-review/SKILL.md b/.claude/skills/code-review/SKILL.md index fcff7368bfc66c..d4579f6a15592c 100644 --- a/.claude/skills/code-review/SKILL.md +++ b/.claude/skills/code-review/SKILL.md @@ -40,9 +40,11 @@ Always focus on the following core invariants during review: - **Evidence Speaks**: All issues with code itself (not memory or environment) must be clearly identified as either having problems or not. For any erroneous situation, if it cannot be confirmed locally, you must provide the specific path or logic where the error occurs. That is, if you believe that if A then B, you must specify a clear scenario where A occurs. - **Review Holistically**: For any new feature or modification, you must analyze its upstream and downstream code to understand the real invocation chain. Identify all implicit assumptions and constraints throughout the flow, then verify carefully that the current change works correctly within the entire end-to-end process. Also determine whether a seemingly problematic local pattern is actually safe due to strong guarantees from upstream or downstream, or whether a conventional local implementation fails to achieve optimal performance because it does not leverage additional information available from the surrounding context. -### 1.3 Critical Checkpoints (Self-Review and Review Priority) +### 1.3 Critical Checkpoints (Review Priority) -The following checkpoints must be **individually confirmed with conclusions** during self-review and review. If the code is too long or too complex, you should delve into the code again as needed when analyzing specific issues, especially the entire logic chain where there are doubts: +For PRs with not only local minor modifications, before answering specific questions, you must read and deeply understand the complete process involved in the code modification, thoroughly comprehend the role, function, and expected functionality of the reviewed functions and modules, the actual triggering methods, potential concurrency, and lifecycle. It is necessary to understand the specific triggering methods and runtime interaction relationships, as well as dependencies, of the specific code in a concrete visual manner. + +The following checkpoints must be **individually confirmed with conclusions** during review. If the code is too long or too complex, you should delve into the code again as needed when analyzing specific issues, especially the entire logic chain where there are doubts: - What is the goal of the current task? Does the current code accomplish this goal? Is there a test that proves it? - Is this modification as small, clear, and focused as possible? @@ -67,6 +69,8 @@ The following checkpoints must be **individually confirmed with conclusions** du - Are end-to-end functional tests comprehensive? - Are there comprehensive negative test cases from various angles? - For complex features, are key functions sufficiently modular with unit tests? +- Has the test result been added/modified? + - Are ALL the new test results correct? - Does the feature need increased observability? If yes: - When bugs occur, are existing logs and VLOGs sufficient to investigate key issues? - Are INFO logs lightweight enough? @@ -86,6 +90,8 @@ After checking all the above items with code. Use the remaining parts of this sk #### 1.3.1 Concurrency and Thread Safety (Highest Priority) +If it involves the judgment of concurrent scenarios, it is necessary to find the starting point of concurrency and actually understand all actually possible concurrent situations (which thread initiated what at what stage, and what concurrent operations there will be). Due to the clear program semantics, some functions of the same module are executed in stages, so concurrency is definitely not present, there should be no misjudgment. + - [ ] **Thread context**: Does every new thread or bthread entry attach the right memory-tracking context? (See `be/src/runtime/AGENTS.md`) - [ ] **Lock protection**: Is shared data accessed under the correct lock and mode? - [ ] **Lock order**: Do nested acquisitions follow the module's documented lock order? (See storage, schema-change, cloud AGENTS.md) diff --git a/.github/workflows/opencode-review-comment.yml b/.github/workflows/opencode-review-comment.yml new file mode 100644 index 00000000000000..96c7cdc97f12d0 --- /dev/null +++ b/.github/workflows/opencode-review-comment.yml @@ -0,0 +1,75 @@ +name: Code Review Comment Dispatch + +on: + issue_comment: + types: [created] + +permissions: + actions: write + pull-requests: write + contents: read + issues: write + +jobs: + resolve-pr: + runs-on: ubuntu-latest + if: >- + github.event.issue.pull_request && + contains(github.event.comment.body, '/review') + outputs: + pr_number: ${{ steps.pr.outputs.pr_number }} + head_sha: ${{ steps.pr.outputs.head_sha }} + base_sha: ${{ steps.pr.outputs.base_sha }} + steps: + - name: Get PR info + id: pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_JSON=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}) + HEAD_SHA=$(echo "$PR_JSON" | jq -r '.head.sha') + BASE_SHA=$(echo "$PR_JSON" | jq -r '.base.sha') + echo "pr_number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" + echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT" + + code-review: + needs: resolve-pr + if: >- + github.event.issue.pull_request && + contains(github.event.comment.body, '/review') + uses: ./.github/workflows/opencode-review-runner.yml + secrets: inherit + with: + pr_number: ${{ needs.resolve-pr.outputs.pr_number }} + head_sha: ${{ needs.resolve-pr.outputs.head_sha }} + base_sha: ${{ needs.resolve-pr.outputs.base_sha }} + + refresh-required-check: + needs: + - resolve-pr + - code-review + runs-on: ubuntu-latest + if: ${{ always() && needs.resolve-pr.result == 'success' && needs.code-review.result != 'skipped' }} + steps: + - name: Rerun pull_request Code Review workflow for current head + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + HEAD_SHA: ${{ needs.resolve-pr.outputs.head_sha }} + run: | + RUNS_JSON=$(gh api repos/${REPO}/actions/workflows/opencode-review.yml/runs --paginate -f event=pull_request -f head_sha=${HEAD_SHA}) + RUN_ID=$(printf '%s' "$RUNS_JSON" | jq -r ' + .workflow_runs + | sort_by(.created_at) + | reverse + | map(select(.head_sha != null)) + | .[0].id // empty + ') + + if [ -z "$RUN_ID" ]; then + echo "No pull_request workflow run found for opencode-review.yml at head ${HEAD_SHA}." + exit 1 + fi + + gh api repos/${REPO}/actions/runs/${RUN_ID}/rerun -X POST diff --git a/.github/workflows/opencode-review-runner.yml b/.github/workflows/opencode-review-runner.yml new file mode 100644 index 00000000000000..22b74a9b8601c7 --- /dev/null +++ b/.github/workflows/opencode-review-runner.yml @@ -0,0 +1,223 @@ +name: Code Review Runner + +on: + workflow_call: + inputs: + pr_number: + required: true + type: string + head_sha: + required: true + type: string + base_sha: + required: true + type: string + +permissions: + pull-requests: write + contents: read + issues: write + +jobs: + code-review: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.head_sha }} + + - name: Install ripgrep + run: | + sudo apt-get update + sudo apt-get install -y ripgrep + + - name: Install OpenCode + run: | + for attempt in 1 2 3; do + if curl -fsSL https://opencode.ai/install | bash; then + echo "$HOME/.opencode/bin" >> $GITHUB_PATH + exit 0 + fi + echo "Install attempt $attempt failed, retrying in 10s..." + sleep 10 + done + echo "All install attempts failed" + exit 1 + + - name: Configure OpenCode auth + run: | + mkdir -p ~/.local/share/opencode + cat > ~/.local/share/opencode/auth.json < opencode.json + + - name: Prepare review prompt + run: | + cat > /tmp/review_prompt.txt <<'PROMPT' + You are performing an automated code review inside a GitHub Actions runner. The gh CLI is available and authenticated via GH_TOKEN. You can comment on the pull request. + + Context: + - Repository: PLACEHOLDER_REPO + - PR number: PLACEHOLDER_PR_NUMBER + - PR Head SHA: PLACEHOLDER_HEAD_SHA + - PR Base SHA: PLACEHOLDER_BASE_SHA + + Before reviewing any code, you MUST read and follow these repository instructions from the checked-out workspace: + 1. Read `.claude/skills/code-review/SKILL.md` in full. + 2. Read the repository root `AGENTS.md` in full. + 3. For each changed source area, read any nearest relevant `AGENTS.md` in the corresponding source directory before drawing conclusions. + + During review, you must strictly follow those instructions. In particular: + - Review the complete invocation chain and runtime behavior, not only the local diff. + - Give explicit conclusions for each applicable critical checkpoint. + - Do not leave uncertainty when the code context can resolve it. + - Use comments only for real issues. If something is safe because of upstream or downstream guarantees, state that clearly instead of speculating. + + ## Submission + - After completing the review, you MUST provide a final summary opinion based on the rules defined in AGENTS.md and the code-review skill. The summary must include conclusions for each applicable critical checkpoint. + - You MUST submit exactly one final GitHub review for the current PR head SHA. + - The final review state MUST be either APPROVE or REQUEST_CHANGES. Do not leave the PR with only comments. + - If no blocking issues are found, submit: gh pr review PLACEHOLDER_PR_NUMBER --approve --body "" + - If blocking issues are found and no inline comments are needed, submit: gh pr review PLACEHOLDER_PR_NUMBER --request-changes --body "" + - If blocking issues are found and inline comments are needed, use GitHub Reviews API to submit a single REQUEST_CHANGES review with inline comments: + - Inline comment bodies may include GitHub suggested changes blocks when you can propose a precise patch. + - Prefer suggested changes for small, self-contained fixes (for example typos, trivial refactors, or narrowly scoped code corrections). + - Do not force suggested changes for broad, architectural, or multi-file issues; explain those normally. + - Build a JSON array of comments like: [{ "path": "", "position": , "body": "..." }] + - Submit via: gh api repos/PLACEHOLDER_REPO/pulls/PLACEHOLDER_PR_NUMBER/reviews --input + - The JSON file should contain: {"event":"REQUEST_CHANGES","body":"","comments":[...]} + - Do not submit a separate summary comment instead of the final review. + PROMPT + sed -i "s|PLACEHOLDER_REPO|${REPO}|g" /tmp/review_prompt.txt + sed -i "s|PLACEHOLDER_PR_NUMBER|${PR_NUMBER}|g" /tmp/review_prompt.txt + sed -i "s|PLACEHOLDER_HEAD_SHA|${HEAD_SHA}|g" /tmp/review_prompt.txt + sed -i "s|PLACEHOLDER_BASE_SHA|${BASE_SHA}|g" /tmp/review_prompt.txt + env: + REPO: ${{ github.repository }} + PR_NUMBER: ${{ inputs.pr_number }} + HEAD_SHA: ${{ inputs.head_sha }} + BASE_SHA: ${{ inputs.base_sha }} + + - name: Run automated code review + id: review + timeout-minutes: 55 + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PROMPT=$(cat /tmp/review_prompt.txt) + + set +e + opencode run "$PROMPT" -m "github-copilot/gpt-5.4" 2>&1 | tee /tmp/opencode-review.log + status=${PIPESTATUS[0]} + set -e + + last_log_line=$(awk 'NF { line = $0 } END { print line }' /tmp/opencode-review.log) + + failure_reason="" + if printf '%s\n' "$last_log_line" | rg -q -i '^Error:|SSE read timed out'; then + failure_reason="$last_log_line" + elif [ "$status" -ne 0 ]; then + failure_reason="OpenCode exited with status $status" + fi + + if [ -n "$failure_reason" ]; then + { + echo "failure_reason<> "$GITHUB_OUTPUT" + exit 1 + fi + + - name: Verify submitted review decision + if: ${{ steps.review.outcome == 'success' }} + id: review_state + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ inputs.pr_number }} + HEAD_SHA: ${{ inputs.head_sha }} + run: | + REVIEWS=$(gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews) + review_state=$(printf '%s' "$REVIEWS" | jq -r --arg head_sha "$HEAD_SHA" ' + [ .[] + | select(.user.login == "github-actions[bot]") + | select(.commit_id == $head_sha) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + ] + | sort_by(.submitted_at) + | last + | .state // "" + ') + + echo "review_state=$review_state" >> "$GITHUB_OUTPUT" + + if [ "$review_state" = "APPROVED" ]; then + echo "Automated review approved the PR." + exit 0 + fi + + if [ "$review_state" = "CHANGES_REQUESTED" ]; then + failure_reason="Automated review requested changes." + else + failure_reason="No final approve/request-changes review was submitted for head SHA ${HEAD_SHA}." + fi + + { + echo "failure_reason<> "$GITHUB_OUTPUT" + exit 1 + + - name: Comment PR on review failure + if: ${{ always() && (steps.review.outcome != 'success' || (steps.review_state.outcome != 'success' && steps.review_state.outputs.review_state == '')) }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEW_FAILURE_REASON: ${{ steps.review.outputs.failure_reason }} + STATE_FAILURE_REASON: ${{ steps.review_state.outputs.failure_reason }} + REVIEW_OUTCOME: ${{ steps.review.outcome }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + error_msg="${REVIEW_FAILURE_REASON:-${STATE_FAILURE_REASON:-Review step was $REVIEW_OUTCOME (possibly timeout or cancelled)}}" + gh pr comment "${{ inputs.pr_number }}" --body "$(cat <- - github.event.issue.pull_request && - contains(github.event.comment.body, '/review') steps: - - name: Get PR info - id: pr + - name: Check automated review decision for current PR head + if: ${{ github.event_name == 'pull_request' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_JSON=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}) - HEAD_SHA=$(echo "$PR_JSON" | jq -r '.head.sha') - BASE_SHA=$(echo "$PR_JSON" | jq -r '.base.sha') - HEAD_REF=$(echo "$PR_JSON" | jq -r '.head.ref') - BASE_REF=$(echo "$PR_JSON" | jq -r '.base.ref') - echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" - echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT" - echo "head_ref=$HEAD_REF" >> "$GITHUB_OUTPUT" - echo "base_ref=$BASE_REF" >> "$GITHUB_OUTPUT" - - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ steps.pr.outputs.head_sha }} - - - name: Install OpenCode - run: | - for attempt in 1 2 3; do - if curl -fsSL https://opencode.ai/install | bash; then - echo "$HOME/.opencode/bin" >> $GITHUB_PATH - exit 0 - fi - echo "Install attempt $attempt failed, retrying in 10s..." - sleep 10 - done - echo "All install attempts failed" - exit 1 - - - name: Configure OpenCode auth - run: | - mkdir -p ~/.local/share/opencode - cat > ~/.local/share/opencode/auth.json < opencode.json - - - name: Prepare review prompt - run: | - cat > /tmp/review_prompt.txt <<'PROMPT' - You are performing an automated code review inside a GitHub Actions runner. The gh CLI is available and authenticated via GH_TOKEN. You can comment on the pull request. - - Context: - - Repository: PLACEHOLDER_REPO - - PR number: PLACEHOLDER_PR_NUMBER - - PR Head SHA: PLACEHOLDER_HEAD_SHA - - PR Base SHA: PLACEHOLDER_BASE_SHA - - When reviewing, you must strictly follow AGENTS.md and the related skills. In addition, you can perform any desired review operations to observe suspicious code and details in order to identify issues as much as possible. - - ## Submission - - After completing the review, you MUST provide a final summary opinion based on the rules defined in AGENTS.md and the code-review skill. The summary must include conclusions for each applicable critical checkpoint. - - If no issues to report, submit a short summary comment saying no issues found using: gh pr comment PLACEHOLDER_PR_NUMBER --body "" - - If issues found, submit a review with inline comments plus a comprehensive summary body. Use GitHub Reviews API to ensure comments are inline: - - Inline comment bodies may include GitHub suggested changes blocks when you can propose a precise patch. - - Prefer suggested changes for small, self-contained fixes (for example typos, trivial refactors, or narrowly scoped code corrections). - - Do not force suggested changes for broad, architectural, or multi-file issues; explain those normally. - - Build a JSON array of comments like: [{ "path": "", "position": , "body": "..." }] - - Submit via: gh api repos/PLACEHOLDER_REPO/pulls/PLACEHOLDER_PR_NUMBER/reviews --input - - The JSON file should contain: {"event":"COMMENT","body":"","comments":[...]} - - Do not use: gh pr review --approve or --request-changes - PROMPT - sed -i "s|PLACEHOLDER_REPO|${REPO}|g" /tmp/review_prompt.txt - sed -i "s|PLACEHOLDER_PR_NUMBER|${PR_NUMBER}|g" /tmp/review_prompt.txt - sed -i "s|PLACEHOLDER_HEAD_SHA|${HEAD_SHA}|g" /tmp/review_prompt.txt - sed -i "s|PLACEHOLDER_BASE_SHA|${BASE_SHA}|g" /tmp/review_prompt.txt - env: REPO: ${{ github.repository }} - PR_NUMBER: ${{ github.event.issue.number }} - HEAD_SHA: ${{ steps.pr.outputs.head_sha }} - BASE_SHA: ${{ steps.pr.outputs.base_sha }} - - - name: Run automated code review - id: review - timeout-minutes: 55 - continue-on-error: true - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PROMPT=$(cat /tmp/review_prompt.txt) - - set +e - opencode run "$PROMPT" -m "github-copilot/gpt-5.4" 2>&1 | tee /tmp/opencode-review.log - status=${PIPESTATUS[0]} - set -e - - last_log_line=$(awk 'NF { line = $0 } END { print line }' /tmp/opencode-review.log) - - failure_reason="" - if printf '%s\n' "$last_log_line" | rg -q -i '^Error:|SSE read timed out'; then - failure_reason="$last_log_line" - elif [ "$status" -ne 0 ]; then - failure_reason="OpenCode exited with status $status" + PR_NUMBER: ${{ github.event.pull_request.number }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + REVIEWS=$(gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews) + review_state=$(printf '%s' "$REVIEWS" | jq -r --arg head_sha "$HEAD_SHA" ' + [ .[] + | select(.user.login == "github-actions[bot]") + | select(.commit_id == $head_sha) + | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") + ] + | sort_by(.submitted_at) + | last + | .state // "" + ') + + if [ "$review_state" = "APPROVED" ]; then + echo "Found APPROVED automated review for ${HEAD_SHA}." + exit 0 fi - if [ -n "$failure_reason" ]; then - { - echo "failure_reason<> "$GITHUB_OUTPUT" + if [ "$review_state" = "CHANGES_REQUESTED" ]; then + echo "Automated review requested changes for ${HEAD_SHA}." exit 1 fi - - name: Comment PR on review failure - if: ${{ always() && steps.review.outcome != 'success' }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - FAILURE_REASON: ${{ steps.review.outputs.failure_reason }} - REVIEW_OUTCOME: ${{ steps.review.outcome }} - RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - run: | - error_msg="${FAILURE_REASON:-Review step was $REVIEW_OUTCOME (possibly timeout or cancelled)}" - gh pr comment "${{ github.event.issue.number }}" --body "$(cat <