Rolling Builds on TravisCI

Work on the Ruby SQL Server stack often requires long build times. Dependencies need to be downloaded, native extensions built, followed by long test runs to remote services. One feature I have come to rely on with Appveyor are their rolling builds - where commits to the same branch cancel previous running builds. Though Appveyor is great for Windows builds, my 💖 belongs to TravisCI. They are my goto for continuous integration due to their GitHub integration and open source presence.

Making rolling builds work on TravisCI led me to open GitHub issues where instead I had hoped for a simple configuration option. I did find great work done by Michael Grosser which allowed use of his open server to a) receive GitHub webhooks and b) make subsequent TravisCI API calls on your behalf. For us at CustomInk, we wanted something to host ourselves while ensuring GitHub payloads were verified to originate from our private repositories. All while ensuring that API calls to Travis are only our own.

Enter The Rolling Travis App

Rolling TravisCI BuildsWith an emphasis on TravisCI pro accounts and your company's security, we build a simple Rails 5 API application that you can configure and deploy to Heroku.

https://github.com/customink/rolling_travis_builds

Without duplicating that project's README, here are a few highlights of what this application does and how it can accomplish rolling builds for your repos.

Verify GitHub Payloads

The most important part of this process is verifying the GitHub payloads are indeed from your repositories. Here is an excerpt from the application's root controller.

class ApplicationController < ActionController::API

  before_filter :webhook_verify!

  private

  def webhook_verify!
    signature = "sha1=" + OpenSSL::HMAC.hexdigest(
      OpenSSL::Digest.new('sha1'),
      WEBHOOK_SECRET,
      request.body.read
    )
    ActiveSupport::SecurityUtils.secure_compare(
      signature,
      request.headers['X-Hub-Signature']
    )
  end

end

As you can see, GitHub signs the JSON payload using the X-Hub-Signature. All we need to do is check the signature against the payload. Using a before filter will ensure that if the secure compare returns false, no further actions are taken.

Async With Sucker Punch 2.0

API calls to Travis could take a second or two. If your organization is big and repositories are numerous, this could block your the webhook application from responding. In order to solve this, we bundle the latest version of Sucker Punch which uses concurrent-ruby to asynchronously process work.

class ApplicationController < ActionController::API
  def webhook
    CancelDuplicateBuildsJob.perform_in 5, webhook_repo
    head :ok
  end
end

class CancelDuplicateBuildsJob
  include SuckerPunch::Job
  def perform(webhook_repo)
    TravisApi.cancel_duplicate_builds!(webhook_repo)
  end
end

Here we are using the perform_in set to 5 seconds to give TravisCI a shot at first scheduling new builds before looking for duplicates.

https://github.com/customink/rolling_travis_builds

We hope you enjoy the Rolling Travis Builds application and help make it better. Thanks!

Resources


by Ken Collins