Creating A GitHub App That Comments On Issues Using GitHub API: Part II

Simon Wathigo
6 min readMay 5, 2020

--

In part one of this tutorial series, we created an app and subscribes to issues webhook. In this tutorial, we are going to implement event handler for opened action. We are going to make our app comment on issues when a issues webhook of ‘opened’ action type is fired.
All the code used in this tutorial can be found here.

The comment can just be:

Thank you @username for opening the issue. I will look into it.

We will start by putting our credentials we obtained in part one of this series into the .env file in the root directory of our project.
This is an example of how your file should look like.

PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
...
HkVN9...
...
-----END DSA PRIVATE KEY-----"
GITHUB_APP_IDENTIFIER=12345
GITHUB_WEBHOOK_SECRET=your webhook secret

The dotenv is going to export these variable to the operating system environment and load them to into our project making the variables accessible in our project. Check this to learn more about environment variables.
Run bundle install to install all the dependencies if you have not done that already.

We are now going to write event handler and explain the template code defined in template_server.rb file in the root directory.
Open template_server.rb in your favorite editor so that we can explain what the template code does.

The first line of code is self-explanatory, we are setting port 3000 to be used when starting the web server. The second line is a Sinatra setting that allows the web server to listen on all available interfaces since localhost is the default setting. This will be useful when we host web server.

Next, we have a declaration of GHAapp class that extends from Sinatra::Application class that provides the default execution context for classic style applications. We are going to write all the code required by our application in this class. Let’s see how we read environment variables in our application.

Next is a code block that enables logging during development, which is the default environment in Sinatra. This code turns on logging at the DEBUG level to show useful output in the Terminal while you are developing the app:

We are going to use before filters in Sinatra that allows you execute some code before the route handler is executed.

This basically calls four helper methods that are executed before the route handler for '/event_handle’ is executed. We are going to go through the helper methods in the later section.
Next, let’s define a route handler for application.

post '/event_handler' doend

This is where we are going to define all the logic for handling issue webhook actions. Before we start implementing the event handler, let’s have a look at the helper methods that are called in before_action filter.

The first method getpayload_request captures the webhook payload and converts it into JSON format for easy manipulation.

The second verify_webhook_signature verifies that the webhook signature was generated by GitHub. To lean more about it check “Securing your webhooks.”

The other helper methods are for authenticating our app. Since we are using Octokit library to make API calls, our app needs to authenticate.
We are going to use two methods of authentication

  1. Authenticate as a GitHub app using JWT(JSON Web Tokens)
  2. Authenticate as a single installation of the app using installation access token.

We will go ahead and run our server so that we can implement our event handlers.
If you have not done it already run bundle install to install dependencies and start the server using smee client.
We are going to handle opened action of issues webhook.

When a GitHub sends the event payload to the client, it includes a request header HTTP_X_GITHUB_EVENT which indicates the type of event fired. We can use the type of event in this request header to handle specific event that we are interested in.
In our case, our app subscribes to issues event so we will Ruby case statement to handle this.
Our payload object contains an action field that which contains the name of the action that triggered the event. We want to handle opened action so that our app can comment on an issue on our behalf.
For now we will just use the logger.debug to display some information on the terminal once an issue is created. We can now go back to our application in the settings and add a repository.
We will need to install the app on a specific account or organisation as shown.

Select one repository for installation just for testing. After that, create an issue on the selected repository.
Start the application by running ruby template_server.rb and go to the selected repository and create an issue.
Currently, we have to rerun the server anytime we make changes, to prevent this check run:

gem install rerun

Then run the server using rerun

rerun template_server.rb

Now, you should be able to see the following output on the terminal:

From this output, we can see that our event handler handled the issues webhook event of opened action type by printing “An issue was opened! message on the terminal.
We are going to use Octikit:Client class instance made available via installation_client class variable defined in authenticate_installation helper method to send requests to the API.

We will use add_comment instant method in the installation client to comment on issues. This method requires several parameters. This is the method signature as defined in the documentation.

#add_comment(repo, number, comment, options={})

We are going to get these parameters from the payload.

  1. repo. We are going to use the name of the repository as the value for this parameter. Use payload["repository"][“full_name"] format to get the name of the repository.
  2. number. This refers to the issue number where we will use payload["issue"]["number"] to get it’s value from our JSON formatted payload.
  3. comment. Here, we will write the comment that we want to issue when an issue is opened. In our case we will just “Thank you for creating a new issue “ + author + “ Our team will look into this ASAP”

Instead of having all the code inside the when in the event_handler, we can define a helper method handle_issue_opened_event handle this.
Our updated code should look like this:

post '/event_handler' do  case request.env['HTTP_X_GITHUB_EVENT']  when 'issues'    if @payload['action'] === 'opened'      handle_issue_opened_event(@payload)    end  endend

Then our handle_issue_opened_event can be deifined as follows

def handle_issue_opened_event(payload)  logger.debug payload  repo = payload["repository"]["full_name"]  number = payload["issue"]["number"]  author = payload["issue"]["user"]["login"]  message = "Thank you for creating a new issue @" + author + ".      \nOur team will look into this ASAP."  @installation_client.add_comment(repo, number, message)end

That is it, install the the app on an organization or individual account, add a repository and create an issue on one of the allowed repository.
We should be able to see the following output.

That is all we need in order to use GitHub API on our app. All the code used in this tutorial can be found here.

Follow me on twitter or LinkedIn to get in touch.

--

--