Practice 7: Git hooks and CI
By the end of this practice, you will know how to:
- Add Travis CI to a GitHub repository
- Set up Travis CI to test your code
- Add Travis CI build information to your repository's readme
- Create Git hooks to test your code
Setup
Log into GitHub and create a new repository. Name it "cse-prac7-ci". Make sure it's a public repository and that you are initializing it with a README file and a .gitignore for Python.
Setting up Travis CI
Go to GitHub marketplace and find Travis CI. It should be under the "Continuous Integration" category. Read the description to familiarise yourself with what Travis CI is and what you can do with it.
When you're ready to move on, scroll to the bottom of the page, to the "Pricing and setup" section. Make sure you choose the "Open Source" package and click "Install for free". The open source package allows you to use limited resources of Travis CI for free, all other packages have a monthly fee.
You will be asked to authorise Travis CI to connect to your GitHub account. You can choose to let it access all of your repositories or only some of them. If you wish, just grant Travis CI access to the "cse-prac7-ci" repository. Don't worry, all of the access can be revoked later.
After registering, you will be taken to http://travis-ci.com. If you are not automatically logged in, use your GitHub account to log in. Find the "cse-prac7-ci" repository in the left sidebar. Notice how Travis tells you that there are no builds for this repository. This is because we have not told Travis what it is expected to do.
Configuring Travis CI
Basic configuration of build procedures for Travis is simple. We will now set up your repository to tell Travis that it should test a python program every time a push is made to the repository.
Add a file called ".travis.yml" (MIND THE FIRST PERIOD!) to your repository. Copy the following code into the file:
language: python python: - "3.6" script: - pytest
Read the code above and try to understand what it means. We are telling Travis that:
- This repository uses the Python programming language
- The version of Python that we use is 3.6
- We want Travis to run the script "pytest"
Pytest is a testing framework for Python. We will be using it today to create a simple example of continuous integration.
Push the .travis.yml file to the repository, go to Travis website, find your repository and notice how Travis now says that the build is failing. Note: Travis build can take up to 30 seconds. Because we don't have any actual code yet, pytest doesn't find anything to test and returns an error. Inspect the Travis CI output. What information is available to you? What commands does Travis run? Can you find pytest's output?
Testing code
Here we will add some sample code for Travis to test. Add a file called "test_sample.py" to your repository. Copy and paste the following code into the file, the push it:
def inc(x): return x + 1 def test_answer(): assert inc(3) == 5
Go to Travis website again, wait for the build to complete (if it hasn't) and inspect the output. The output should be different now - pytest finds stuff to test but the test fails. Try to identify why the test is failing, fix the error and push new file to the repository (hint: the amount being added is not correct). If you can't find what the problem is, scroll to the end of this document for the answer.
Go to Travis website, find your repository and notice how Travis now says that build is passing. Inspect the output: can you find pytest output and where it says that 1 out of 1 tests has passed?
Adding Travis CI status to GitHub
If you have used GitHub more on your own, you might have noticed how some open source repositories have little badges in their README files that show if the build is passing or failing. Let's add this to our repository. Go to Travis website, find your repository. Notice the build status badge next to the repository address:
Click on the status badge. This opens a pop-up window that lets you embed the badge in other places. Choose "Markdown" as the format and copy the code. Edit your repository's README file and paste the code after the title. Go to your GitHub, find your repository and make sure the image is showing.
Test it - make the pytest fail again by returning the code to it's initial state and push the changes. Notice how Travis says that the build is now failing and the README shows this information as well.
Adding a Git hook
As a bonus, let's add a pre-commit Git hook to our repository.
In order for this to work, you need to have Python and Pytest installed on your computer. If you have Python installed, check if you have pip installed by running pip
on the command line. If you don't, install it. After you have installed pip, install pytest.
If you don't have Python installed or run into some other problems, just continue with the practice. The test will not work, but just to show that you understood how hooks are created, include output in your repository.
If you haven't yet, clone the repository to your computer. Create a file called "pre-commit" in @.git/hooks/@ directory of your repository. Add the following code to the file:
pytest
With this hook, we're telling git to run pytest before each commit. Because of the way pytest works, it will automatically abort the commit if any errors are found and allow us to commit if everything is OK.
Test the hook - run it once with the code broken (like it was in the beginning) and once with the code fixed. Make a screenshot of the output or copy the text, add it to your repository and push to GitHub.
NB: If you get a weird error message when trying to do "git commit", try writing this in the file:
#!/bin/sh pytest
The first line tells the command line interpreter what program can run this file, in this case "/bin/sh" marks this as bash (command line) script. If your hook was written in Python, for example, it could've been "/usr/local/bin/python" or wherever your python executable is.
How to fix pytest
The problem is that the testing function expects inc(3)
to equal 5. If you look at the definition of inc
function, you see that it adds 1 to the number it is supplied. To fix the error, make it add 2 instead:
def inc(x): return x + 2 def test_answer(): assert inc(3) == 5