Skip to content

Commit f6140d4

Browse files
committed
feat: initial release of replace-comment GitHub Action
Composite action to find, delete, and recreate GitHub issue/PR comments to move them to the bottom of conversations for better visibility. Features: - Find comments using flexible search criteria (body-includes, body-regex, author) - Delete existing comments automatically using GitHub REST API - Create fresh comments that appear at bottom of conversation - Perfect for CI/CD status updates and bot notifications - Built as composite action leveraging peter-evans/find-comment and peter-evans/create-or-update-comment - Comprehensive documentation with examples and migration guide - GitHub workflows for testing and releases Resolves the common issue where updated comments stay in their original chronological position instead of being easily visible at the bottom.
0 parents  commit f6140d4

7 files changed

Lines changed: 484 additions & 0 deletions

File tree

.github/workflows/example.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: 'Example: E2E Test Results'
2+
on:
3+
pull_request:
4+
types: [opened, synchronize]
5+
6+
permissions:
7+
pull-requests: write
8+
9+
jobs:
10+
e2e-tests:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Run E2E Tests
17+
id: tests
18+
run: |
19+
echo "Running E2E tests..."
20+
sleep 2
21+
echo "status=✅ All tests passed!" >> $GITHUB_OUTPUT
22+
echo "total=42" >> $GITHUB_OUTPUT
23+
echo "duration=3m 24s" >> $GITHUB_OUTPUT
24+
25+
- name: Update E2E Results Comment
26+
uses: ./
27+
with:
28+
issue-number: ${{ github.event.pull_request.number }}
29+
body-includes: '<!-- e2e-test-results -->'
30+
comment-author: 'github-actions[bot]'
31+
body: |
32+
<!-- e2e-test-results -->
33+
## 🧪 E2E Test Results
34+
${{ steps.tests.outputs.status }}
35+
- **Total tests:** ${{ steps.tests.outputs.total }}
36+
- **Duration:** ${{ steps.tests.outputs.duration }}
37+
- **Browser:** Chrome 120
38+
- **Commit:** ${{ github.sha }}
39+
- **Workflow:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
40+
*This comment will always appear at the bottom for easy visibility! 🚀*
41+
reactions: 'rocket,eyes'

