Creating A GitHub App That Comments On Issues Using GitHub API: Part II
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
- Authenticate as a GitHub app using JWT(JSON Web Tokens)
- 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.
- 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. - number. This refers to the issue number where we will use
payload["issue"]["number"]
to get it’s value from our JSON formatted payload. - 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.