diff --git a/home/roles/development/skills/gitea_pr_review.md b/home/roles/development/skills/gitea_pr_review.md new file mode 100644 index 0000000..cda6221 --- /dev/null +++ b/home/roles/development/skills/gitea_pr_review.md @@ -0,0 +1,244 @@ +--- +description: Manage and respond to Gitea/Forgejo PR review comments +--- + +# Gitea PR Review Comments + +This skill enables reading PR review comments and posting inline thread replies on Gitea/Forgejo instances. + +## Prerequisites + +- `tea` CLI configured with a Gitea/Forgejo instance +- Access token from tea config: `~/.config/tea/config.yml` +- Repository must be a Gitea/Forgejo remote (not GitHub) + +## Configuration + +Get the Gitea instance URL and token from tea config: + +```bash +# Get the default login URL and token +yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml +yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml +``` + +Or if you have a specific login name: +```bash +yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .url' ~/.config/tea/config.yml +yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .token' ~/.config/tea/config.yml +``` + +## Commands + +### 1. List PR Review Comments + +Fetch all reviews and their comments for a PR: + +```bash +# Set environment variables +GITEA_URL="https://git.johnogle.info" +TOKEN="" +OWNER="" +REPO="" +PR_NUMBER="" + +# Get all reviews for the PR +curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq + +# Get comments for a specific review +REVIEW_ID="" +curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | jq +``` + +### 2. View All Review Comments (Combined) + +```bash +# Get all reviews and their comments in one view +curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \ + jq -r '.[] | "Review \(.id) by \(.user.login): \(.state)\n Body: \(.body)"' + +# For each review, show inline comments +for REVIEW_ID in $(curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq -r '.[].id'); do + echo "=== Review $REVIEW_ID comments ===" + curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \ + jq -r '.[] | "[\(.path):\(.line)] \(.body)"' +done +``` + +### 3. Reply to Review Comments (Web Endpoint Method) + +The Gitea REST API does not support replying to review comment threads. The web UI uses a different endpoint: + +``` +POST /{owner}/{repo}/pulls/{pr_number}/files/reviews/comments +Content-Type: multipart/form-data +``` + +**Required form fields:** +- `reply`: Review ID to reply to +- `content`: The reply message +- `path`: File path +- `line`: Line number +- `side`: `proposed` or `original` +- `single_review`: `true` +- `origin`: `timeline` +- `_csrf`: CSRF token (required for web endpoint) + +**Authentication Challenge:** +This endpoint requires session-based authentication, not API tokens. Options: + +#### Option A: Use Browser Session (Recommended) + +1. Log in to Gitea in your browser +2. Open browser developer tools and copy cookies +3. Use the session cookies with curl + +```bash +# First, get CSRF token from the PR page +CSRF=$(curl -s -c cookies.txt -b cookies.txt \ + "$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files" | \ + grep -oP 'name="_csrf" value="\K[^"]+') + +# Post the reply +curl -s -b cookies.txt \ + -F "reply=$REVIEW_ID" \ + -F "content=Your reply message here" \ + -F "path=$FILE_PATH" \ + -F "line=$LINE_NUMBER" \ + -F "side=proposed" \ + -F "single_review=true" \ + -F "origin=timeline" \ + -F "_csrf=$CSRF" \ + "$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files/reviews/comments" +``` + +#### Option B: Create Top-Level Comment (Fallback) + +If thread replies are not critical, use the API to create a top-level comment: + +```bash +# Create a top-level comment mentioning the review context +curl -s -X POST \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"body\": \"Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER\n\nYour reply here\"}" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/issues/$PR_NUMBER/comments" +``` + +Or use tea CLI: +```bash +tea comment $PR_NUMBER "Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER + +Your reply here" +``` + +### 4. Submit a New Review + +Create a new review with inline comments: + +```bash +curl -s -X POST \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "body": "Overall review comments", + "event": "COMMENT", + "comments": [ + { + "path": "path/to/file.py", + "body": "Comment on this line", + "new_position": 10 + } + ] + }' \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" +``` + +Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES` + +## Workflow Example + +### Reading and Responding to Reviews + +1. **Set up environment**: + ```bash + export GITEA_URL=$(yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml) + export TOKEN=$(yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml) + export OWNER="johno" + export REPO="nixos-configs" + export PR_NUMBER="5" + ``` + +2. **List all pending review comments**: + ```bash + # Get reviews + curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \ + jq -r '.[] | select(.state == "REQUEST_CHANGES" or .state == "COMMENT") | + "Review \(.id) by \(.user.login) (\(.state)):\n\(.body)\n"' + ``` + +3. **Get detailed comments for a review**: + ```bash + REVIEW_ID="2" + curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \ + jq -r '.[] | "File: \(.path):\(.line)\nComment: \(.body)\nID: \(.id)\n---"' + ``` + +4. **Respond using top-level comment** (most reliable): + ```bash + tea comment $PR_NUMBER "Addressing review feedback: + + - File \`path/to/file.py\` line 10: Fixed the issue by... + - File \`other/file.py\` line 25: Updated as suggested..." + ``` + +## API Reference + +### Endpoints + +| Action | Method | Endpoint | +|--------|--------|----------| +| List reviews | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` | +| Get review | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` | +| Get review comments | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments` | +| Create review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` | +| Submit review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` | +| Delete review | DELETE | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` | +| Create issue comment | POST | `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` | + +### Review States + +- `PENDING` - Draft review not yet submitted +- `COMMENT` - General comment without approval/rejection +- `APPROVE` - Approving the changes +- `REQUEST_CHANGES` - Requesting changes before merge + +## Limitations + +1. **Thread replies**: The Gitea REST API does not support replying directly to review comment threads. This is a known limitation. Workarounds: + - Use top-level comments with context + - Use the web UI manually for thread replies + - Implement session-based authentication to use the web endpoint + +2. **CSRF tokens**: The web endpoint for thread replies requires CSRF tokens, which expire and need to be fetched from the page. + +3. **Session auth**: API tokens work for REST API but not for web endpoints that require session cookies. + +## Tips + +- Always quote file paths and line numbers when responding via top-level comments +- Use `tea pr view $PR_NUMBER --comments` to see all comments +- Use `tea open pulls/$PR_NUMBER` to open the PR in browser for manual thread replies +- Consider using `tea pr approve $PR_NUMBER` after addressing all comments + +## See Also + +- Gitea API Documentation: https://docs.gitea.com/api/1.20/ +- `tea` CLI: https://gitea.com/gitea/tea