Run GitHub Actions on Issue Comments
Janne Kemppainen |GItHub Actions lets you do all sorts of fun things with your CI pipelines. Issue comments are one of the many events that can trigger an action run, and they can be used to create some useful interactions.
ChatOps is a way to integrate your communication tools to the automated processes in your system. Since GitHub is a collaboration platform where people discuss and give feedback on code changes, it makes sense to utilize that same space for triggering events in your repositories.
Services such as Atlantis already handle their communication and interaction through pull request comments. With GitHub Actions you can relatively easily write your own workflows and custom logic to create a similar experience.
You might also be interested in my Gentle Introduction to GitHub Actions! Remember to also check out my actions-playground repository.
Triggering an action from an issue comment
When you want to start a workflow based on a comment, the issue_comment
event is your friend. You’d typically use the created
event type, but you can also use edited
if you want to run the workflow when someone edits their message. When someone deletes a message that will trigger the deleted
event.
Here is a complete example workflow that reacts to a positive comment about pizza, you can save it for example as .github/workflows/like-pizza.yml
:
name: Like pizza
on:
issue_comment:
types:
- created
jobs:
like-pizza:
runs-on: ubuntu-latest
if: ${{ github.event.comment.body == 'I like pizza'}}
steps:
- name: Like pizza
uses: actions/github-script@v4
with:
script: |
const {owner, repo} = context.issue
github.reactions.createForIssueComment({
owner,
repo,
comment_id: context.payload.comment.id,
content: "+1",
});
The on
section defines issue_comment
as the triggering rule for the created
event. If you want to support message editing you can add the edited
event to this list. All issue comments will now trigger this workflow, remember that pull requests are also issues.
There is a catch when you’re developing actions that react to issue comments! When a new comment is added to a pull request, the event actually happens on the default branch of the repository, typically
main
ormaster
. This makes development of new action workflows a bit difficult since you’re not able to test them properly until the code is merged to the default branch. Therefore, you might want to do your development and testing in a separate repository, and then copy the final working result back to your original repository.
The workflow contains a single job called like-pizza
that is run conditionally based on the message body from the event. The event data can be accessed from the github.event
object. Here the if
statement applies to the whole but you can use them on individual steps too.
The actual step uses the actions/github-script action to run a piece of JavaScript that calls the GitHub API to add a reaction to the comment, in this case a thumbs up. This action is really useful in when you want to interact with the GitHub API but don’t want to create your own separate action for it.
Distinguish between issues and pull requests
We already learned that the event data is available via the github.event
object. We also learned that pull requests are issues too. So how can we tell if we are working with an issue or a PR?
In the case of a pull request the event data will contain the issue.pull_request
property, so we can use a conditional to check for this:
name: Run on PR comment
on:
issue_comment:
types:
- created
jobs:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request }}
steps:
...
We can use the inverse condition (exclamation point) to run the job for issue comments only:
if: ${{ !github.event.issue.pull_request }}
Checkout the correct commit
Since the issue comment event runs against the main branch we need to figure out a way to checkout the correct revision if we want to work with the changes from a pull request.
Here is an imaginary workflow that would create a new deployment when someone comments a PR with “/deploy”:
name: Deploy
on:
issue_comment:
types:
- created
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request && github.event.comment.body == '/deploy'}}
steps:
- name: Get PR SHA
id: sha
uses: actions/github-script@v4
with:
result-encoding: string
script: |
const { owner, repo, number } = context.issue;
const pr = await github.pulls.get({
owner,
repo,
pull_number: number,
});
return pr.data.head.sha
- uses: actions/checkout@v2
with:
ref: ${{ steps.sha.outputs.result }}
- name: Run deployment
run: make deploy
Here I’m assuming that make deploy
would be your command to create a new deployment to the proper environment.
The actions/checkout action is used to clone the repository to the actions runner. Before that, we need to use the GitHub API to find out the correct commit SHA for the pull request since that information is not included in the triggering event. Note that the result-encoding: string
parameter is important, since otherwise the script action would output JSON.
The GitHub script action automatically sets the returned value to an output called result
. Since we have defined an id
for that step we can access this value in the checkout step as a parameter to ref
. Now the correct revision is going to be fetched for the deployment step.
Reply back to the issue
To make the chat interaction complete your workflow should probably reply back to the issue with some details on how the deployment went.
The following snippet contains three steps. The first one emulates a deployment that can fail randomly. The other two steps respond the result of the deployment back to the issue.
- name: Fail randomly
run: |
cat README.md
exit $((RANDOM % 2))
- name: Message success
if: ${{ success() }}
uses: actions/github-script@v4
with:
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Deployment succeeded! ✅',
});
- name: Message failure
if: ${{ failure() }}
uses: actions/github-script@v4
with:
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Deployment failed! ❌',
});
Here we use the modulo operator to get a random exit value of either 0 or 1. Zero is interpreted as success, which will then trigger the step to message success. One means failure, so the failure()
function returns true and the “message failure” step is executed. The default behavior for each step is to run on success, so the if statement is not strictly needed in the first case, but it does add some clarity.
Summary
As we have seen, creating conversational workflows is doable using GitHub Actions. If you know some JavaScript then you can even create more complicated interactions with the GitHub script action.
You can take a look at the dummy deployment workflow in my Actions playground repository if you want a starting point for your own custom workflows. It contains the needed code for code checkout, reactions, and issue comments based on the deployment status.
Previous post
Use Make on Windows