57
Simple Ruby/Sinatra Github webhook script KYOSS July 2015 Jeff Squyres

Fun with Github webhooks: verifying Signed-off-by

Embed Size (px)

Citation preview

Simple Ruby/Sinatra Github webhook script

KYOSS July 2015Jeff Squyres

Github: A popular Git-hosting service

www.github.com

Github hostsyour Git repos

Git repo

Git repo

Git repo

Github hostsyour Organization’s Git repos

Git repo

Git repo

Git repo

YoyodyneOrganization

Github hostsyour Organization’s Git repos

Git repo

Git repo

Git repo

YoyodyneOrganization

Git repo

Git repo

Git repo

Git repo

Github has other services

Wiki

Code reviews

Git web UIWeb UI for editing / committing

Git branch control

Git repo forkingPull requestsIssue tracking

Milestone tracking

Github PagesMarkdown rendering

Read-only Git accessOrganization teams

Gravatar integration

Federated login services

Github has other services

Wiki

Code reviews

Git web UIWeb UI for editing / committing

Git branch control

Git repo forkingPull requestsIssue tracking

Milestone tracking

Github PagesMarkdown rendering

Read-only Git accessOrganization teams

Gravatar integration

Federated login services

API hooks

The Github Webhook

Hey!Something

just happened!

The Github Webhook

Hey!Something

just happened!

Github servers

The Github Webhook

Your serverGithub serversHTTP post

