Building a GitHub App with Glitch, Probot & Recast.AI
Yesterday I attended a Craftwork Amsterdam workshop with the promise to ‘create a GitHub App’. With the help of ‘Hubbers’ Don, Bas and Anisha we built a bot that triages GitHub issues.
The assignment is to flag GH issues using NLP (Natural Language Processing). Inspecting the content of an issue (or more specifically the issue title) using Recast.AI, we classify new entries as either: a command (which we interpret as an enhancement), a question (either a 'what question', WhQuery, or a 'yes/no question', YnQuery) or, an assertion (which we interpret as a bug report); and label it as such.
The stack
We're using Probot, a framework for building GitHub Apps in Node.js. It aims to eliminate all the drudgery – like receiving and validating webhooks. An app is a first-class actor on GitHub, like a user (e.g. @FloorD) or an organization (e.g. @phusion). The app is given access to a repository or repositories by being "installed" on a user or organization account and can perform actions through the API.
Probot.github.io features a couple of nifty hosted, free, and open source apps to extend your project on GitHub. Examples are Stale, which closes Issues and Pull Requests that have been taking up repository space for over 2 years, Work In Progress, preventing the merging of Pull Requests with "WIP" in the title, and Welcome, which welcomes new users.
To avoid having to mess with (setting up) a Node.js environment entirely, Don instructs us to use Glitch.
Glitch lets you instantly create, remix, edit, and host an app, bot or site, and allows you to invite collaborators to simultaneously edit code with you. Glitch is created by Fog Creek (the wonderful people that also gave us Trello). We didn’t have to start from scratch as Don already created a Glitch ‘remix’ for us, folder structure et al.
We then created a GitHub repository to act as our sandbox environment. Via developer settings, GitHub Apps you can go back and edit your bot at any time. I assigned it a different visual as it defaults to my GitHub avatar which I found confusing.
Hi bot-me!
Last but not least we use Recast.AI, a platform allowing developers to create conversational interfaces, more commonly known as (chat)bots.
The app
To get our own Glitch-hosted Probot up-and-running we configured a new app on GitHub. It needs a URL that looks like this: https://random-word.glitch.me/probot, except random-word will be the word combination Glitch randomly assigns to your app. Second-bite, in my case.
For the Webhook URL, we use this URL (again, updating the domain to match yours): https://random-word.glitch.me/. For the Webhook Secret, Don told us to just use any word we could easily remember.
On the Permissions & webhooks tab, we added read and write permissions for issues. And on the Permissions & webhooks tab, we subscribe to Issues events. From the configuration page that comes up after saving, we could download our private key to be saved in a file named my-app-name.2018-06-20.private-key.pem..
On GitHub, again, we click the Install tab, and install our app into the sandbox repo we just created. Using the New File button in Glitch we saved the private key in .data/private-key.pem
.
After adding our app credentials to the .env
file - APP_ID
from the About section of your GitHub app, WEBHOOK_SECRET
, PRIVATE_KEY_PATH
set to .data/private-key.pem
and NODE_ENV
set to production - a green Live label indicates our app finished loading.
Our code:
var recastai = require('recastai').default
module.exports = (app) => {
app.log('Yay! The app was loaded!')
app.on('issues.opened', async context => {
var request = new recastai.request(process.env.RECASTAI_TOKEN)
request.analyseText(context.payload.issue.title)
.then(function(result) {
// Is this an assertion, a question, or a command.
if(result.isAssert()) {
// This is an assertion. Let's file this as a bug report
var label = 'bug'
}
else if (result.isCommand()) {
// This is a command. Let's file this as a feature request
var label = 'enhancement'
}
else if (result.isWhQuery() || result.isYnQuery()) {
// This is a question, let's label it as such
var label = 'question'
}
if(label) {
let params = context.issue({
labels: [label]
})
context.github.issues.addLabels(params)
}
})
.catch(function(err) {
app.log(err)
})
})
}
Opening a new issue in our sandbox repository (with 'Support React!' as the title, for instance), the bot should now respond to the new entry by assigning a label.
The Logs button in Glitch allows you to view the activity log.
Octokit vs. Probot
In Monitoring GitHub issue tickets through automated tagging we wrote about open sourcing our support aggregator SupportCentral. SupportCentral also labels GitHub issues. In the blog post we only mention using a ‘webhook’. More specifically we're using Octokit to configure GitHub to post to /webhooks/github/hook
, which is then processed by Webhooks::GithubWebhookController
, which contains all the code for applying and unapplying the SupportCentral labels.
The ‘SupportCentral’ label is automatically added, Hongli is not frantically assigning and un-assigning labels (usually).
So what?
After yesterday's workshop I've set myself the goal to see if I can go ahead and replace the Octokit hack with a Probot app. I also would like to try and feed my bot all the issues (titles) the Passenger project received (over 2,000 open and closed issues combined). I'll report on my progress.