Creating a GitHub App to Improve Our Developer Experience
I 💚 to code
The best part of my workday is when I put on my headphones, crank up some music, and then zone out the world. I love the click-clack my mechanical keyboard makes as my fingers press the keys in a rush to keep up with my brain.
It’s this love that has driven me to find ways to increase my time coding. The only way to spend more time coding was to decrease the time spent on other menial tasks.
Three Great Virtues of a Programmer
Larry Walls, the author of Perl, said a great programmer has three particular virtues. These virtues are laziness, impatience, and hubris.
Laziness is a virtue?!? Get real! But it is laziness that drives the programmer “to go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote, so you don’t have to answer so many questions about it.”
Hubris influences the programmer to “write (and maintain) programs that other people won’t want to say bad things about” (even yourself).
Impatience comes from “the anger you feel when the computer is being lazy.” My “laziness” and “impatience” toward non-coding tasks has resulted in many utility programs. These utilities not only benefited me but also helped my co-workers.
Improving the Developer Experience
I’ve been a proponent of improving the developer experience for a while now. Rewind to 5 years ago when a venture capital company purchased my past employer. The new management told the developers we had to track our time spent on each task. Nothing puts the kibosh on available coding time than tasks like time tracking.
So I slapped together a WinForm application to sync my time tracking tool with Jira called Toggl to Jira. When I started working, I’d open the Toggl desktop widget, enter the Jira issue number for the name, and click “Start.” I clicked “Start/Stop” as I went about my day. Plus, I had the Toggl app on my phone if I forgot to click stop or needed to adjust the time. At the end of the week, I opened my sync tool and clicked the “Sync” button to add my time for that week. Easy-peasy!
What’s in a Branch Name?
Recently at Healthline, I developed a GitHub app to solve a time-wasting task. We use GitHub for version control. I was about to code one day when I remembered I hadn’t started a new branch for my work. I groaned and rolled my eyes. Ugh. Now I had to break my stride, find the GitHub issue, and use its title to come up with some name. With a branch name in mind, I pulled up a terminal in Visual Studio Code and proceeded typing like a madwoman.
git stash
git checkout master
git pull
git checkout -b 12345-program-page-giving-500-error
git stash pop
“What a waste. There has to be some way to make this fast and easy,” I thought. Unfortunately, this was not the first time this has happened to me.
A Star is Born
My brain started churning. If only there were a simple way to generate a branch before I started coding. I remembered Jira had a “Create Branch” button in the issue view. When clicked, Jira creates a branch in Bitbucket based on the issue title.
How could I put in place similar functionality in GitHub? I found a GitHub app called Create Issue Branch as a solution, but it didn’t fit the bill. First, its branch naming convention didn’t match our standard. And second, once assigned to a user, the app creates a branch and a comment with the branch name. Our issues start assigned to a product manager not a developer. We didn’t want the branch created until the assigned developer started. Also, some issues are research spikes, and those do not need branches.
I downloaded the source code from the project repository and started the modifications.
About my App
Issue to Branch is a GitHub app that also generates a comment on the issue when assigned to a user. But, my app adds a comment with a link to create a branch for every assignment change. This feature makes it easier to find one of these comments inside high activity issues.
Once the user clicks on one of the branch creation links, the app makes a branch based on its title. Then a new comment appears, which provides the Git commands for checking out the new branch. This feature makes it so the developer, not the first assignee, creates the branch.
GitHub Apps Made Easy
The GitHub app I forked used a framework for creating GitHub apps called Probot. Probot runs on a Node.js web application framework called Express.js and uses a GitHub API wrapper called Octokit. It has everything you need to create a successful GitHub app. The app has both development and testing environments set up at the get-go.
- Logging using Pino package
- Local development via a webhook proxy using Smee.io
- Unit Testing using Jest
Let’s Get to It!
Prerequisites: Intermediate Node web development skills, Node, NPM or Yarn, & Ruby
Let’s write a simple GitHub app that adds a “Hello world” comment to an issue upon assignment. The best way to get started is to use the Probot’s command-line tool (CLI) create-probot-app. For more detailed instructions on development, visit the Probot Development page.
Install Probot
-
Open a terminal window and enter the following command. Replace
idahogurl-github-app
with a name for your repository.yarn create probot-app idahogurl-github-app
or
npx create-probot-app idahogurl-github-app
Either command creates a folder containing a Probot app.
-
Open “Add an existing project to GitHub using the command line” from the GitHub documentation.
-
Follow the steps outlined to create your repository.
Note: Remember to use the repo name you used for the create-probot-app
command above.
-
Open your app folder in your code editor.
-
Create a new file called
.env
. -
Open
.gitignore
. -
Add
.env
as an entry within the.gitignore
file. -
Open
.env.example
. -
Copy and paste the contents to the
.env
file you created. -
Your
.env
file should look like this:# The ID of your GitHub App APP_ID= WEBHOOK_SECRET=development # Use `trace` to get verbose logging or `info` to show less LOG_LEVEL=debug # Go to https://smee.io/new set this to the URL that you are redirected to. WEBHOOK_PROXY_URL=
You’ll be able to fill in the values as you register your GitHub app.
Registering Your GitHub App
-
Open “Creating a GitHub App” from the GitHub documentation.
-
Follow the steps outlined until you reach the Homepage URL input.
-
For the Homepage URL, enter either the app’s website, or you can use your repository address as I did.
Webhook URL
-
Open https://smee.io/new.
-
Copy the Webhook Proxy URL to the Webhook URL input for your GitHub app.
-
Open your
.env
file and paste the Webhook Proxy URL as theWEBHOOK_PROXY_URL
value.
Webhook Secret
-
With Ruby installed, open a terminal window and run:
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
-
Copy and paste the value into the Webhook secret input.
-
Return to your
.env
file and paste the Webhook secret as theWEBHOOK_SECRET
value.
App ID
Return to your .env
file and paste the App ID as the APP_ID
value
Private Key
-
Click Generate a private key.
-
Save the file to your project folder.
-
Rename the file to
private-key.pem
.
Setting Up App Permissions & Events
Permissions
-
Next, you need to set the permissions needed for your app.
-
Click the Permissions & events tab.
-
Determine what data you’ll need for your app and make those selections.
Since we need to create a comment on an issue, we need the Read & write permission for Issues.
-
Once you make the Read & write selection on Issues, it selects the Metadata permission for you.
Event Subscription
-
Scroll down to Subscribe to events.
-
Select the events you want to handle. Those selected events, when triggered, have an HTTP POST payload sent to your webhook URL. Creating a comment on an issue upon assignment requires we subscribe to the Issues events.
Note: Depending on which events you select, you may need to adjust your permissions. For more information, visit “Editing a GitHub App’s Permissions” in the GitHub documentation.
Install Your App
Once you register and set up your GitHub app, you’ll be able to install it on one of your repositories. Open and follow the steps outlined in “Creating a GitHub App” from the GitHub documentation.
Running Your App
-
Open a terminal instance and navigate to your app folder.
-
Enter the following to run your app in watch mode:
yarn dev
ornpm run dev
or enter to run without watch mode
yarn start
ornpm run start
-
Open http://localhost:3000 in your browser.
-
You should see a page that looks like the screenshot below but with ”teterino” replaced with your app name.
Writing Your App
Handling Subscribed Events
-
Open
index.js
from your app folder.You’ll see
create-probot-app
already created an event listener for the “opened” action of the “issues” webhook event.app.on
will listen for the webhook event specified by the first argument. The first argument comprises the event name with the event action appended with a.
. The second argument is the function to run when the event triggers.module.exports = app => { app.on('issues.opened', async context => { // An issue was just opened. }) }
The function receives the event’s context, including the payload and helpers. You’ll be adding an event listener for each of your subscribed events. You can set several or all your subscribed events to use the same handler. For a list of all webhook events and their payloads see the GitHub documentation.
-
Your application should create a comment on an issue when assigned to a user so swap out
issues.opened
forissues.assigned
. -
Replace
// An issue was just opened.
with the code below. This code creates a comment containing “Hello world” on the assigned issue.const { owner, repo, number } = context.issue(); return context.github.issues.createComment({ owner, repo, issue_number: number, body: 'Hello world', });
context.issue
is a helper function. It gets information about the issue that triggered the event.context.github
is an instance of Octokit (GitHub API client).context.github.issues
provides functions to run issue-related API calls.
The Octokit documentation lists the other API functions available.
Testing Your App
To test our GitHub app, we need to trigger the issue assigned event.
-
Inside the repository that you installed your app, create or open an existing GitHub issue.
-
Assign the issue to yourself.
-
If successful, you will shortly see a comment containing “Hello world”
-
Go to your Developer settings and click on the Edit button next to your app.
-
Click the Advanced tab.
This screen lists all the deliveries sent to your GitHub app. It would be good to bookmark this because you’ll be visiting here often for debugging.
-
Under Recent Deliveries expand the latest entry.
You’ll see the sample response received a successful response. Upon failure, you’ll see a red indicator next to Response with the HTTP status code.
You can click on the Response tab and see the full response from your GitHub app.
Simulating Webhook events
While you develop, you’ll want to trigger your events many times. Clicking Redeliver will resend the event to your GitHub app. Another way is to simulate webhook events in your local environment.
-
Copy the JSON from the Payload
-
Save the copied payload to a new file in the
test/fixtures
directory asissues.assigned.json
. -
Open terminal and run:
yarn probot receive -e issues -p test/fixtures/issues.assigned.json ./index.js
or
npm run probot receive -e issues -p test/fixtures/issues.assigned.json ./index.js
Either command triggers the issues webhook using
issues.assigned.json
as the payload.
Deploy Your App
The last step is to deploy your GitHub app. You have several options for hosting. You’ll need a host that will run Node servers. Heroku and Glitch are popular ones. You can also run your app as a serverless function. Probot provides instructions for deploying serverless functions on AWS and Google Cloud. I’ve used Heroku for many projects, but Vercel is my new go-to for serverless functions.
-
Create a Vercel account.
-
Install Vercel CLI using
yarn global add vercel
ornpm install -g vercel
. -
Login with
vercel login
. -
Run these commands replacing the
aaa
andbbb
with the values for those variablesvercel secrets add probot-api-id aaa
vercel secrets add probot-webhook-secret bbb
vercel secrets add probot-private-key "$(cat ~/Downloads/*.private-key.pem | base64)"
Note: Replace the path with the path to the private key file in your project file
-
Connect your GitHub repository to Vercel and deploy on git push
-
While you wait for the deploy to finish, go back to your app settings page
-
Update the Webhook URL to the URL of your deployment (which vercel has kindly copied to your clipboard)
-
Repeat steps 3–5 in Testing your App
-
Click Redeliver on the expanded event
-
Return to your GitHub issue. If successful, you should see another comment containing “Hello world”.
Finishing Touches
No app would be complete without error handling, logging, and testing. I’d also be remiss if I didn’t share tips and solutions to some challenges before and after development. Your brain is probably about to explode with all this recent information. To save you from such an unfortunate event, I will post a follow-up article to address those loose ends. Until then!
Credits
Photo by Kelly Sikkema on Unsplash