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 or master. 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.

Subscribe to my newsletter

What’s new with PäksTech? Subscribe to receive occasional emails where I will sum up stuff that has happened at the blog and what may be coming next.

powered by TinyLetter | Privacy Policy