Skip to content

Add pr comment trigger action #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions pr-comment-trigger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# PR Comment Trigger

This action simplifies the creation of workflows that trigger on a comment on a PR.

It checks that the comment matches a specific pattern, optionally extracting data from it, and returns the context of the PR.

Inputs that it receives:

- `match`: The regex pattern to match the comment against. By default, it will match any comment.
- `ack`: A thumbs up reaction is added to the comment if it matched. Enabled by default.

Notice that you'll need to grant the workflow `issues: write` permission for the ack to work.

Outputs:

- pr-number: The PR number where the triggering comment was added
- pr-head-sha: The SHA of the HEAD commit of the PR
- pr-head-ref: The PR branch
- pr-base-ref: The base branch of the PR
- comment-body: The full comment body
- matches: A JSON object with the regex matches
- match-found: Whether the regex matched or not
- comment-id: The ID of the triggering comment

Notice that you will also have a lot of information available in the context for `issue_comment`:

- `github.event.comment`: The [full comment object](https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#get-an-issue-comment)
- `github.event.issue`: The [full issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue)
- [And more](https://docs.github.com/en/webhooks/webhook-events-and-payloads#issue_comment)

## Usage

Here's a simple workflow that uses this action:

```yaml
name: Trigger on a PR comment

permissions:
issues: write # Required to ack the comment

on:
issue_comment:
types: [created]

jobs:
check-comment:
# It's recommended that you use the action in a separate job.
# This makes it easier to skip the rest of the workflow if there was no match.

# The match on the comment body is not necessary, but it's a good way to skip unrelated comments quickly.
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/foo') }}

runs-on: ubuntu-latest

outputs:
match-found: ${{ steps.comment-check.outputs.match-found }}
matches: ${{ steps.comment-check.outputs.matches }}
pr-head-sha: ${{ steps.comment-check.outputs.pr-head-sha }}
pr-head-ref: ${{ steps.comment-check.outputs.pr-head-ref }}
pr-base-ref: ${{ steps.comment-check.outputs.pr-base-ref }}
steps:
- id: comment-check
uses: ensuro/github-actions/pr-comment-trigger@pr-comment-trigger # Use a tag or commit hash here
with:
match: "^/foo (?<bar>.*?) (?<baz>.*?)$"

process-command:
needs: check-comment
if: ${{ needs.check-comment.outputs.match-found == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Process Command
run: |
echo "Processing /check on PR #${{ github.event.issue.number }} ${{ needs.check-comment.outputs.pr-head-ref }} -> ${{ needs.check-comment.outputs.pr-base-ref }}" >> $GITHUB_STEP_SUMMARY
echo "bar: ${{ fromJson(needs.check-comment.outputs.matches).bar }}" >> $GITHUB_STEP_SUMMARY
echo "baz: ${{ fromJson(needs.check-comment.outputs.matches).baz }}" >> $GITHUB_STEP_SUMMARY
```

Notice that this job will be associated with the main branch and not with the PR. If you want to provide feedback on the PR besides the reaction, you can use checks and/or comments on the PR.

A more comprehensive workflow example showcasing this is provided in the [examples](./examples) directory.
112 changes: 112 additions & 0 deletions pr-comment-trigger/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: "PR Comment Trigger"
description: "Processes PR comments and extracts information based on regex patterns"

inputs:
match:
description: "Regex pattern to match against the comment"
required: false
default: ""
ack:
description: "Whether to acknowledge the comment with a reaction. Requires permission to write on issues."
required: false
default: "true"

outputs:
pr-number:
description: "PR number"
value: ${{ fromJson(steps.process-comment.outputs.result).prNumber }}
pr-head-sha:
description: "HEAD SHA of the PR"
value: ${{ fromJson(steps.process-comment.outputs.result).prHeadSha }}
pr-head-ref:
description: "HEAD branch reference of the PR"
value: ${{ fromJson(steps.process-comment.outputs.result).prHeadRef }}
pr-base-ref:
description: "Base branch reference of the PR (usually main)"
value: ${{ fromJson(steps.process-comment.outputs.result).prBaseRef }}
comment-body:
description: "Full comment body"
value: ${{ fromJson(steps.process-comment.outputs.result).commentBody }}
matches:
description: "JSON object with regex matches"
value: ${{ toJson(fromJson(steps.process-comment.outputs.result).matches) }}
match-found:
description: "Whether the regex matched or not"
value: ${{ fromJson(steps.process-comment.outputs.result).matchFound }}
comment-id:
description: "The ID of the triggering comment"
value: ${{ fromJson(steps.process-comment.outputs.result).commentId }}

runs:
using: "composite"

steps:
- name: Process PR Comment
id: process-comment
uses: actions/github-script@v7
with:
script: |
const commentBody = context.payload.comment.body;
const issueNumber = context.payload.issue.number;
const commentId = context.payload.comment.id;

// Get PR details
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: issueNumber
});