{ "action": "opened", "number": 1138, "pull_request": { "url": "https://api.github.com/repos/ofiwg/libfabric/pulls/1138", "id": 39149978, "html_url": "https://github.com/ofiwg/libfabric/pull/1138", "diff_url": "https://github.com/ofiwg/libfabric/pull/1138.diff", "patch_url": "https://github.com/ofiwg/libfabric/pull/1138.patch", "issue_url": "https://api.github.com/repos/ofiwg/libfabric/issues/1138", "number": 1138, "state": "open", "locked": false, "title": "replace usd_open with usd_open_for_attrs ", …}

Hey!Something

just happened!

This is what happened

What can you do with Github webhooks?

A common Github webhook use

Your serverGithub servers

A pull request was just opened

Kick off a buildof the pull requestand test the result

Send resultsback to Github

Example: Github kicked off tests

Example: all tests passed

Let’s look at another case

The Libfabric project

• Next generation high performance networking user stack

• Multi-vendor / organization

• Open source• Hosted on Github

Libfabric commits

Require a “Signed-off-by” line in Git commit messages

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/libtool.html. Fixes #1118. Signed-off-by: Jeff Squyres <[email protected]>

Libfabric commits

Require a “Signed-off-by” line in Git commit messages

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/libtool.html. Fixes #1118. Signed-off-by: Jeff Squyres <[email protected]>

commit efd7a5c878f53ba12ed771fa006d57b7f9d264fdAuthor: Jithin Jose <[email protected]>Date: Thu Jul 2 09:05:16 2015 -0700

sockets: fix resource leak in comm buffer Signed-off-by: Jithin Jose <[email protected]>

Libfabric commits

Require a “Signed-off-by” line in Git commit messages

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/libtool.html. Fixes #1118. Signed-off-by: Jeff Squyres <[email protected]>

commit efd7a5c878f53ba12ed771fa006d57b7f9d264fdAuthor: Jithin Jose <[email protected]>Date: Thu Jul 2 09:05:16 2015 -0700

sockets: fix resource leak in comm buffer Signed-off-by: Jithin Jose <[email protected]>

commit 621dd04cbe2c9e066602393d508c85d175bebbb9Author: James Swaro <[email protected]>Date: Mon Jun 29 12:28:32 2015 -0500

Initialize param_list to avoid crashing in library destructor

Libfabric commits

Require a “Signed-off-by” line in Git commit messages

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/libtool.html. Fixes #1118. Signed-off-by: Jeff Squyres <[email protected]>

commit efd7a5c878f53ba12ed771fa006d57b7f9d264fdAuthor: Jithin Jose <[email protected]>Date: Thu Jul 2 09:05:16 2015 -0700

sockets: fix resource leak in comm buffer Signed-off-by: Jithin Jose <[email protected]>

commit 621dd04cbe2c9e066602393d508c85d175bebbb9Author: James Swaro <[email protected]>Date: Mon Jun 29 12:28:32 2015 -0500

Initialize param_list to avoid crashing in library destructor

commit 621dd04cbe2c9e066602393d508c85d175bebbb9Author: James Swaro <[email protected]>Date: Mon Jun 29 12:28:32 2015 -0500

Initialize param_list to avoid crashing in library destructor

Missing Signed-off-by line!

Libfabric only accepts commitsvia pull requests

Libfabric Github Git repo

Libfabric only accepts commitsvia pull requests

Libfabric Github Git repo

jsquyres Github forked libfabric repo

Step 1: Jeff makes a Gihub “fork”of the upstream Libfabric repo

Libfabric only accepts commitsvia pull requests

Libfabric Github Git repo

jsquyres Github forked libfabric repo commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417

Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/... Signed-off-by: Jeff Squyres <[email protected]>

Step 2: Jeff makes a commit on hislibfabric fork

Libfabric only accepts commitsvia pull requests

Libfabric Github Git repo

jsquyres Github forked libfabric repo commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417

Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/... Signed-off-by: Jeff Squyres <[email protected]>

Step 3: Jeff files a pull request to askif his commit can be accepted intothe main Libfabric repo

Libfabric only accepts commitsvia pull requests

Libfabric Github Git repo

jsquyres Github forked libfabric repo commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417

Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/... Signed-off-by: Jeff Squyres <[email protected]>

Step 4: A libfabric maintainer acceptsJeff’s pull request

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/... Signed-off-by: Jeff Squyres <[email protected]>

commit d70ebb7b6f7afb51e3026c92c40b6aedae8b1417Author: Jeff Squyres <[email protected]>Date: Thu Jul 2 15:03:42 2015 -0700

libfabric.so: bump the Libtool .so version to 2:0:1

Per https://www.gnu.org/software/libtool/manual/... Signed-off-by: Jeff Squyres <[email protected]>

Wait – you missed a step

The missing step

1. Jeff makes a Gihub “fork” of the upstream Libfabric repo

2. Jeff makes a commit on his libfabric fork3. Jeff files a pull request to ask if his commit can

be accepted into the main Libfabric repo4. Libfabric maintainers review the pull request

commit(s)5. A libfabric maintainer accepts Jeff’s pull

request

The missing step

1. Jeff makes a Gihub “fork” of the upstream Libfabric repo

2. Jeff makes a commit on his libfabric fork3. Jeff files a pull request to ask if his commit can

be accepted into the main Libfabric repo4. Libfabric maintainers review the pull request

commit(s)5. A libfabric maintainer accepts Jeff’s pull

request

Is the commit good?

Does the code fit the style guide?

Is the code correct?

Does the code fit with the rest?

Does the code pass CI?

Does the code break anything?

The missing step

1. Jeff makes a Gihub “fork” of the upstream Libfabric repo

2. Jeff makes a commit on his libfabric fork3. Jeff files a pull request to ask if his commit can

be accepted into the main Libfabric repo4. Libfabric maintainers review the pull request

commit(s)5. A libfabric maintainer accepts Jeff’s pull

request

Is the commit good?

Does the code fit the style guide?

Is the code correct?

Does the code fit with the rest?

Does the code pass CI?

Does the code break anything?

Is there a Signed-off-by line?

The Signed-off-by lineIt’s kind of a silly thing

It has nothing to do with the code itself

The Signed-off-by line

But the lawyers tell us we must have it

The Signed-off-by line

We developers don’t want to have to deal with it

We just want to make sure it happens, but at zero cost to us

The Signed-off-by line

Sounds like a perfect jobfor automation

Example: PR with three commits

Example: PR with three commits

Example PR with Badness

Example PR with Badness

Example PR with Badness

Why is this one marked bad?

Example PR with Badness

Why is this one marked bad?

Github uses the status of the last commit as the statusof the overall pull request

That’s what it doesNow let’s look what it is

Sinatra

sinatrarb.com

Sinatra

sinatrarb.com

Sinatra: logistics

Sinatra processLocal browser

http://localhost:4567

Sinatra: logistics

Sinatra process

http:

//lo

calh

ost:4

567

nginx processRemote browser

http:

//ex

ampl

e.co

m

Sinatra “Signed-off-by” Github webhook

Your serverGithub servers

A pull request was just

opened (or modified)

Check to make sure each commit has a Signed-off-by line

Send resultsback to Github

Github to Sinatra logistics

Github servers Some web serververify-

signed-off.rb

Send resultsback to Github

“Signed-off-by” checkerSource code:

https://github.com/ofiwg/libfabric/blob/master/config/github-webhook/verify-signed-off.rb

github.com/ofiwg/libfabric

config subdir

github-webhook subdir

verify-signed-off.rb

Heavily inspired byhttp://git-scm.com/book/en/v2/GitHub-Scripting-GitHub

Setuprequire 'rubygems’ # Required by RHEL 6require 'httparty’ # Used in the body below for HTTP requestsrequire 'sinatra’ # All the Sinatra gluerequire 'json’ # Used in the body below for JSON stuff

# Put Sinatra on port 5000 (a fairly arbitrary choice)set :port, 5000

# Globalsuser_agent = 'ofiwg/signed-off-by-checker'

# Read the Github auth token in from the environment (it wouldn't do# the hard-code it where it would show up in a public Github repo!)auth_token = ENV['GITHUB_AUTH_TOKENif auth_token == nil then puts "Someone forgot to set \$GITHUB_AUTH_TOKEN before launching me. Aborting!\n" exit 1end

Handle HTTP Get# Github webhooks are not delivered as HTTP GETs. If we don't have# this Sinatra method here, Sinatra displays a goofy error message if# someone just visits "http[s]://url_to_this_script".

# Responds to an HTTP Get for “/”get '/' do # Output this plain string and exit 'Nothing to see here but us chickens'end

Handle HTTP Post# Respond to an HTTP Post to “/”post '/' do # Parse the JSON into the “push” variable push = JSON.parse(request.body.read) repo_name = push['repository']['full_name']

# If this is not a push on a pull request (i.e., if there's no # commits to examine, such as if this is a test webhook ping from # github), then just return HTTP status 200 (i.e., success) with a # handy message that you can see in the Github webhook debug logs. if push['action'] == nil || (push['action'] != 'synchronize' && push['action'] != 'opened') then # Returns an HTTP 200 status and a plain string return [200, ‘Nothing for this bot to do!'] end

…continues…

Get all commits on this pull request

# Get _all_ commits associated with this pull request commits_url = push['pull_request']['commits_url'] commits = HTTParty.get(commits_url, :headers => { 'Content-Type' => 'application/json', 'User-Agent' => user_agent, 'Authorization' => "token #{auth_token}" } )

# Setup some variables happy = true targetURL = 'https://github.com/ofiwg/libfabric/wiki#how-to-contribute' debug_message = "checking debug URL: #{commits_url}\n\n" final_message = ''

…continues…

Start analyzing the commit messages

# Loop over all the commit meta data we just downloadedcommits.each_with_index do |commit, index| sha = commit['sha'] status_url = “https://api.github.com/repos/#{repo_name}/statuses/#{sha}” status = { 'context' => 'Signed-off-by checker’ }

# Look for a Signed-off-by string in this commit if /Signed-off-by/.match commit['commit']['message'] status['state'] = 'success' status['description'] = 'This commit is signed off' else status['state'] = 'failure' status['description'] = 'This commit is not signed off' status['target_url'] = targetURL happy = false end final_message = status['description']

…continues…

Is this the last commit? if index == (commits.length - 1) && index > 0 then if happy then status['state'] = 'success' status['description'] = 'All commits were signed off. Yay!' else status['state'] = 'failure' status['description'] = 'At least one commit was not signed off' status['target_url’] = targetURL end final_message = status['description'] end

…continues…

Send back the results for this commit

HTTParty.post(status_url, :body => status.to_json, :headers => { 'Content-Type' => 'application/json', 'User-Agent' => user_agent, 'Authorization' => "token #{auth_token}" } ) end

…continues…

All done! # Return HTTP status 200 and a message that shows up in the # Github webhook debug logs return [200, ”Thanks for playing -- #{final_message}"]end

Example / reminder of output

Yay!