Creating a GitHub App to Improve Our Developer Experience

September 29, 2020  •  12 min read
  • GitHub
  • 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.

kelly sikkema YK0HPwWDJ1I unsplash

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.

comment to create

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.

branch created comment

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

  1. 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.

  2. Open “Add an existing project to GitHub using the command line” from the GitHub documentation.

  3. Follow the steps outlined to create your repository.

Note: Remember to use the repo name you used for the create-probot-app command above.

  1. Open your app folder in your code editor.

  2. Create a new file called .env.

  3. Open .gitignore.

  4. Add .env as an entry within the .gitignore file.

  5. Open .env.example.

  6. Copy and paste the contents to the .env file you created.

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

  1. Open “Creating a GitHub App” from the GitHub documentation.

  2. Follow the steps outlined until you reach the Homepage URL input.

  3. For the Homepage URL, enter either the app’s website, or you can use your repository address as I did.

    webhook url

Webhook URL

  1. Open https://smee.io/new.

    smee new

  2. Copy the Webhook Proxy URL to the Webhook URL input for your GitHub app.

    webhook url

  3. Open your .env file and paste the Webhook Proxy URL as the WEBHOOK_PROXY_URL value.

Webhook Secret

  1. With Ruby installed, open a terminal window and run:

    ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'

  2. Copy and paste the value into the Webhook secret input.

    webhook secret

  3. Return to your .env file and paste the Webhook secret as the WEBHOOK_SECRET value.

App ID

general

Return to your .env file and paste the App ID as the APP_ID value

Private Key

private keys

  1. Click Generate a private key.

  2. Save the file to your project folder.

  3. Rename the file to private-key.pem.

Setting Up App Permissions & Events

Permissions

  1. Next, you need to set the permissions needed for your app.

  2. Click the Permissions & events tab.

    permissions tab

  3. Determine what data you’ll need for your app and make those selections.

    permissions

    Since we need to create a comment on an issue, we need the Read & write permission for Issues.

  4. Once you make the Read & write selection on Issues, it selects the Metadata permission for you.

Event Subscription

event subscription

  1. Scroll down to Subscribe to events.

  2. 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

  1. Open a terminal instance and navigate to your app folder.

  2. Enter the following to run your app in watch mode:

    yarn dev or npm run dev

    or enter to run without watch mode

    yarn start or npm run start

  3. Open http://localhost:3000 in your browser.

  4. You should see a page that looks like the screenshot below but with ”teterino” replaced with your app name.

    probot screen

Writing Your App

Handling Subscribed Events

  1. 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.

  2. Your application should create a comment on an issue when assigned to a user so swap out issues.opened for issues.assigned.

  3. 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.

  1. Inside the repository that you installed your app, create or open an existing GitHub issue.

  2. Assign the issue to yourself.

  3. If successful, you will shortly see a comment containing “Hello world”

  4. Go to your Developer settings and click on the Edit button next to your app.

  5. Click the Advanced tab.

    advanced

    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.

  6. 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.

  1. Copy the JSON from the Payload

  2. Save the copied payload to a new file in the test/fixtures directory as issues.assigned.json.

  3. 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.

  1. Create a Vercel account.

  2. Install Vercel CLI using

    yarn global add vercel or npm install -g vercel.

  3. Login with vercel login.

  4. Run these commands replacing the aaa and bbb with the values for those variables

    • vercel 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

  5. Connect your GitHub repository to Vercel and deploy on git push

  6. While you wait for the deploy to finish, go back to your app settings page

  7. Update the Webhook URL to the URL of your deployment (which vercel has kindly copied to your clipboard)

  8. Repeat steps 3–5 in Testing your App

  9. Click Redeliver on the expanded event

  10. 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


Copyright © Rebecca Vest and Camp Code Blog, 2022. Unauthorized use and/or duplication of this material without express and written permission from this site's author and/or owner is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to Rebecca Vest and the Camp Code Blog with appropriate and specific direction to the original content. Built with Gatsby.