Checking CLAs via Travis — or — Make the Robots Enforce Legal F/OSS Requirements

If you're running an Open Source project using a Copyleft license like the GNU General Public License, its weaker cousin the LGPL, or even a variation on the Apache License, you should probably be getting Contributor License Agreements from everyone submitting code for inclusion.

Why?

Without a CLA, or some other legal contract, you technically have no right to redistribute anyone elses changes. There's a big debate online about whether CLAs are too heavy-handed, whether they provide any real protection, and if they drive away would-be contributors.

I want to side-step the socio-legal issues for now, and just focus on using a the Travis CI tool for enforcing CLAs whenever a Github Pull Request is submitted.

The concept is pretty straightforward: maintain a list of identities (read: email addresses) of people who have signed and submitted CLAs. Keep that file in-repo, and teach the CI build script to check the new commits it's testing against that list.

Simple!

Here's the format of the CONTRIBUTORS file:

# comments start with an octothorpe
# and run until the end of the line

# ^^ blank lines are ignored.

# the normal case - one person, one email
Felicia Adkins <felicia@example.com>

# two emails can be supplied if necessary
Mike Nash <m@example.com> <mike.nash@example.com>

When someone signs a new CLA, add their name to the list!

With Travis, we can use the TRAVIS_COMMIT_RANGE environment variable, which lists the range of commits (SHA1 IDs) that were included in the git push or Github Pull Request. The idea is this:

Here's a Bash function you can add to your CI script:

check_cla() {
  local passchar="\xe2\x9c\x94" # U+2714 - ballot check
  local failchar="\xe2\x9c\x98" # U+2718 - ballot x
  local rc=0
  local IFS=$'\n'

  echo "Checking CONTRIBUTOR status..."
  for x in $(git log --pretty=format:'%aE %h - %s (%aN <%aE>)' \
                 ${TRAVIS_COMMIT_RANGE}); do
    email=${x%% *}
    desc=${x#* }
    if grep -q '^[^#].*<'${email}'>' CONTRIBUTORS; then
      echo -e "\033[32m${passchar}\033[0m $desc"
    else
      echo -e "\033[31m${failchar}\033[0m $desc"
      echo -e "  \033[31m<${email}> not listed in CONTRIBUTORS file!\033[0m"
      rc=1
    fi
  done
  echo

  return $rc
}

When you call it, it will print out something like this:

If it finds any commit authors who aren't listed (again, by email address) in the CONTRIBUTORS file, it will print a message to that effect and ultimately return non-zero. With set -e enabled, this will fail your build. Otherwise, you can just check $? or wrap the call in an if block.

Happy Hacking!

James (@iamjameshunt) works on the Internet, spends his weekends developing new and interesting bits of software and his nights trying to make sense of research papers.

Currently working on Rook.