Good software engineering teams commit often and implement often. These are some of the main ideas behind continuous integration (CI) and continuous deployment (CD). Gone are the days of quarterly or annual releases and long-lived feature branches!
Today we’re going to show you how you can automatically deploy your app to Heroku whenever the code is merged into your master branch using GitLab CI/CD.
Ready to dig in?
Start of work
Before we get started, we’ll need two things: a Heroku account and a GitLab account.
Heroku is a great place to host and deploy your applications. As a Platform as a Service (PaaS), Heroku lets you focus on building cool stuff while abstracting away much of the infrastructure complexity. You can create a Heroku account here.
GitLab is a great place to store your code. In addition to being just a source code management tool, GitLab also offers native CI/CD capabilities so you can set up pipelines for testing and deploying your code without the need for another third-party tool. You can create a GitLab account here.
The demo application shown in this article uses GitLab and Heroku. You can find all the code in the GitLab repo here.
Running our application locally
You can run the application locally by cloning the repo, installing the dependencies, and running the start command. On your terminal, do the following:
$ git clone https://gitlab.com/tylerhawkins1/heroku-gitlab-ci-cd-demo.git
$ cd heroku-gitlab-ci-cd-demo
$ npm install
$ npm start
After running the application visit http://localhost:5001/ in your browser and you will see the application running locally:
Now that the app is running locally, let’s deploy it to Heroku so you can access it anywhere, not just on your computer.
If you don’t already have the Heroku CLI installed on your machine, you’ll need to install it before proceeding.
After installing the Heroku CLI, run the following commands from your terminal to create a new Heroku app, deploy it, and open it in your browser:
$ heroku create
$ git push heroku main
$ heroku open
Additionally, you should see the same application, but this time running on the Heroku URL instead of localhost. Well done — you’ve deployed your app to Heroku!
Changes in our application
Now that we have the Heroku app running, what if we want to make some changes? You can make changes to the source code and then add, commit, and push those changes to the main
branch office.
If you wanted to apply those changes to a production Heroku application, you should be up and running git push heroku main
again. Wouldn’t it be nice if we could automate the deployment instead of having to manually deploy it every time?
This is where GitLab CI/CD comes into play.
Continuous integration/continuous implementation
Continuous integration (CI) means taking over frequently and keeping the building in good condition at all times. Normally, you would verify that the middleware is in good health by running checks in the CI pipeline. These checks may include linters, unit tests, and/or end-to-end tests.
Continuous Deployment (CD) is all about frequent deployment. If the checks in the CI pipeline pass, the intermediate version is deployed. If the checks in the CI pipeline fail, the intermediate version is not deployed.
With GitLab CI/CD, we can configure our CI pipeline to do just that — run our tests and then deploy our application to Heroku if all tests pass.
Configuring GitLab CI/CD
We can create a CI pipeline in GitLab programmatically using a .gitlab-ci.yml
file. Our file looks like this:
image: node:20.10.0
cache:
paths:
- node_modules/
before_script:
- node -v
- npm install
stages:
- test
- deploy
unit-test-job:
stage: test
script:
- npm test
deploy-job:
stage: deploy
environment: production
script:
- apt-get update -yq
- apt-get install -y ruby-dev
- gem install dpl
- dpl - provider=heroku - app=$HEROKU_APP_NAME - api-key=$HEROKU_PRODUCTION_KEY
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
The GitLab CI pipeline consists of stages and jobs. Each phase can contain one or more jobs. In our process we have two phases: test
and deploy
. in test
phase, we run our unit tests in unit-test-job
. in deploy
phase, we deploy our application on Heroku in deploy-job
.
If all jobs in the previous phase pass, then we can advance to the next phase. This means that if the unit tests fail, the application will not be deployed, which is a good thing! We wouldn’t want to deploy our application if it was in bad shape.
You will notice that deploy
degree refers to two variables called $HEROKU_APP_NAME
and $HEROKU_PRODUCTION_KEY
. These are stored as CI/CD variables within GitLab.
If you’re setting this up in your GitLab account, you’ll need to find your API key in your Heroku account first. Within your Heroku account settings, you should see a section with your API key. If you haven’t generated an API key yet, please generate one now.
- Value for
$HEROKU_PRODUCTION_KEY
will be the API key from your Heroku account. - Value for
$HEROKU_APP_NAME
will be the name of your Heroku application.
My app name is heroku-gitlab-ci-cd-demo
but since app names are universally unique to Heroku, yours will be slightly different.
Let’s modify the code in our application. I made a simple change to the title text, changing the words from “Heroku and GitLab CI/CD demo” to “Heroku and GitLab CI/CD Rules!” You can make a similar change in your code.
Now add, commit and push that change to the main
branch office. This will start the GitLab CI pipeline. You can see the pipeline inside GitLab and see the progress in real time.
A good CI pipeline allows you to ship new features quickly and reliably without manually managing the deployment process. Heroku and GitLab CI/CD make a great team!