GitHub Webhook

GitHub supports “Webhooks” to notify you of changes. In their own words:

Webhooks allow you to build or set up integrations which subscribe to certain events on GitHub.com. When one of those events is triggered, we’ll send a HTTP POST payload to the webhook’s configured URL. Webhooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. You’re only limited by your imagination.

Since so many R packages are developed on GitHub, this can be a useful integration. You might want to have R respond to new issues created for a repository, evaluate changes to data or code on GitHub, or just about anything else you can imagine.

Setup GitHub Webhook

The best guide to setting up a GitHub Webhook is the official documentation page. In brief…

  1. Navigate to the “Settings” page of your repository.
  2. Click the “Webhooks & Services” tab in the sidebar.
  3. Click the “Add webhook” button.
  4. For the “Payload URL”, use the URL at which your plumber POST endpoint is exposed, set the “Content type” to application/x-www-form-urlencoded, and enter a secret key.
  5. Click “Add webhook”

At this point, any commits that are pushed to that repository will trigger a POST request to the URL you specified. You can use code like the example below to respond to these triggers.

Example

In this example, we’ll demonstrate how to setup an plumber endpoint that is capable of listening for Webhook notifications from GitHub. The example will simply subscribe to push notitifications on the plumber repository (which are triggered any time a commit is pushed to that repo) and, in response, will install the most up-to-date version of plumber.

We’ll add one additional endpoint that enables us to see what version of plumber is installed on the system at that moment. You should find that the sha1 value of the response matches the latest commit hash in the master branch of plumber.

GET /version

GET https://plumber.tres.tl/github/version
Click "Get" to see the response.

appender.R

        #* Get information about the currently available
#* @get /version
function(){
  desc <- read.dcf(
    system.file("DESCRIPTION", package="plumber"))
  resp <- list(
    version = unname(desc[1,"Version"]),
    built = unname(desc[1,"Built"])
  )

  if ("GithubSHA1" %in% colnames(desc)){
    resp["sha1"] <- unname(desc[1,"GithubSHA1"])
  }

  resp
}

#* Give GitHub Webhook a way to alert us about 
#* new pushes to the new plumber repo. See
#* https://developer.github.com/webhooks/
#* @post /update
function(req, res){

  # Verify the provided signature to confirm this
  # request actually came from GitHub.

  # I stored my secret in a file at ~/.github
  secret <- readLines("~/.github")[1]
  hm <- digest::hmac(secret, req$postBody, algo="sha1")
  hm <- paste0("sha1=", hm)
  if (!identical(hm, req$HTTP_X_HUB_SIGNATURE)){
    # Invalid signature
    res$status <- 400
    res$body <- "invalid GitHub signature."
    return(res)
  }

  # Install new package
  devtools::install_github("trestletech/plumber")

  TRUE
}