const result = {
prNumber: issueNumber,
prHeadSha: pr.head.sha,
prHeadRef: pr.head.ref,
prBaseRef: pr.base.ref,
commentBody: commentBody,
commentId: commentId,
matches: {},
matchFound: true
};

// Process regex match if pattern provided
const matchPattern = '${{ inputs.match }}';
if (matchPattern) {
try {
const regex = new RegExp(matchPattern, 'm');
const match = regex.exec(commentBody);

if (match) {
result.matchFound = true;

// Add numbered groups
for (let i = 1; i < match.length; i++) {
result.matches[i-1] = match[i] || "";
}

// Add named groups if they exist
if (match.groups) {
Object.assign(result.matches, match.groups);
}

if ('${{ inputs.ack }}'.toLowerCase() === 'true') {
// React to the comment with a thumbs up
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
content: '+1',
});
}
} else {
result.matchFound = false;
}
} catch (error) {
console.log(`Error processing regex: ${error.message}`);
}
}

return result;
- name: Debug
shell: bash
run: |
echo ${{ steps.process-comment.outputs.result }}
102 changes: 102 additions & 0 deletions pr-comment-trigger/examples/workflow-with-feedback.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Trigger on a PR comment

permissions:
pull-requests: write # Required to add a comment
checks: write # Required to add a check
issues: write # Required to react to the triggering comment

on:
issue_comment:
types: [created]

jobs:
check-comment:
if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/foo') }}
runs-on: ubuntu-latest
outputs:
match-found: ${{ steps.comment-check.outputs.match-found }}
matches: ${{ steps.comment-check.outputs.matches }}
pr-head-sha: ${{ steps.comment-check.outputs.pr-head-sha }}
pr-head-ref: ${{ steps.comment-check.outputs.pr-head-ref }}
pr-base-ref: ${{ steps.comment-check.outputs.pr-base-ref }}
steps:
- id: comment-check
name: Check PR Comment
uses: ensuro/github-actions/pr-comment-trigger@pr-comment-trigger # Use a tag or commit hash here
with:
match: "^/foo (?<bar>.*?) (?<baz>.*?)$"

- name: Debug Outputs
run: |
echo "Match found: ${{ steps.comment-check.outputs.match-found }}"
echo "Matches: ${{ steps.comment-check.outputs.matches }}"
echo "PR head SHA: ${{ steps.comment-check.outputs.pr-head-sha }}"

process-command:
needs: check-comment
if: ${{ needs.check-comment.outputs.match-found == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Create a check
id: create-check
uses: actions/github-script@v7
with:
script: |
const { data: check } = await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'Command: Check Transaction',
head_sha: '${{ needs.check-comment.outputs.pr-head-sha }}',
status: 'in_progress',
output: {
title: 'Transaction Check Initiated',
summary: 'Processing the check transaction command...'
}
});
return check.id;

- name: Process Command
run: |
echo "Processing PR #${{ github.event.issue.number }} ${{ needs.check-comment.outputs.pr-head-ref }} -> ${{ needs.check-comment.outputs.pr-base-ref }}"
echo "Bar: ${{ fromJson(needs.check-comment.outputs.matches).bar }}"
echo "Baz: ${{ fromJson(needs.check-comment.outputs.matches).baz }}"

- name: Comment on PR
id: comment
uses: actions/github-script@v7
with:
script: |
const matches = JSON.parse(`${{ needs.check-comment.outputs.matches }}`);

const comment = `## Foo command was executed
**Bar**: ${matches.bar || "unknown"}
**Baz**: \`${matches.baz || "unknown"}\`
**Status**: ✅ Success

*This check was triggered by a [comment](${{ github.event.comment.html_url }})*`;

const posted = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.issue.number }},
body: comment
});

return posted.url

- name: Complete the check
uses: actions/github-script@v7
if: always() # The check should be completed even if the job fails
with:
script: |
await github.rest.checks.update({
owner: context.repo.owner,
repo: context.repo.repo,
check_run_id: ${{ steps.create-check.outputs.result }},
status: 'completed',
conclusion: '${{ job.status }}',
output: {
title:'Transaction Check Finished',
summary: 'See ${{ steps.comment.outputs.result }}'
}
});