Skip to content

Commit 230463e

Browse files
committed
feat: add pr-auto-comments, written with Claude 3.7 Sonnet
1 parent 2b10168 commit 230463e

File tree

3 files changed

+356
-2
lines changed

3 files changed

+356
-2
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
name: PR Automated Comments
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
org_name:
7+
description: 'GitHub organization name to identify internal team members'
8+
required: true
9+
type: string
10+
additional_internal_users:
11+
description: 'Additional comma-separated list of usernames to consider as internal'
12+
required: false
13+
type: string
14+
default: ''
15+
first_pr_comment:
16+
description: 'Message for first PR comments (leave empty to disable first PR comments)'
17+
required: false
18+
type: string
19+
default: ''
20+
ready_for_review_comment:
21+
description: 'Message for PR ready for review comments (leave empty to disable ready for review comments)'
22+
required: false
23+
type: string
24+
default: ''
25+
merged_pr_comment:
26+
description: 'Message for merged PR comments (leave empty to disable merged PR comments)'
27+
required: false
28+
type: string
29+
default: ''
30+
secrets:
31+
token:
32+
description: 'GitHub token with org:read permission'
33+
required: false
34+
35+
jobs:
36+
# Central job to check if contributor is external and determine PR status
37+
check-contributor:
38+
name: Check Contributor Status
39+
runs-on: ubuntu-latest
40+
outputs:
41+
is_external: ${{ steps.check.outputs.is_external }}
42+
is_first_pr: ${{ steps.check.outputs.is_first_pr }}
43+
event_type: ${{ steps.event.outputs.type }}
44+
steps:
45+
- name: Determine event type
46+
id: event
47+
run: |
48+
if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
49+
if [[ "${{ github.event.action }}" == "opened" ]]; then
50+
echo "type=opened" >> $GITHUB_OUTPUT
51+
elif [[ "${{ github.event.action }}" == "ready_for_review" ]]; then
52+
echo "type=ready_for_review" >> $GITHUB_OUTPUT
53+
elif [[ "${{ github.event.action }}" == "closed" && "${{ github.event.pull_request.merged }}" == "true" ]]; then
54+
echo "type=merged" >> $GITHUB_OUTPUT
55+
else
56+
echo "type=other" >> $GITHUB_OUTPUT
57+
fi
58+
else
59+
echo "type=other" >> $GITHUB_OUTPUT
60+
fi
61+
62+
- name: Check if external contributor
63+
id: check
64+
run: |
65+
# Get the PR author
66+
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
67+
ORG_NAME="${{ inputs.org_name }}"
68+
ADDITIONAL_USERS="${{ inputs.additional_internal_users }}"
69+
70+
# First check if user is in the additional internal users list
71+
if [[ "$ADDITIONAL_USERS" == *"$PR_AUTHOR"* ]]; then
72+
echo "User is in additional internal users list, skipping comment"
73+
echo "is_external=false" >> $GITHUB_OUTPUT
74+
echo "is_first_pr=false" >> $GITHUB_OUTPUT
75+
exit 0
76+
fi
77+
78+
# Check if user is an org member
79+
echo "Checking if user is a member of organization $ORG_NAME"
80+
ORG_MEMBER=$(gh api -H "Accept: application/vnd.github+json" \
81+
"/orgs/$ORG_NAME/members/$PR_AUTHOR" --silent || echo "not_found")
82+
83+
if [[ "$ORG_MEMBER" != "not_found" ]]; then
84+
echo "User is an organization member, skipping comment"
85+
echo "is_external=false" >> $GITHUB_OUTPUT
86+
echo "is_first_pr=false" >> $GITHUB_OUTPUT
87+
exit 0
88+
fi
89+
90+
# User is external
91+
echo "is_external=true" >> $GITHUB_OUTPUT
92+
93+
# Only check if this is their first PR if needed
94+
if [[ "${{ steps.event.outputs.type }}" == "opened" ]]; then
95+
# Check if this is their first PR to the repo
96+
PR_COUNT=$(gh api graphql -f query='
97+
query($owner:String!, $repo:String!, $author:String!) {
98+
repository(owner:$owner, name:$repo) {
99+
pullRequests(first:100, states:[OPEN, CLOSED, MERGED], author:$author) {
100+
totalCount
101+
}
102+
}
103+
}' -f owner=${{ github.repository_owner }} -f repo=${{ github.event.repository.name }} -f author=$PR_AUTHOR | jq '.data.repository.pullRequests.totalCount')
104+
105+
if [ "$PR_COUNT" -eq 1 ]; then
106+
echo "First PR from this contributor"
107+
echo "is_first_pr=true" >> $GITHUB_OUTPUT
108+
else
109+
echo "Not the first PR from this contributor"
110+
echo "is_first_pr=false" >> $GITHUB_OUTPUT
111+
fi
112+
else
113+
echo "is_first_pr=false" >> $GITHUB_OUTPUT
114+
fi
115+
env:
116+
GH_TOKEN: ${{ secrets.token || github.token }}
117+
118+
# Leave a comment on first PRs
119+
first-pr-comment:
120+
name: First PR Comment
121+
needs: check-contributor
122+
if: needs.check-contributor.outputs.event_type == 'opened' && needs.check-contributor.outputs.is_external == 'true' && needs.check-contributor.outputs.is_first_pr == 'true' && inputs.first_pr_comment != ''
123+
runs-on: ubuntu-latest
124+
steps:
125+
- name: Leave first PR comment
126+
uses: actions/github-script@v6
127+
with:
128+
github-token: ${{ github.token }}
129+
script: |
130+
github.rest.issues.createComment({
131+
issue_number: context.issue.number,
132+
owner: context.repo.owner,
133+
repo: context.repo.repo,
134+
body: `${{ inputs.first_pr_comment }}`
135+
});
136+
137+
# Leave a comment when PR is marked ready for review
138+
ready-for-review-comment:
139+
name: Ready for Review Comment
140+
needs: check-contributor
141+
if: needs.check-contributor.outputs.event_type == 'ready_for_review' && needs.check-contributor.outputs.is_external == 'true' && inputs.ready_for_review_comment != ''
142+
runs-on: ubuntu-latest
143+
steps:
144+
- name: Leave ready for review comment
145+
uses: actions/github-script@v6
146+
with:
147+
github-token: ${{ github.token }}
148+
script: |
149+
github.rest.issues.createComment({
150+
issue_number: context.issue.number,
151+
owner: context.repo.owner,
152+
repo: context.repo.repo,
153+
body: `${{ inputs.ready_for_review_comment }}`
154+
});
155+
156+
# Leave a comment when PR is merged
157+
merged-pr-comment:
158+
name: Merged PR Comment
159+
needs: check-contributor
160+
if: needs.check-contributor.outputs.event_type == 'merged' && needs.check-contributor.outputs.is_external == 'true' && inputs.merged_pr_comment != ''
161+
runs-on: ubuntu-latest
162+
steps:
163+
- name: Leave merged PR comment
164+
uses: actions/github-script@v6
165+
with:
166+
github-token: ${{ github.token }}
167+
script: |
168+
github.rest.issues.createComment({
169+
issue_number: context.issue.number,
170+
owner: context.repo.owner,
171+
repo: context.repo.repo,
172+
body: `${{ inputs.merged_pr_comment }}`
173+
});

README.md

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,120 @@
1-
# auto-comments
2-
Reusable workflow for automated comments on Pull Requests in Github
1+
# Automated PR Comments for GitHub Actions
2+
3+
This reusable workflow adds automated comments on Pull Requests from external contributors. It identifies external contributors as users who are not members of your GitHub organization.
4+
5+
## Features
6+
7+
The workflow can leave comments in these situations:
8+
9+
- On a contributor's **first** Pull Request to your repository
10+
- When a Pull Request is marked as **ready for review**
11+
- When a Pull Request is **merged**
12+
13+
Each comment type can be individually enabled or disabled.
14+
15+
## Setup
16+
17+
To use this workflow in your repository, create a new workflow file in your `.github/workflows` directory:
18+
19+
```yaml
20+
name: PR Comments
21+
22+
on:
23+
pull_request_target:
24+
types: [opened, ready_for_review, closed]
25+
26+
jobs:
27+
pr-comments:
28+
uses: your-org/auto-comments/.github/workflows/pr-auto-comments.yml@main
29+
with:
30+
org_name: "your-organization-name"
31+
# Enable only the comment types you want by providing content
32+
first_pr_comment: |
33+
# Welcome to our project!
34+
35+
Thanks for your first contribution. We're glad you're here.
36+
# To disable a comment type, either remove it or leave it empty
37+
merged_pr_comment: |
38+
# PR Merged
39+
40+
Thank you for your contribution!
41+
secrets:
42+
token: ${{ secrets.GITHUB_TOKEN }}
43+
```
44+
45+
## Configuration
46+
47+
### Required Inputs
48+
49+
| Input | Description |
50+
|-------|-------------|
51+
| `org_name` | GitHub organization name to identify internal team members |
52+
53+
### Optional Inputs
54+
55+
| Input | Description | Default | Behavior if not provided |
56+
|-------|-------------|---------|--------------------------|
57+
| `additional_internal_users` | Additional comma-separated list of usernames to consider as internal | `''` | No additional users excluded |
58+
| `first_pr_comment` | Message for first PR comments | `''` | First PR comments disabled |
59+
| `ready_for_review_comment` | Message for ready for review comments | `''` | Ready for review comments disabled |
60+
| `merged_pr_comment` | Message for merged PR comments | `''` | Merged PR comments disabled |
61+
62+
### Secrets
63+
64+
| Secret | Description | Required | Default |
65+
|--------|-------------|----------|---------|
66+
| `token` | GitHub token with org:read permission | No | `github.token` |
67+
68+
## Enabling and Disabling Comment Types
69+
70+
You can selectively enable comment types by providing content for the corresponding input:
71+
72+
- To **enable** a comment type: provide the message text
73+
- To **disable** a comment type: omit the input or provide an empty string
74+
75+
For example, to enable only first PR and merged PR comments:
76+
77+
```yaml
78+
first_pr_comment: |
79+
# Welcome!
80+
Thanks for your contribution.
81+
# ready_for_review_comment is omitted to disable it
82+
merged_pr_comment: |
83+
# Merged
84+
Your PR has been merged.
85+
```
86+
87+
## Comment Formatting
88+
89+
You can use full Markdown syntax in your comment messages, including:
90+
91+
- Headings (`# Heading`)
92+
- Lists (`- Item`)
93+
- Formatting (**bold**, *italic*)
94+
- Links (`[text](url)`)
95+
- Code blocks
96+
- Emojis (`:tada:`)
97+
98+
## How It Works
99+
100+
1. The workflow first checks the PR author in a central job:
101+
- Determines if they are an external contributor (not in your org or additional users list)
102+
- For first PR comments, checks if this is their first contribution
103+
104+
2. Based on the event type (opened/ready for review/merged) and author status:
105+
- Runs only the appropriate comment job
106+
- Only if the corresponding comment text is provided
107+
108+
3. Each enabled job posts its specific comment to the PR
109+
110+
This architecture ensures contributor checks run only once per workflow execution, making the process more efficient.
111+
112+
## Security Considerations
113+
114+
This workflow uses `pull_request_target` to ensure it has the necessary permissions to comment on PRs. Since this event has access to secrets, the workflow is designed to only perform safe operations (commenting) and does not check out code from external PRs.
115+
116+
The workflow requires a token with `org:read` permission to check organization membership.
117+
118+
## License
119+
120+
MIT

example-usage.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: PR Comments
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, ready_for_review, closed]
6+
7+
jobs:
8+
# Example 1: All comment types enabled
9+
pr-comments-all:
10+
uses: your-org/auto-comments/.github/workflows/pr-auto-comments.yml@main
11+
with:
12+
org_name: "your-organization-name"
13+
additional_internal_users: "external-consultant,bot-account"
14+
15+
# First PR comment
16+
first_pr_comment: |
17+
# 🌟 Welcome to your first contribution!
18+
19+
We're thrilled to see your first PR to our project. Here at [Organization], we value every contribution.
20+
21+
## Next steps
22+
- Our team will review your changes soon
23+
- You might get some feedback or requests for changes
24+
- Feel free to ask questions if anything is unclear
25+
26+
Thank you for taking the time to contribute to our project!
27+
28+
# Ready for review comment
29+
ready_for_review_comment: |
30+
# Ready for review - thank you!
31+
32+
Your PR has been marked as ready for review and will be reviewed by our team soon.
33+
34+
Please allow 2-3 business days for the review process.
35+
36+
# Merged PR comment
37+
merged_pr_comment: |
38+
# Congratulations! 🎉
39+
40+
Your PR has been merged successfully. Thank you for your valuable contribution!
41+
42+
Your changes will be included in the next release of our software.
43+
secrets:
44+
token: ${{ secrets.GITHUB_TOKEN }}
45+
46+
# Example 2: Only specific comment types enabled
47+
pr-comments-selective:
48+
uses: your-org/auto-comments/.github/workflows/pr-auto-comments.yml@main
49+
with:
50+
org_name: "your-organization-name"
51+
52+
# Only enable first PR and merged PR comments
53+
first_pr_comment: |
54+
# Welcome to our project!
55+
Thank you for your first contribution!
56+
57+
# Ready for review comments disabled by not providing this input
58+
59+
merged_pr_comment: |
60+
# PR Merged
61+
Thank you for your contribution!
62+
secrets:
63+
token: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)