From df460f9ab76db9dde3c3b67ab0c3ed42ab02ef5f Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Sun, 18 Jan 2026 01:33:42 +0400 Subject: [PATCH] chore: merge opencode and greeting into issue validation --- .github/workflows/greetings.yml | 20 --- .github/workflows/issue-validation.yml | 230 +++++++++++++++++++++---- 2 files changed, 199 insertions(+), 51 deletions(-) delete mode 100644 .github/workflows/greetings.yml diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml deleted file mode 100644 index 22d1801..0000000 --- a/.github/workflows/greetings.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Greetings - -on: - issues: - types: [opened] - pull_request: - types: [opened] - -jobs: - greeting: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/first-interaction@1c4688942c71f71d4f5502a26ea67c331730fa4d # v3.1.0 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - issue_message: "Welcome to the community and thank you for your first issue ❤️ A human will review it shortly." - pr_message: "Welcome to the community and thank you for your first contribution ❤️ A human will review your PR shortly. Make sure that the pipelines are green, so that the PR is considered ready for a review and could be merged." diff --git a/.github/workflows/issue-validation.yml b/.github/workflows/issue-validation.yml index 36d56bd..afd97a6 100644 --- a/.github/workflows/issue-validation.yml +++ b/.github/workflows/issue-validation.yml @@ -1,16 +1,25 @@ -name: Issue Validation +name: Issue & PR Automation on: issues: types: [opened] + pull_request: + types: [opened] + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] permissions: contents: read issues: write + pull-requests: write models: read + id-token: write jobs: validate-issue: + if: github.event_name == 'issues' runs-on: ubuntu-latest steps: @@ -20,7 +29,6 @@ jobs: - name: Get issue templates id: get-templates run: | - # Read the issue templates if [ -f ".github/ISSUE_TEMPLATE/01-bug-report.md" ]; then echo "bug-template-exists=true" >> $GITHUB_OUTPUT fi @@ -53,7 +61,7 @@ jobs: with: prompt-file: issue_analysis.txt system-prompt: | - You are an issue validation assistant for Donut Browser, an anti-detect browser. + You are an issue validation assistant for Donut Browser, an anti-detect browser. Analyze the provided issue content and determine if it contains sufficient information based on these requirements: @@ -100,17 +108,14 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_AUTHOR: ${{ github.event.issue.user.login }} run: | - # Check if user has created issues before (excluding the current one) ISSUE_COUNT=$(gh api "/repos/${{ github.repository }}/issues" \ --jq "map(select(.user.login == \"$ISSUE_AUTHOR\" and .number != ${{ github.event.issue.number }})) | length" \ --paginate || echo "0") - + if [ "$ISSUE_COUNT" = "0" ]; then echo "is_first_time=true" >> $GITHUB_OUTPUT - echo "✅ First-time contributor detected" else echo "is_first_time=false" >> $GITHUB_OUTPUT - echo "ℹ️ Returning contributor" fi - name: Parse validation result and take action @@ -118,41 +123,29 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RESPONSE_FILE: ${{ steps.validate.outputs.response-file }} run: | - # Read from the response file (RESPONSE env var gets truncated for large outputs) if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then - echo "Reading response from file: $RESPONSE_FILE" RAW_OUTPUT=$(cat "$RESPONSE_FILE") else echo "::error::Response file not found: $RESPONSE_FILE" exit 1 fi - # Extract JSON if wrapped in markdown code fences; otherwise use raw JSON_RESULT=$(printf "%s" "$RAW_OUTPUT" | sed -n '/```json/,/```/p' | sed '1d;$d') if [ -z "$JSON_RESULT" ]; then JSON_RESULT="$RAW_OUTPUT" fi - # Validate JSON before parsing if ! echo "$JSON_RESULT" | jq empty 2>/dev/null; then echo "::warning::Invalid JSON in AI response, using fallback" - echo "Raw output:" - echo "$RAW_OUTPUT" - # Use fallback values when AI response is malformed JSON_RESULT='{"is_valid":true,"issue_type":"other","missing_info":[],"suggestions":[],"overall_assessment":"Unable to validate automatically"}' fi - # Parse JSON fields IS_VALID=$(echo "$JSON_RESULT" | jq -r '.is_valid // false') ISSUE_TYPE=$(echo "$JSON_RESULT" | jq -r '.issue_type // "other"') MISSING_INFO=$(echo "$JSON_RESULT" | jq -r '.missing_info[]? // empty' | sed 's/^/- /') SUGGESTIONS=$(echo "$JSON_RESULT" | jq -r '.suggestions[]? // empty' | sed 's/^/- /') ASSESSMENT=$(echo "$JSON_RESULT" | jq -r '.overall_assessment // "No assessment provided"') - echo "Issue validation result: $IS_VALID" - echo "Issue type: $ISSUE_TYPE" - - # Prepare greeting message for first-time contributors IS_FIRST_TIME="${{ steps.check-first-time.outputs.is_first_time }}" GREETING_SECTION="" if [ "$IS_FIRST_TIME" = "true" ]; then @@ -160,7 +153,6 @@ jobs: fi if [ "$IS_VALID" = "false" ]; then - # Create a comment asking for more information { printf "%b" "$GREETING_SECTION" printf "## 🤖 Issue Validation\n\n" @@ -181,17 +173,9 @@ jobs: printf -- "---\n*This validation was performed automatically to ensure we have all the information needed to help you effectively.*\n" } > comment.md - # Post the comment gh issue comment ${{ github.event.issue.number }} --body-file comment.md - - # Add a label to indicate validation needed gh issue edit ${{ github.event.issue.number }} --add-label "needs-info" - - echo "✅ Validation comment posted and 'needs-info' label added" else - echo "✅ Issue contains sufficient information" - - # Prepare a summary comment even when valid SUGGESTIONS_SECTION="" if [ -n "$SUGGESTIONS" ]; then SUGGESTIONS_SECTION=$(printf "### 💡 Suggestions:\n%s\n\n" "$SUGGESTIONS") @@ -206,10 +190,8 @@ jobs: printf -- "---\n*This validation was performed automatically to help triage issues.*\n" } > comment.md - # Post the summary comment gh issue comment ${{ github.event.issue.number }} --body-file comment.md - # Add appropriate labels based on issue type case "$ISSUE_TYPE" in "bug_report") gh issue edit ${{ github.event.issue.number }} --add-label "bug" @@ -220,6 +202,192 @@ jobs: esac fi + - name: Run opencode analysis + uses: anomalyco/opencode/github@latest + env: + ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }} + with: + model: zai-coding-plan/glm-4.7 + - name: Cleanup + run: rm -f issue_analysis.txt comment.md + + handle-pr: + if: github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 + with: + fetch-depth: 0 + + - name: Check if first-time contributor + id: check-first-time + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} run: | - rm -f issue_analysis.txt comment.md + PR_COUNT=$(gh api "/repos/${{ github.repository }}/pulls?state=all&per_page=100" \ + --jq "[.[] | select(.user.login == \"$PR_AUTHOR\" and .number != ${{ github.event.pull_request.number }})] | length" \ + || echo "0") + + if [ "$PR_COUNT" = "0" ]; then + echo "is_first_time=true" >> $GITHUB_OUTPUT + else + echo "is_first_time=false" >> $GITHUB_OUTPUT + fi + + - name: Get PR diff + id: get-diff + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr diff ${{ github.event.pull_request.number }} > pr_diff.txt + head -c 10000 pr_diff.txt > pr_diff_truncated.txt + + - name: Create PR analysis prompt + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + run: | + { + printf "## Pull Request to Review:\n\n" + printf "**Title:** %s\n\n" "$PR_TITLE" + printf "**Description:**\n%s\n\n" "$PR_BODY" + printf "**Diff:**\n" + cat pr_diff_truncated.txt + } > pr_analysis.txt + + - name: Analyze PR with AI + id: analyze + uses: actions/ai-inference@334892bb203895caaed82ec52d23c1ed9385151e # v2.0.4 + with: + prompt-file: pr_analysis.txt + system-prompt: | + You are a code review assistant for Donut Browser, an open-source anti-detect browser built with Tauri, Next.js, and Rust. + + Review the provided pull request and provide constructive feedback. Focus on: + 1. Code quality and best practices + 2. Potential bugs or issues + 3. Security concerns (especially important for an anti-detect browser) + 4. Performance implications + 5. Consistency with the project's patterns + + Respond ONLY with valid JSON (no markdown fences). + + JSON structure: + { + "summary": "Brief 1-2 sentence summary of what this PR does", + "quality_score": "good"|"needs_work"|"critical_issues", + "feedback": ["feedback point 1", "feedback point 2"], + "suggestions": ["suggestion 1", "suggestion 2"], + "security_notes": ["security note if any"] or [] + } + + IMPORTANT CONSTRAINTS: + - Maximum 4 items in feedback array + - Maximum 3 items in suggestions array + - Maximum 2 items in security_notes array + - Each array item must be under 150 characters + - summary must be under 200 characters + - Be constructive and helpful, not harsh + - Output ONLY the JSON object, nothing else + model: gpt-5 + + - name: Post PR feedback comment + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RESPONSE_FILE: ${{ steps.analyze.outputs.response-file }} + run: | + if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then + RAW_OUTPUT=$(cat "$RESPONSE_FILE") + else + echo "::error::Response file not found" + exit 1 + fi + + JSON_RESULT=$(printf "%s" "$RAW_OUTPUT" | sed -n '/```json/,/```/p' | sed '1d;$d') + if [ -z "$JSON_RESULT" ]; then + JSON_RESULT="$RAW_OUTPUT" + fi + + if ! echo "$JSON_RESULT" | jq empty 2>/dev/null; then + echo "::warning::Invalid JSON in AI response, using fallback" + JSON_RESULT='{"summary":"Unable to analyze automatically","quality_score":"good","feedback":[],"suggestions":[],"security_notes":[]}' + fi + + SUMMARY=$(echo "$JSON_RESULT" | jq -r '.summary // "No summary"') + QUALITY=$(echo "$JSON_RESULT" | jq -r '.quality_score // "good"') + FEEDBACK=$(echo "$JSON_RESULT" | jq -r '.feedback[]? // empty' | sed 's/^/- /') + SUGGESTIONS=$(echo "$JSON_RESULT" | jq -r '.suggestions[]? // empty' | sed 's/^/- /') + SECURITY=$(echo "$JSON_RESULT" | jq -r '.security_notes[]? // empty' | sed 's/^/- ⚠️ /') + + IS_FIRST_TIME="${{ steps.check-first-time.outputs.is_first_time }}" + + { + if [ "$IS_FIRST_TIME" = "true" ]; then + printf "## 👋 Welcome!\n\n" + printf "Thank you for your first contribution ❤️ A human will review your PR shortly. Make sure that the pipelines are green, so that the PR is considered ready for review and could be merged.\n\n" + printf -- "---\n\n" + fi + + printf "## 🤖 PR Review\n\n" + printf "**Summary:** %s\n\n" "$SUMMARY" + + case "$QUALITY" in + "good") + printf "**Status:** ✅ Looking good!\n\n" + ;; + "needs_work") + printf "**Status:** 🔧 Some improvements suggested\n\n" + ;; + "critical_issues") + printf "**Status:** ⚠️ Please address the issues below\n\n" + ;; + esac + + if [ -n "$FEEDBACK" ]; then + printf "### 📝 Feedback:\n%s\n\n" "$FEEDBACK" + fi + + if [ -n "$SUGGESTIONS" ]; then + printf "### 💡 Suggestions:\n%s\n\n" "$SUGGESTIONS" + fi + + if [ -n "$SECURITY" ]; then + printf "### 🔒 Security Notes:\n%s\n\n" "$SECURITY" + fi + + printf -- "---\n*This review was performed automatically. A human maintainer will also review your changes.*\n" + } > comment.md + + gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md + + - name: Run opencode analysis + uses: anomalyco/opencode/github@latest + env: + ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }} + with: + model: zai-coding-plan/glm-4.7 + + - name: Cleanup + run: rm -f pr_diff.txt pr_diff_truncated.txt pr_analysis.txt comment.md + + opencode-command: + if: | + (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && + (contains(github.event.comment.body, ' /oc') || + startsWith(github.event.comment.body, '/oc') || + contains(github.event.comment.body, ' /opencode') || + startsWith(github.event.comment.body, '/opencode')) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #v6.0.1 + + - name: Run opencode + uses: anomalyco/opencode/github@latest + env: + ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }} + with: + model: zai-coding-plan/glm-4.7