@@ -1,103 +1,179 @@
|
||||
# PR Commenter Diff-Aware Action
|
||||
# 🧩 PR Commenter for GitHub & Gitea
|
||||
|
||||
A GitHub/Gitea Action to automatically post comments to pull requests based on a `git diff`. Supports:
|
||||
A composite Action that posts PR comments from **large output files**, such as Terraform/OpenTofu plans, logs, or diffs — directly to **GitHub** or **Gitea** pull requests.
|
||||
|
||||
- Inline comments per changed line
|
||||
- Multiline comment templates
|
||||
- Cross-platform support (GitHub and Gitea)
|
||||
- Diff-aware commenting, automatically detecting added lines
|
||||
- Default values for repository info
|
||||
- Optional `diff` input — can post general PR comments without a diff
|
||||
- Remote URL usage for Gitea Actions
|
||||
It’s based on [`tofu-pr-commenter`](https://github.com/alexnorell/tofu-pr-commenter) but extended for **Gitea compatibility**, **multi-line comment templates**, and **general-purpose content handling** (not just diffs).
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
## 🚀 Features
|
||||
|
||||
- **GitHub & Gitea support**: Works seamlessly on both platforms.
|
||||
- **Diff-aware**: Parses a `git diff` to determine which lines were added.
|
||||
- **Inline comments**: Comments appear directly on changed lines in PRs.
|
||||
- **Multiline templates**: Supports `{line}` for a single line and `{lines}` for all added lines in a file.
|
||||
- **Optional diff**: If no diff is provided, posts a general PR comment.
|
||||
- **Default repository values**: `repo_name`, `repo_owner`, and `api_url` default to GitHub environment variables if not provided.
|
||||
- **Remote URL usage**: Can reference the action directly from a Git repository URL for Gitea.
|
||||
- ✅ Works with **GitHub** and **Gitea** PR APIs
|
||||
- ✅ Handles **large text outputs** (plans, diffs, logs, etc.)
|
||||
- ✅ Allows **multiline comment templates** with placeholders
|
||||
- ✅ Defaults to environment variables from the Action runner (no hardcoded repo info required)
|
||||
- ✅ Can run on **both GitHub Actions** and **Gitea Actions**
|
||||
|
||||
---
|
||||
|
||||
## Inputs
|
||||
## ⚙️ Inputs
|
||||
|
||||
| Input | Description | Required | Default |
|
||||
|-------|-------------|----------|---------|
|
||||
| `platform` | Target platform: `github` or `gitea` | yes | `github` |
|
||||
| `token` | API token for authentication | yes | - |
|
||||
| `api_url` | Gitea API URL (only required for Gitea, e.g., `https://gitea.example.com/api/v1`) | no | `${{ github.api_url }}` |
|
||||
| `repo_owner` | Repository owner (user/org) | no | `${{ github.repository_owner }}` |
|
||||
| `repo_name` | Repository name | no | `${{ github.repository }}` |
|
||||
| `pr_index` | PR number (GitHub) or index (Gitea) | yes | - |
|
||||
| `diff` | Git diff to parse (optional). If empty, posts only a general PR comment. | no | - |
|
||||
| `comment_template` | Multiline template for comment body. Use placeholders `{line}` and `{lines}` | no | `Auto-comment: changed line -> {line}` |
|
||||
| Name | Required | Default | Description |
|
||||
|------|-----------|----------|-------------|
|
||||
| `platform` | ❌ | `github` | Platform type (`github` or `gitea`) |
|
||||
| `token` | ✅ | — | Access token for API requests (`GITHUB_TOKEN` or personal token) |
|
||||
| `pr_index` | ✅ | — | Pull request number or index |
|
||||
| `repo_owner` | ❌ | `${{ github.repository_owner }}` | Repository owner |
|
||||
| `repo_name` | ❌ | `${{ github.repository }}` | Repository name |
|
||||
| `api_url` | ❌ | `${{ github.api_url }}` | API URL for Gitea (required only for Gitea) |
|
||||
| `content` | ❌ | — | Large text input — diff, plan, or log content |
|
||||
| `comment_template` | ❌ | See below | Comment body template supporting `{line}` and `{lines}` placeholders |
|
||||
|
||||
### 🧠 Template Variables
|
||||
|
||||
| Placeholder | Description |
|
||||
|--------------|--------------|
|
||||
| `{line}` | The specific line being commented on (when parsed from diff-style input) |
|
||||
| `{lines}` | The full set of added lines for that file or section |
|
||||
|
||||
---
|
||||
|
||||
## Example Usage
|
||||
|
||||
### GitHub PR
|
||||
## 🧩 Default `comment_template`
|
||||
|
||||
```yaml
|
||||
comment_template: |
|
||||
Auto-comment:
|
||||
---
|
||||
{line}
|
||||
```
|
||||
|
||||
## 💡 Example: GitHub Action Workflow
|
||||
```yaml
|
||||
name: Comment Terraform Plan on PR
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
comment-pr-diff:
|
||||
plan-comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate git diff
|
||||
id: gitdiff
|
||||
- name: Run Terraform Plan
|
||||
id: plan
|
||||
run: |
|
||||
git fetch origin main
|
||||
git diff origin/main > diff.txt
|
||||
terraform plan -no-color > plan.txt
|
||||
echo "plan_text<<EOF" >> $GITHUB_OUTPUT
|
||||
cat plan.txt >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post diff-aware comments to GitHub PR
|
||||
- name: Comment Plan on GitHub PR
|
||||
uses: "https://gitea.example.com/your-org/pr-commenter-action@main"
|
||||
with:
|
||||
platform: github
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pr_index: ${{ github.event.pull_request.number }}
|
||||
diff: ${{ steps.gitdiff.outputs.diff }}
|
||||
content: ${{ steps.plan.outputs.plan_text }}
|
||||
comment_template: |
|
||||
Auto-comment on file:
|
||||
---
|
||||
Changed line: {line}
|
||||
Full added lines in this file:
|
||||
🚀 **Terraform Plan Summary**
|
||||
```
|
||||
{lines}
|
||||
```
|
||||
```
|
||||
|
||||
### Gitea PR
|
||||
|
||||
## 💡 Example: Gitea Action Workflow
|
||||
```yaml
|
||||
name: Comment Plan on PR (Gitea)
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
comment-pr-diff:
|
||||
plan-comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate git diff
|
||||
id: gitdiff
|
||||
- name: Run Tofu Plan
|
||||
id: tofu
|
||||
run: |
|
||||
git fetch origin main
|
||||
git diff origin/main > diff.txt
|
||||
tofu plan -no-color > tofu-plan.txt
|
||||
echo "plan_text<<EOF" >> $GITEA_OUTPUT
|
||||
cat tofu-plan.txt >> $GITEA_OUTPUT
|
||||
echo "EOF" >> $GITEA_OUTPUT
|
||||
|
||||
- name: Post diff-aware comments to Gitea PR
|
||||
- name: Post Plan to Gitea PR
|
||||
uses: "https://gitea.example.com/your-org/pr-commenter-action@main"
|
||||
with:
|
||||
platform: gitea
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
api_url: "https://gitea.example.com/api/v1"
|
||||
pr_index: 42
|
||||
diff: ${{ steps.gitdiff.outputs.diff }}
|
||||
pr_index: ${{ gitea.event.pull_request.number }}
|
||||
content: ${{ steps.tofu.outputs.plan_text }}
|
||||
comment_template: |
|
||||
Auto-comment on file:
|
||||
---
|
||||
Changed line: {line}
|
||||
Full added lines in this file:
|
||||
🧠 **OpenTofu Plan Diff**
|
||||
```
|
||||
{lines}
|
||||
```
|
||||
```
|
||||
```
|
||||
|
||||
## 🪵 Example: Posting Large Log Output
|
||||
```yaml
|
||||
- name: Upload build logs to PR
|
||||
uses: "https://gitea.example.com/your-org/pr-commenter-action@main"
|
||||
with:
|
||||
platform: gitea
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
api_url: "https://gitea.example.com/api/v1"
|
||||
pr_index: 42
|
||||
content: ${{ steps.build.outputs.log }}
|
||||
comment_template: |
|
||||
🧾 **Build Log Summary**
|
||||
```
|
||||
{lines}
|
||||
```
|
||||
```
|
||||
|
||||
## 🧰 Local Development
|
||||
|
||||
You can test the script locally by exporting the necessary environment variables and running:
|
||||
|
||||
```
|
||||
export PLATFORM=gitea
|
||||
export TOKEN=<your_token>
|
||||
export REPO_OWNER=your-org
|
||||
export REPO_NAME=your-repo
|
||||
export PR_INDEX=42
|
||||
export API_URL=https://gitea.example.com/api/v1
|
||||
export CONTENT="$(cat plan.txt)"
|
||||
python3 comment_pr.py
|
||||
```
|
||||
|
||||
## ⚠️ Limitations & Tips for Large Files
|
||||
|
||||
1. GitHub & Gitea Comment Limits
|
||||
- GitHub: max ~65,536 characters per comment.
|
||||
- Gitea: may vary depending on server configuration.
|
||||
|
||||
2. Chunk Large Content
|
||||
- For extremely long plans/logs, split content into smaller chunks and post multiple comments.
|
||||
- Example using shell `split`:
|
||||
|
||||
```bash
|
||||
split -l 5000 plan.txt plan_chunk_
|
||||
for file in plan_chunk_*; do
|
||||
CONTENT=$(cat "$file")
|
||||
python3 comment_pr.py ...
|
||||
done
|
||||
```
|
||||
|
||||
3. Diff Parsing
|
||||
- If your content is a diff, {line} and {lines} placeholders work.
|
||||
- Otherwise, the action will post the entire content under {lines}.
|
||||
|
||||
4. Avoid Passing Huge Strings via Env Variables
|
||||
- Always prefer writing output to a file and reading it in the script.
|
||||
+43
-54
@@ -1,64 +1,53 @@
|
||||
name: "PR Commenter Diff-Aware"
|
||||
description: "Post comments to changed lines in a PR based on a git diff, for GitHub or Gitea. Supports multiline templates."
|
||||
author: "Trez.One"
|
||||
|
||||
branding:
|
||||
icon: "paperclip"
|
||||
color: "blue"
|
||||
name: "PR Commenter for GitHub and Gitea"
|
||||
description: "Posts PR comments from large outputs like diffs, logs, or Terraform plans."
|
||||
author: "Your Name"
|
||||
inputs:
|
||||
platform:
|
||||
description: "Platform type: github or gitea"
|
||||
required: false
|
||||
default: "github"
|
||||
token:
|
||||
description: "Access token for the API"
|
||||
required: true
|
||||
pr_index:
|
||||
description: "Pull request number or index"
|
||||
required: true
|
||||
repo_owner:
|
||||
description: "Repository owner (default: GITHUB_REPOSITORY_OWNER)"
|
||||
required: false
|
||||
repo_name:
|
||||
description: "Repository name (default: GITHUB_REPOSITORY)"
|
||||
required: false
|
||||
api_url:
|
||||
description: "API URL for Gitea (default: GITHUB_API_URL)"
|
||||
required: false
|
||||
content:
|
||||
description: "Large text content (diff, log, plan, etc.)"
|
||||
required: false
|
||||
comment_template:
|
||||
description: "Template for the comment body (supports multiline and placeholders {line}, {lines})"
|
||||
required: false
|
||||
default: |
|
||||
Auto-comment:
|
||||
---
|
||||
{line}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
- name: Install Python dependencies
|
||||
run: pip install requests
|
||||
shell: bash
|
||||
|
||||
- name: Run PR commenter
|
||||
run: python comment_pr_diff.py
|
||||
shell: python
|
||||
- name: Run PR Commenter
|
||||
run: python3 ${{ github.action_path }}/comment_pr.py
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
TOKEN: ${{ inputs.token }}
|
||||
API_URL: ${{ inputs.api_url || github.api_url }}
|
||||
REPO_OWNER: ${{ inputs.repo_owner || github.repository_owner }}
|
||||
REPO_NAME: ${{ inputs.repo_name || github.repository }}
|
||||
PR_INDEX: ${{ inputs.pr_index }}
|
||||
DIFF: ${{ inputs.diff }}
|
||||
COMMENT_TEMPLATE: ${{ inputs.comment_template }}
|
||||
|
||||
inputs:
|
||||
platform:
|
||||
description: "Target platform: github or gitea"
|
||||
required: true
|
||||
default: "github"
|
||||
token:
|
||||
description: "API token for authentication"
|
||||
required: true
|
||||
api_url:
|
||||
description: "Gitea API URL (only required for Gitea, e.g., https://gitea.example.com/api/v1)"
|
||||
required: false
|
||||
repo_owner:
|
||||
description: "Repository owner (user/org)"
|
||||
required: false
|
||||
repo_name:
|
||||
description: "Repository name"
|
||||
required: false
|
||||
pr_index:
|
||||
description: "PR number (GitHub) or index (Gitea)"
|
||||
required: true
|
||||
diff:
|
||||
description: "Git diff to parse (optional). If empty, posts only a general PR comment."
|
||||
required: false
|
||||
comment_template:
|
||||
description: |
|
||||
Multiline template for comment body.
|
||||
Use placeholders:
|
||||
- {line} → replaced by single changed line
|
||||
- {lines} → replaced by all added lines in the file
|
||||
required: false
|
||||
default: "Auto-comment: changed line -> {line}"
|
||||
REPO_OWNER: ${{ inputs.repo_owner }}
|
||||
REPO_NAME: ${{ inputs.repo_name }}
|
||||
API_URL: ${{ inputs.api_url }}
|
||||
CONTENT: ${{ inputs.content }}
|
||||
COMMENT_TEMPLATE: ${{ inputs.comment_template }}
|
||||
+5
-5
@@ -11,15 +11,15 @@ owner = os.environ.get("REPO_OWNER", os.environ.get("GITHUB_REPOSITORY_OWNER"))
|
||||
repo = os.environ.get("REPO_NAME", os.environ.get("GITHUB_REPOSITORY"))
|
||||
pr_index = os.environ["PR_INDEX"]
|
||||
api_url = os.environ.get("API_URL", os.environ.get("GITHUB_API_URL"))
|
||||
diff_text = os.environ.get("DIFF") # optional
|
||||
content_text = os.environ.get("CONTENT") # optional large text input
|
||||
comment_template = os.environ.get("COMMENT_TEMPLATE", "Auto-comment: changed line -> {line}")
|
||||
|
||||
if not token or not pr_index:
|
||||
print("TOKEN and PR_INDEX are required.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# If diff_text is empty, just post a general PR comment
|
||||
if not diff_text:
|
||||
# If no content provided, just post a general PR comment
|
||||
if not content_text:
|
||||
body = comment_template.replace("{line}", "").replace("{lines}", "")
|
||||
if platform == "github":
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/issues/{pr_index}/comments"
|
||||
@@ -44,12 +44,12 @@ if not diff_text:
|
||||
print(resp.text, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# --- If diff_text exists, parse diff normally ---
|
||||
# --- If content_text exists, parse it for added lines like a diff ---
|
||||
diff_files = {}
|
||||
current_file = None
|
||||
new_line_num = None
|
||||
|
||||
for line in diff_text.splitlines():
|
||||
for line in content_text.splitlines():
|
||||
if line.startswith("+++ b/"):
|
||||
current_file = line[6:].strip()
|
||||
diff_files[current_file] = []
|
||||
|
||||
Reference in New Issue
Block a user