.github/workflows/release.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: 'Release'
2+
on:
3+
push:
4+
tags:
5+
- 'v*'
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Create Release
18+
uses: softprops/action-gh-release@v2
19+
with:
20+
name: Release ${{ github.ref_name }}
21+
body: |
22+
## 🚀 Replace Comment Action v${{ github.ref_name }}
23+
24+
Find, delete, and recreate GitHub comments to move them to the bottom of conversations.
25+
26+
### Features
27+
- ✅ **Find comments** using flexible search criteria
28+
- 🗑️ **Delete existing comments** automatically
29+
- ✨ **Create fresh comments** at the bottom for visibility
30+
- 🔧 **Perfect for CI/CD** status updates and bot notifications
31+
32+
### Usage
33+
```yaml
34+
- uses: johanwulf/replace-comment@${{ github.ref_name }}
35+
with:
36+
issue-number: ${{ github.event.pull_request.number }}
37+
body-includes: '<!-- my-comment -->'
38+
body: |
39+
<!-- my-comment -->
40+
Your comment content here
41+
```
42+
draft: false
43+
prerelease: false

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# IDE
2+
.vscode/
3+
.idea/
4+
*.swp
5+
*.swo
6+
7+
# OS
8+
.DS_Store
9+
Thumbs.db
10+
11+
# Logs
12+
*.log
13+
14+
# Temporary files
15+
*.tmp
16+
*.temp
17+
18+
# Environment variables
19+
.env
20+
.env.local
21+
.env.*.local

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Johan Wulf
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# replace-comment
2+
3+
A GitHub Action that finds, deletes, and recreates a comment to move it to the bottom of the conversation. This is perfect for CI/CD status updates, bot notifications, and any content you want to keep highly visible.
4+
5+
## Why Replace Instead of Update?
6+
7+
When you **update** a comment, it stays in its original chronological position. When you **delete and recreate**, the new comment appears at the bottom of the conversation - much more visible!
8+
9+
**Perfect for:**
10+
- ✅ CI/CD test results
11+
- ✅ Build status updates
12+
- ✅ Bot notifications
13+
- ✅ Status reports
14+
- ✅ Any content that should stay at the bottom for visibility
15+
16+
## Usage
17+
18+
### Basic Example
19+
20+
```yaml
21+
- name: Replace E2E Test Results Comment
22+
uses: johanwulf/replace-comment@v1
23+
with:
24+
issue-number: ${{ github.event.pull_request.number }}
25+
body-includes: '<!-- e2e-test-results -->'
26+
comment-author: 'github-actions[bot]'
27+
body: |
28+
<!-- e2e-test-results -->
29+
## 🧪 E2E Test Results
30+
31+
✅ All tests passed!
32+
33+
- **Total tests:** 42
34+
- **Duration:** 3m 24s
35+
- **Browser:** Chrome 120
36+
```
37+
38+
### Advanced Example with File Body
39+
40+
```yaml
41+
- name: Replace Build Report
42+
uses: johanwulf/replace-comment@v1
43+
with:
44+
token: ${{ secrets.GITHUB_TOKEN }}
45+
repository: ${{ github.repository }}
46+
issue-number: ${{ github.event.pull_request.number }}
47+
body-regex: '<!-- build-report-\\d+ -->'
48+
comment-author: 'build-bot[bot]'
49+
body-path: './build-report.md'
50+
reactions: 'rocket,eyes'
51+
```
52+
53+
## Inputs
54+
55+
| Name | Description | Required | Default |
56+
|------|-------------|----------|---------|
57+
| `token` | GITHUB_TOKEN or a repo scoped PAT | No | `${{ github.token }}` |
58+
| `repository` | The full name of the repository | No | `${{ github.repository }}` |
59+
| `issue-number` | The number of the issue or pull request | **Yes** | |
60+
| `body-includes` | A string to search for in comment bodies | No* | |
61+
| `body-regex` | A regular expression to search for in comment bodies | No* | |
62+
| `comment-author` | The GitHub username of the comment author | No | |
63+
| `direction` | Search direction: `first` or `last` | No | `first` |
64+
| `nth` | 0-indexed number specifying which comment to replace if multiple found | No | `0` |
65+
| `body` | The comment body content | No** | |
66+
| `body-path` | Path to a file containing the comment body | No** | |
67+
| `reactions` | Comma-separated reactions to add (+1, -1, laugh, confused, heart, hooray, rocket, eyes) | No | |
68+
69+
\* Either `body-includes` or `body-regex` must be provided
70+
\** Either `body` or `body-path` must be provided
71+
72+
## Outputs
73+
74+
| Name | Description |
75+
|------|-------------|
76+
| `comment-id` | The ID of the created comment |
77+
| `comment-url` | The URL of the created comment |
78+
| `comment-body` | The body of the created comment |
79+
| `found-comment-id` | The ID of the comment that was found and deleted (empty if none found) |
80+
81+
## How It Works
82+
83+
1. **🔍 Find**: Uses [peter-evans/find-comment](https://github.com/peter-evans/find-comment) to search for existing comments
84+
2. **🗑️ Delete**: Removes the found comment using GitHub's REST API (if any)
85+
3. **✨ Create**: Uses [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) to create a new comment at the bottom
86+
87+
This action is a **composite action** that combines the excellent work of Peter Evans into a simple "find-delete-create" workflow.
88+
89+
## Examples
90+
91+
### CI/CD Test Results
92+
93+
```yaml
94+
name: Tests
95+
on: [push, pull_request]
96+
97+
jobs:
98+
test:
99+
runs-on: ubuntu-latest
100+
steps:
101+
- uses: actions/checkout@v4
102+
103+
- name: Run tests
104+
id: tests
105+
run: |
106+
# Your test commands here
107+
echo "result=✅ All tests passed!" >> $GITHUB_OUTPUT
108+
109+
- name: Update test results comment
110+
if: github.event_name == 'pull_request'
111+
uses: johanwulf/replace-comment@v1
112+
with:
113+
issue-number: ${{ github.event.pull_request.number }}
114+
body-includes: '<!-- test-results -->'
115+
comment-author: 'github-actions[bot]'
116+
body: |
117+
<!-- test-results -->
118+
## 🧪 Test Results
119+
120+
${{ steps.tests.outputs.result }}
121+
122+
**Commit:** ${{ github.sha }}
123+
**Workflow:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
124+
```
125+
126+
### Deployment Status
127+
128+
```yaml
129+
- name: Update deployment status
130+
uses: johanwulf/replace-comment@v1
131+
with:
132+
issue-number: ${{ github.event.pull_request.number }}
133+
body-regex: '<!-- deploy-status-.*? -->'
134+
body: |
135+
<!-- deploy-status-${{ github.run_id }} -->
136+
## 🚀 Deployment Status
137+
138+
**Environment:** staging
139+
**Status:** ${{ job.status }}
140+
**URL:** https://staging-${{ github.event.pull_request.number }}.example.com
141+
**Deploy time:** $(date)
142+
reactions: 'rocket'
143+
```
144+
145+
### Build Artifacts Report
146+
147+
```yaml
148+
- name: Generate build report
149+
run: |
150+
echo "## 📦 Build Artifacts" > build-report.md
151+
echo "" >> build-report.md
152+
echo "| File | Size |" >> build-report.md
153+
echo "|------|------|" >> build-report.md
154+
ls -la dist/ | awk '{print "| " $9 " | " $5 " |"}' >> build-report.md
155+
156+
- name: Post build report
157+
uses: johanwulf/replace-comment@v1
158+
with:
159+
issue-number: ${{ github.event.pull_request.number }}
160+
body-includes: '<!-- build-artifacts -->'
161+
body-path: './build-report.md'
162+
```
163+
164+
## Tips
165+
166+
### Unique Comment Identification
167+
168+
Use HTML comments to uniquely identify your comments:
169+
170+
```yaml
171+
body: |
172+
<!-- my-unique-identifier -->
173+
Your visible content here
174+
```
175+
176+
### Multiple Bot Comments
177+
178+
Use different identifiers for different types of comments:
179+
180+
```yaml
181+
# Test results
182+
body-includes: '<!-- test-results -->'
183+
184+
# Build status
185+
body-includes: '<!-- build-status -->'
186+
187+
# Deploy status
188+
body-includes: '<!-- deploy-status -->'
189+
```
190+
191+
### Conditional Comments
192+
193+
Only create comments when needed:
194+
195+
```yaml
196+
- name: Replace comment
197+
if: github.event_name == 'pull_request' && failure()
198+
uses: johanwulf/replace-comment@v1
199+
with:
200+
# ... your config
201+
```
202+
203+
## Comparison with Other Actions
204+
205+
| Action | Find | Update | Delete+Create | Use Case |
206+
|--------|------|--------|---------------|----------|
207+
| `peter-evans/create-or-update-comment` | ✅ | ✅ | ❌ | Update existing content |
208+
| `edumserrano/find-create-or-update-comment` | ✅ | ✅ | ❌ | Simplified find+update |
209+
| `replace-comment` (this action) | ✅ | ❌ | ✅ | Keep comments at bottom |
210+
211+
## Development
212+
213+
This is a composite GitHub Action that leverages:
214+
- [peter-evans/find-comment](https://github.com/peter-evans/find-comment) for finding comments
215+
- [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) for creating comments
216+
- GitHub REST API for deleting comments
217+
218+
No build step required! The action runs directly from the `action.yml` file.
219+
220+
## Contributing
221+
222+
Contributions are welcome! Please feel free to submit a Pull Request.
223+
224+
## License
225+
226+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
227+
228+
## Acknowledgments
229+
230+
- Inspired by the excellent work of [Peter Evans](https://github.com/peter-evans) on GitHub Actions for comments
231+
- Built to fill the gap for delete+create comment workflows

0 commit comments

Comments
 (0)