{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Continuous integration\n",
    "\n",
    "In this course we have been running tests on a single remote machine by invoking `pytest` from the command line. Every time we make changes to the code we manually re-run the tests in order to check that our changes haven't broken anything.\n",
    "\n",
    "In a software project there might be many developers working on different parts of the code remotely, then pushing their changes (commits) to a centralised repository, such as one on [GitHub](https://github.com). How can we make sure that tests are run whenever a new commit is received and that we are notified whenever a change breaks the code?\n",
    "\n",
    "When running tests on your local machine you can be confident that the code is working in your environment, i.e. your operating system, version of python, etc. How can we be sure that the code will work on a range of environments?\n",
    "\n",
    "[Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration) (or CI) is the practice that attempts to solve these problems. The idea is that every time a new commit is pushed a CI server sets up a clean environment, does a `git clone` of your code from the central repository, then attempts to build it and run any tests.\n",
    "\n",
    "There are many fantastic free CI services. In this course we will use [Travis](https://travis-ci.org) since it integrates well with [GitHub](https://github.com). (You should already have a GitHub account that is linked to Travis. If not, set one up now.)\n",
    "\n",
    "## Configuring the CI build\n",
    "\n",
    "Central to using Travis is the `.travis.yml` file. This is a _hidden_ configuration file (as indicated by the `.` at the start of the file name) that sets options for the build. (The extension `.yml` indicates that this is a [YAML](https://en.wikipedia.org/wiki/YAML) file, which is a recursive acronym for \"YAML Ain't Markup Language\".)\n",
    "\n",
    "A typical `travis.yml` file for a python build looks something like the following:\n",
    "\n",
    "```yml\n",
    "language: python\n",
    "\n",
    "python:\n",
    "    - \"3.5\"\n",
    "    - \"3.6\"\n",
    "\n",
    "script:\n",
    "    - pytest\n",
    "```\n",
    "\n",
    "At the top of the file the `language` keyword is used to indicate that this is a `python` codebase. Next we specify which versions of python we want to use for our builds. Finally we come to the `script` section, which specifies the commands that will be run once the build environment is set up and our code has been cloned from GitHub (which will be done automatically). Full details about customising builds can be found [here](https://docs.travis-ci.com/user/customizing-the-build/).\n",
    "\n",
    "(Note that there is an inconsistency with the naming of the `pytest` executable for the different versions of python provided by Travis. This makes it hard to use the same `script` section for a wide range of python versions. See [here](https://github.com/travis-ci/docs-travis-ci-com/issues/982) for details.)\n",
    "\n",
    "## Your first CI build\n",
    "\n",
    "We'll now walk through the process of running your first CI build. Before starting you'll need to open a _terminal_ by locating the `Home` Jupyter tab, clicking on the `New` dropdown button near the top right, then selecting `Terminal` from the list. For convenience, it might be preferable to split your screen so the terminal tab is next to this one. That way you can follow through the tutorial as you execute various commands.\n",
    "\n",
    "First move into the `grid` directory. This has already been set up with a `.travis.yml` file and a GitHub `README.md` file.\n",
    "\n",
    "```bash\n",
    "cd grid\n",
    "```\n",
    "\n",
    "Edit `README.md` so that both instances of `USERNAME` are replaced by your GitHub username.\n",
    "\n",
    "Also edit `grid.py` to re-introduce the bug that you fixed earlier. (Replace `h-1` with `w` on line 133.) Run `pytest` to check that the tests fail.\n",
    "\n",
    "```bash\n",
    "pytest\n",
    "```\n",
    "\n",
    "Now initialise a new `git` repository.\n",
    "\n",
    "```bash\n",
    "git init\n",
    "```\n",
    "\n",
    "Add all of the files in the directory and stage them for the commit.\n",
    "\n",
    "```bash\n",
    "git add .\n",
    "```\n",
    "\n",
    "Commit the files that you've staged in your local repository.\n",
    "\n",
    "```bash\n",
    "git commit -m \"First commit.\"\n",
    "```\n",
    "\n",
    "Go to your [GitHub](https://github.com) profile page and create a new repository called `grid`. (It's important that you use this exact name!) To do so click on the `Repositories` tab, then the `New` button. Leave the check box for \"Initialize this repository with a README\" unchecked.\n",
    "\n",
    "<img src=\"assets/01.png\" width=800>\n",
    "\n",
    "We now need to add Travis integration to the new repository. If you go to your Travis homepage you should see a list of your GitHub repositories. If not, click on the `Sync account` button in order to refresh the list. (It can sometimes take a couple of minutes for a new repository to appear.) Once you see the `grid` repository, click on the slider to enable the CI hook.\n",
    "\n",
    "<img src=\"assets/02.png\" width=800>\n",
    "\n",
    "Back on the command line we can now add a URL for the new remote repository that you just created. Once again, replace `USERNAME` with your actual GitHub username.\n",
    "\n",
    "```bash\n",
    "git remote add origin remote https://github.com/USERNAME/grid.git\n",
    "```\n",
    "\n",
    "You can now push your commit to the remote repository.\n",
    "\n",
    "```bash\n",
    "git push origin master\n",
    "```\n",
    "\n",
    "Since we enabled a build hook for the `grid` repository, Travis will detect the presence of the `.travis.yml` file in your new repository and automatically initiate a build. If you visit the GitHub page for the repository you will see a Travis _build status_ image on the main page. \n",
    "\n",
    "<img src=\"assets/03.png\" width=800>\n",
    "\n",
    "Clicking on this will take you to the Travis page for the repository, where you can see the progress of the current build, as well as the details of any previous builds. You should see the status reported as _failed_. In addition, you might also receive an email notifying you of the error.\n",
    "\n",
    "<img src=\"assets/04.png\" width=800>\n",
    "\n",
    "(It's normally bad practice to push code that you know is broken. In this case we're using it as an example to show how to go about fixing it in the correct way.)\n",
    "\n",
    "## Creating an issue\n",
    "\n",
    "On the GitHub page for the `grid` repository you can open an _issue_ to alert people that the code is broken. Click on the `Issues` tab followed by the `New issue` button. Give your issue whatever title you like, then hit submit. It's good practice to give a minimal example that illustrates the problem. This helps the owner of the repository to reproduce the problem. You could also provide a new unit test if none of the current ones trigger the bug. In this case, we already have a good test that catches the error.\n",
    "\n",
    "<img src=\"assets/05.png\" width=800>\n",
    "\n",
    "## Pushing a fix\n",
    "\n",
    "Back in your local repository fix the bug that you introduced earlier and verify that the tests now pass.\n",
    "\n",
    "Having done this you can stage the `grid.py` file, then commit the change. For simplicity we'll do this in a single step.\n",
    "\n",
    "```bash\n",
    "git commit grid.py -m \"Fixed a bug affecting cells at the top of a grid. [closes #1]\"\n",
    "```\n",
    "\n",
    "Now push the commit to GitHub.\n",
    "\n",
    "```bash\n",
    "git push\n",
    "```\n",
    "\n",
    "The commit will now appear on GitHub and Travis will run another build using the updated version of the code. Once the build is complete you should hopefully see a green status badge on the repository homepage to indicate that it passed.\n",
    "\n",
    "<img src=\"assets/06.png\" width=800>\n",
    "<img src=\"assets/07.png\" width=800>\n",
    "\n",
    "Take another look at the `Issues` tab. You should see that the issue that you opened is now _closed_. This happened automatically because we included the phrase \"`closes #1`\" somewhere in our commit message. Here `#1` is the issue number, i.e. the first issue that was opened.\n",
    "\n",
    "<img src=\"assets/08.png\" width=800>\n",
    "<img src=\"assets/09.png\" width=800>\n",
    "\n",
    "(It is often helpful to put CI flags in brackets at the end of a commit message. This way it keeps the output of `git log` clean and it is easy to search for them.)\n",
    "\n",
    "(If you find a bug in someone else's code then you should make a [fork](https://guides.github.com/activities/forking) of the repository, fix the bug, then create a pull request.)\n",
    "\n",
    "## Skipping a CI build\n",
    "\n",
    "Sometimes you might commit changes that don't affect the functionality of the code, e.g. comments, or changes to the `README.md` file. In this case there is no need to run another CI build since none of the changes will affect the result of the tests. Since a build can be a time consuming process it would be wasteful to run one if wasn't absolutely necessary.\n",
    "\n",
    "Thankfully there is a flag that can be added to commit messages in order to indicate that a CI build should be skipped, `ci-skip`.\n",
    "\n",
    "Edit the `README.md` file to include a new line saying \"Testing is great!\" (or whatever you prefer). Now commit your changes.\n",
    "\n",
    "```bash\n",
    "git commit README.md -m \"Updated the README. [ci skip]\"\n",
    "```\n",
    "\n",
    "Finally push the changes to GitHub.\n",
    "\n",
    "```bash\n",
    "git push\n",
    "```\n",
    "\n",
    "If you go to the GitHub or Travis page for your `grid` repository you should find that there wasn't a third CI build. On the GitHub page you can click on where it says \"`3 commits`\" to show the commit history. There should be a red cross (failed) next to the first commit, a green tick (passed) next to the second, and nothing (skipped) next to the third.\n",
    "\n",
    "<img src=\"assets/10.png\" width=800>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
