name: Ampcode PR Review on: pull_request: types: [opened, ready_for_review, synchronize] branches: [main, staging] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read pull-requests: write issues: write jobs: ampcode-review: if: github.event.pull_request.draft == false timeout-minutes: 15 runs-on: ubuntu-latest strategy: matrix: review-chunk: [1] max-parallel: 3 steps: - name: Checkout PR branch uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' - name: Install Ampcode CLI run: | npm install -g @sourcegraph/amp amp --version - name: Setup pnpm uses: pnpm/action-setup@v4 - name: Install project dependencies run: pnpm install --frozen-lockfile - name: Get changed files id: changed-files run: | git fetch origin ${{ github.event.pull_request.base.ref }} CHANGED_FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}..HEAD | grep -E '\.(ts|tsx|js|jsx|py|go|java|cpp|c|h|hpp|rs|rb|php|cs|swift|kt|scala|clj|hs|ml|fs|elm|dart|lua|r|sql|sh|bash|zsh|fish|ps1|bat|cmd|yaml|yml|json|toml|ini|cfg|conf|xml|html|css|scss|sass|less|styl|vue|svelte|astro|md|mdx|tex|latex|bib|org|rst|adoc|asciidoc|wiki|txt)$' | head -50 || echo "") echo "changed_files<> $GITHUB_OUTPUT echo "$CHANGED_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT echo "Changed files count: $(echo "$CHANGED_FILES" | wc -l)" - name: Add Review Started Reaction if: steps.changed-files.outputs.changed_files != '' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Add a reaction to indicate review has started curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/json" \ -d '{"content":"eyes"}' \ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/reactions" echo "Added 👀 reaction to indicate review has started" - name: Run Ampcode Review if: steps.changed-files.outputs.changed_files != '' env: AMP_API_KEY: ${{ secrets.AMPCODE_API_KEY }} run: | echo "Running ampcode review on changed files..." # Create a temporary file with the changed files list echo "${{ steps.changed-files.outputs.changed_files }}" > changed_files.txt # Run ampcode review on the changed files with specific instructions for line-level feedback REVIEW_OUTPUT=$(amp -x "Review the following files for code quality, potential bugs, security issues, performance concerns, and best practices. For each issue found, provide the specific file path and line number where the issue occurs. Format your response as: FILE:line_number:issue_description. Focus on providing specific, actionable feedback with exact line numbers. Files to review: $(cat changed_files.txt | tr '\n' ' ')" 2>&1 || echo "Ampcode review failed") # Save review output to file echo "$REVIEW_OUTPUT" > ampcode_review.txt # Display review output for debugging echo "=== Ampcode Review Output ===" cat ampcode_review.txt echo "=== End Review Output ===" - name: Parse and Post Line-Specific Comments if: steps.changed-files.outputs.changed_files != '' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Check if review output exists and is not empty if [ ! -f ampcode_review.txt ] || [ ! -s ampcode_review.txt ]; then echo "No review output found or file is empty" exit 0 fi # Read the review output REVIEW_CONTENT=$(cat ampcode_review.txt) # Parse the review content for line-specific comments # Look for patterns like "file.ts:123:issue description" echo "$REVIEW_CONTENT" | grep -E '^[^:]+:[0-9]+:' > line_comments.txt || true # If no line-specific comments found, create a general comment if [ ! -s line_comments.txt ]; then echo "No line-specific comments found, creating general review comment" # Create a general PR review comment jq -n \ --arg content "$REVIEW_CONTENT" \ --arg run_id "${{ github.run_id }}" \ --arg repo "${{ github.repository }}" \ '{ "body": ("## 🤖 Automated Code Review by Ampcode\n\n**Review Summary:**\n\nI'\''ve analyzed the changes in this PR using AI-powered code review. Here are my findings:\n\n### 📋 Review Results\n\n```\n" + $content + "\n```\n\n### 🔍 Key Areas Reviewed\n- Code quality and best practices\n- Potential bugs and security issues\n- Performance considerations\n- Maintainability and readability\n\n### 📝 Notes\n- This is an automated review generated by Ampcode AI\n- Please review the suggestions and apply them as appropriate\n- For questions about specific recommendations, feel free to ask!\n\n---\n*Generated by [Ampcode](https://ampcode.com) • [View Workflow](https://github.com/" + $repo + "/actions/runs/" + $run_id + ")*"), "event": "COMMENT" }' > review_comment.json # Post the general review comment curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/json" \ -d @review_comment.json \ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" echo "General review comment posted successfully" exit 0 fi # Process line-specific comments echo "Processing line-specific comments..." # Initialize arrays for the review declare -a comments=() comment_count=0 # Read line comments and create GitHub review comments while IFS= read -r line; do # Parse the line: file:line:comment file_path=$(echo "$line" | cut -d: -f1) line_number=$(echo "$line" | cut -d: -f2) comment_text=$(echo "$line" | cut -d: -f3-) # Skip if file doesn't exist or line number is invalid if [ ! -f "$file_path" ] || ! [[ "$line_number" =~ ^[0-9]+$ ]]; then echo "Skipping invalid comment: $line" continue fi # Create the comment object comment_obj=$(jq -n \ --arg path "$file_path" \ --arg line "$line_number" \ --arg body "🤖 **Ampcode Review:** $comment_text" \ '{ "path": $path, "line": ($line | tonumber), "body": $body }') comments+=("$comment_obj") comment_count=$((comment_count + 1)) echo "Added comment for $file_path:$line_number" done < line_comments.txt # If we have comments, create the review if [ $comment_count -gt 0 ]; then # Create the review payload jq -n \ --arg body "## 🤖 Automated Code Review by Ampcode\n\nI've reviewed the changes and found $comment_count issue(s) that need attention. Please review the inline comments below.\n\n---\n*Generated by [Ampcode](https://ampcode.com) • [View Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*" \ --arg event "REQUEST_CHANGES" \ --argjson comments "$(printf '%s\n' "${comments[@]}" | jq -s .)" \ '{ "body": $body, "event": $event, "comments": $comments }' > review_payload.json # Post the review with line-specific comments curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/json" \ -d @review_payload.json \ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" echo "Posted review with $comment_count line-specific comments" else echo "No valid line-specific comments to post" fi - name: Add Review Completed Reaction if: always() && steps.changed-files.outputs.changed_files != '' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Add a reaction to indicate review has completed curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/json" \ -d '{"content":"white_check_mark"}' \ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/reactions" echo "Added ✅ reaction to indicate review has completed" - name: Cleanup if: always() run: | # Clean up temporary files rm -f changed_files.txt ampcode_review.txt review_comment.json review_payload.json line_comments.txt echo "Cleanup completed" - name: Summary if: always() run: | echo "=== Ampcode PR Review Summary ===" echo "PR Number: ${{ github.event.pull_request.number }}" echo "PR Title: ${{ github.event.pull_request.title }}" echo "Base Branch: ${{ github.event.pull_request.base.ref }}" echo "Head Branch: ${{ github.event.pull_request.head.ref }}" echo "Changed Files: $(echo '${{ steps.changed-files.outputs.changed_files }}' | wc -l)" echo "Review Status: $([ -f ampcode_review.txt ] && echo 'Completed' || echo 'Skipped')" echo "=== End Summary ==="