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.
With 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.
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.
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.
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.
We hope you enjoy the Rolling Travis Builds application and help make it better. Thanks!