CircleCI Tutorial

Introduction

 
CircleCI is a service that detects when your codebase changes and runs jobs (that you define) on the code. It's used for CI/CD.
  • Continuous Integration (CI) - When pushing a code change, you want to know if the state of the code is still okay. (Ex. Does the code compile (build), and do all the tests pass.) 
  • Continuous Deployment (CD) - After making a code change (that hopefully passes your CI checks), deploy that code to wherever it's meant to be. (Ex. Deploy a server to Heroku/AWS, or run a one-time job (ex. database migration)) ​
CI/CD is incredibly useful for any project, even personal ones. The automation saves a lot of time and improves workflows, especially if you have to work with other people.
 
The reasons to use CircleCI are,
  • It's free 
  • It's not that hard to set up (compared to other CI tools) 
  • Many companies use CircleCI so it's good to be familiar with 
In this article, we're going to create a simple project -> create a repo for that project on github -> setup CI with CircleCI.
 

Our Demo Python Project

 
Let's create a file named "main.py" and add the following code to it,
  1. def Add(a, b):  
  2.         return a + b  
  3.           
  4. def SayHello():  
  5.         print("sup world from Rithik")  
  6.   
  7. if __name__ == '__main__':  
  8.         SayHello()  
We can run this file with the following command (in your terminal),
  1. $ python3 main.py  
Let's also create a file named "main-test.py" to test our main file.
  1. # Import the Add function, and assert that it works correctly.  
  2. from main import Add  
  3.   
  4. def TestAdd():  
  5.         assert Add(2,3) == 5  
  6.         print("Add Function works correctly")  
  7.   
  8. if __name__ == '__main__':  
  9.         TestAdd()  
We can run the test using the following command (in your terminal),
  1. $ python3 main-test.py  

Push The Project To Github

 
Let's create a github repository for this project. Go to github and make a new repo named "circleci-demo".
 
Now let's push our project (which is currently on our local computer) to the github repo. (Make sure to change the username to be yours) In your terminal:
  1. $ git init  
  2. $ git add .  
  3. $ git commit -m "Initial commit"  
  4. $ git remote add origin https://github.com/RithikBanerjee/circleci-demo.git  
  5. $ git push -u origin master  

Setting Up CI With CircleCI

 
So our github repo is set up, now we want to enable CI on it by setting up CircleCI. We need to create a configuration file so that CircleCI will know what we want it to do . Create a folder named ".circleci" and inside of it create a file named "config.yml".
  1. mkdir .circleci  
  2. touch .circleci/config.yml  
 Open ".circleci/config.yml" and add the following code:
  1. version: 2.1  
  2.   
  3. jobs:  
  4.   build:  
  5.     working_directory: ~/circleci-python  
  6.     docker:  
  7.       - image: "circleci/python:3.6.4"  
  8.     steps:  
  9.       - checkout  
  10.       - run: python3 main.py  
  11.   test:  
  12.     working_directory: ~/circleci-python  
  13.     docker:  
  14.       - image: "circleci/python:3.6.4"  
  15.     steps:  
  16.       - checkout  
  17.       - run: python3 main-test.py  
  18.   
  19. workflows:  
  20.   build_and_test:  
  21.     jobs:  
  22.       - build  
  23.       - test:  
  24.           requires:  
  25.             - build  
Notice that we have two types of tasks (jobs) that we want CircleCI to do: We want to run main.py, and we want to run our tests. We're also specifying that for every branch we want to perform the build job, and the test job. The official CircleCI Docs can show how to create more complex configurations, but this is a good starting point.
 
We also need to give CircleCI access to our repo so that they're authorized to run these jobs. Go to CircleCI's website, login in with your github account, and authorize them to access your circleci-demo repo that we just created. ​Now let's push our new config file to github.
  1. $ git add .  
  2. $ git commit -m "Adds CircleCI config file"  
  3. $ git push  
CircleCI should detect the code changes and run our build_and_test workflow for our branch. We should be able to see the jobs run on CircleCI's website.
 
(Pro-tip: If there are errors then make sure your config.yml is linted correctly. Use 2 spaces instead of tabs)
 

Testing That CircleCI Is Working

 
We have CI running now so let's test it out. Let's add a line to our unit test for our Add function to make sure 5 + 5 is equal to 10. Check out a new branch:
  1. $ git checkout -b add-test  
Add the following line of code to our TestAdd() function in "main-test.py":
  1. assert Add(5,5) == 10  
Push this branch up to github.
  1. $ git add .  
  2. $ git commit -m "Adds test to make sure 5 + 5 is equal to 10"  
  3. $ git push --set-upstream origin add-test  
Let's go to our github repo and make a Pull Request for this branch to see if CircleCI is running our jobs. We should see a build job and a test job with green checkmarks since they pass. We have CI working properly.
 
Of course, the useful part of CI (and unit tests) is making sure our codebase is working properly. Let's see what happens if we mess up old code by making our Add function do multiplication instead of tradition. In "main.py" change our Add function to multiply instead of add.
  1. return a * b  
Commit this change and push our updated branch to github.
  1. $ git add .  
  2. $ git commit -m "Add function now multiplies instead of adds"  
  3. $ git push  
If we look at our Pull Request, we should see that the build step passes since the code is still functional, but now our test fails since 2 * 3 isn't 5. So now we'd know the code change being introduced by this branch is harmful and we shouldn't merge this PR (and CircleCI can use that info to stop other jobs, like making sure not to deploy if the tests fail).
 

Conclusion

 
CI/CD is very important, and we've seen how to use CircleCI to set up CI. Our project was very simple, but extending the project to more complicated scenarios isn't that hard once you have the base config file. And of course, setting up the CD part is as simple as adding a job to deploy and adding that to our workflow. A few examples of how to make the most with CI/CD and CircleCI are,
  • Using CircleCI to run (scheduled) cron jobs. 
  • Add different workflows for different branches. (Ex. We only want to deploy the master branch, but not random PR branches) 
  • Set up the process to CD to a staging server. 
  • Ask CircleCI to run your Dockerized projects instead of needing to use native images. (For example, we used a Python base image this time)
 Keep an eye out for a more advanced CircleCI tutorial where we go through setting up the config for a Dockerized project that will also CD to a cloud host like Heroku.