CI/CD with GitHub, Travis CI, and Heroku
The idea of Continuous Integration (CI) stems from the principle that developers should push code more frequently to a shared repository like GitHub.
With that in place, tests can be written by the developers to verify the integrity of the code. Then a CI service can pick up the code, build it and run tests on it and notify the required personnel if issues arise.
Continuous Delivery takes this a step further and automatically deploys the code changes to testing and/or a production environment after a successful build.
In this tutorial, we’ll see how to set up continuous integration/delivery with GitHub, Travis CI, and Heroku but you can do the same for other services like AWS or Digital Ocean.
Prerequisites
For this tutorial, you’ll need:
- A basic understanding on HTML, CSS, JavaScript, Node/express
- A text editor
- A Web browser
- Travis CI account
- Heroku account
- GitHub account
Setup Node Server Application
We’ll be working on a simple server that responds with Hello world to any request. We’ll use a node server to build the server.
To start off, let’s initialize the project. From your terminal, run:
npm init
It would ask you a few questions that would be needed to setup the project:
Once done, you would see a package.json file in the project directory. Next, we’ll install a few packages that the application
npm install --save express
npm install --save-dev jest supertest
express
– for handling routesjest
andsupertest
– for running tests on our node app
Create a new file app.js
in the route of your project and add the following snippet:
const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.status(200).json('Hello world')
})
module.exports = app.listen(4000, () => console.log(`Running on http://localhost:4000`))
It’s a simple express server that returns Hello world when the route http://localhost:4000
is requested. Navigate to the URL in your browser and you will see the text in your browser.
For the next step, we’ll write a test to verify the integrity of our code. Generally, you will write tests before you write the code to pass the test but since this is a small application, we’ll be writing the tests after.
Create a new file tests/routes.test.js
and add the following snippet:
const request = require('supertest')
const app = require('../app')
describe('Get route', () => {
it('page should return hello world', async (done) => {
const res = await request(app).get('/')
expect(res.statusCode).toEqual(200)
expect(res.body).toEqual('Hello world')
done()
})
})
afterAll(async () => { await app.close() })
It’s a simple test that checks the response status code is 200 and checks that we get the text “Hello world.” To run the test, add the following to the “scripts” section of package.json
.
"test": "jest"
Also, add the following key to the object:
"jest": {
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/"
]
}
In your terminal, run the tests:
yarn test // or npm run test
If the server was setup correctly, the tests should pass.
Setting up Continuous Integration
As mentioned earlier, the idea behind continuous integration is that code is pushed to a central repository frequently. Now that we have our code setup, let’s push the code to GitHub.
- Create a new project on GitHub
- Follow the instructions to push your code to GitHub
- Once done, your code will be available on GitHub
Now that we have the code available on GitHub, we’ll connect Travis CI to our repository so it will monitor the repository and automatically test and deploy our code.
To get started with Travis CI using GitHub
- Go to Travis CI website and Sign up with GitHub.
- Accept the authorization of Travis CI. You’ll be redirected to GitHub.
- Select your profile picture in the top right of your Travis Dashboard and click Settings.
- Select manage repositories button which would take you to GitHub
- On GitHub, you can choose to either install Travis CI for all repositories (including future repositories) or only a select few.
- If you choose only select repositories, be sure to select the repository you created earlier before you click Approve and Install.
Add a new file travis.yml
to the root directory of the project and add the following:
language: node_js
node_js:
- 10.13.0
install:
- npm install
script:
- npm run test
branches:
only:
- master
- language – specifies the language of choice (Node.js)
- We specify the version of node.js (you can get your version by running
node -v
in the terminal) - install – specifies commands needed to install any required packages
- script – specifies scripts to be run after the installation phase
- branches – indicates the branches Travis should monitor
Save, commit the changes and push to GitHub. On Travis CI, you will see a new build start:
If the script fails, the color becomes red to indicate a failure. Else, it lights up green like this:
Continuous Delivery with Heroku
At the moment, we’ve set up continuous integration with GitHub and Travis CI but it still requires human interaction to deploy to a production server. We can automate this step with Travis CI or better still, we can tell Travis to deploy to different environments (staging, test, production, etc) depending on the branch pushed to GitHub.
Most teams work with at least 2 branches: master and dev/develop. master branch is reserved for the well-reviewed and tested code while dev branch contains code for the next version of the project. Developers create feature branches from the dev branch and create pull requests to the dev branch when they are done.
For this demo, we’ll have two environments: staging and production. staging is where we’ll deploy code while testing and production will be the final approved version will be deployed. We’ll also have 2 branches: master and dev.
dev -> staging
master -> production
Our dev branch would deploy to the staging environment and the master deploys to the production environment. Let’s create an account on Heroku.
To get started with Heroku
- Create an account on Heroku
- From the dashboard, create a new app:
- We’ll create two apps: one for staging and one for production (take note of the app names)
For this demo, we’ll use:
node-hello-world99 for production
node-hello-world-staging for staging
For the next step, you’ll need two things:
Download and install both CLI. Once installed, login on both services.
// for heroku
heroku login
// for travis
travis login --pro
Then create dev branch from the master branch
git checkout -b dev
Update .travis.yml
like so:
language: node_js
node_js:
- 10.13.0
install:
- npm install
script:
- npm run test
branches:
only:
- master
- dev
deploy:
provider: heroku
api_key:
secure: ADD_API_KEY_HERE
app:
master: node-hello-world99
dev: node-hello-world-staging
There are new steps we’ve added to .travis.yml
. We’ve added a new branch (dev) for Travis CI to track. We’ve also included a new step to our build: deploy. This is where we add instructions for deployment. We specify our provider (heroku), api_key (we’ll get to this in a moment), and our app. The app is where we specify the app name and branch to deploy. So the master branch is deployed to node-hello-world99 and dev is deployed to node-hello-world-staging.
To get your API key, run this command from the terminal inside your project directory:
travis encrypt $(heroku auth:token) –add deploy.api_key –pro
It will automatically generate an API key and automatically add the key to api_key.secure. Now, you can commit your code and push to GitHub. On Travis CI, a new build will start but this time a deploy step is included which will push the application to Heroku. At the end of it, you would get a link which can be used to view the app.
Or from your Heroku dashboard, you can select the app and click “open app.”
Since we just pushed the dev branch, it gets deployed to the staging environment. Once you are happy with the project and it’s passed all reviews, it can then be merged to the master branch which will automatically deploy it to the production environment. With this set up in place, you can keep working and deploying to a staging environment without disrupting your production environment.
Conclusion
To recap, we’ve looked at Continuous Integration and Delivery (CI/CD). We’ve looked at Travis CI and Heroku and how both platforms can be used to automate builds, tests, and deploys.
There are other use cases for these two beyond the scope of this tutorial. Hopefully, this tutorial is a good introduction to the concept of CI/CD which you can build on.