Okay, at this point we already achieved a lot. Now, we are just showing off. Wouldn't it be great if you wouldn't have to build and deploy your application? If it would just magically do it all automatically.
We can achieve this using GitLab and CI. It is common to manage code using a git repository. After this tutorial, our applications will automatically build and deploy whenever we make a commit to the master branch. I will not cover the basics of git here, for that I recommend this series by atlassian.
To complete this tutorial you will need a GitLab account and push this project into a new repository. Again, we could achieve the same by using bitbucket or GitHub. GitLab is just my personal preference.
After setting up your GitLab project you will find a CI/CD tab on the left sidebar. This basically enables us to run a script that, if it succeeds deploys our app. If you want to lear more about CI and CD have a look here.
GitLab automatically looks for a .gitlab-ci.yml file in the project root directory. If present it will run a pipeline. Create this file and add the following code, make sure to replace all placeholders.
variables: APP_NAME_FRONTEND: <YOUR FRONTEND APP NAME> APP_NAME_BACKEND: <YOUR BACKEND APP NAME> stages: - build - deploy Build-Deploy-Frontend-to-Heroku: stage: build image: docker:latest services: - docker:dind only: - master script: - echo "Deploying Frontend..." - docker login -u _ -p $HEROKU_API_KEY registry.heroku.com - docker build --file=frontend/frontend.dockerfile --rm=true -t registry.heroku.com/$APP_NAME_FRONTEND/web . - docker push registry.heroku.com/$APP_NAME_FRONTEND/web - docker run -e HEROKU_API_KEY=$HEROKU_API_KEY wingrunr21/alpine-heroku-cli:latest container:release web -a $APP_NAME_FRONTEND - echo "Frontend Deployment Complete!" Build-Backend: stage: build image: gradle:6.0.1-jdk13 before_script: - export GRADLE_USER_HOME=`pwd`/backend/.gradle script: - echo "Building Backend..." - cd ./backend - gradle build - echo "Backend Build Complete!" cache: key: "$CI_COMMIT_SHA" policy: push paths: - backend/build artifacts: paths: - backend/build/libs/*.jar expire_in: 1 week only: - master Deploy-Backend-to-Heroku: stage: deploy image: docker:latest services: - docker:dind only: - master script: - echo "Deploying Backend..." - docker login -u _ -p $HEROKU_API_KEY registry.heroku.com - docker build --file=backend/backend.dockerfile --rm=true -t registry.heroku.com/$APP_NAME_BACKEND/web . - docker push registry.heroku.com/$APP_NAME_BACKEND/web - docker run -e HEROKU_API_KEY=$HEROKU_API_KEY wingrunr21/alpine-heroku-cli:latest container:release web -a $APP_NAME_BACKEND - echo "Backend Deployment Complete!"
Let's go through each part of the file.
These variables will be used in the other parts.
Each block below stages (Build & Deploy Frontend to Heroku, Build Backend, Deploy Backend to Heroku) is a job to GitLab. Each job belongs to one of the following stages
- build → this is run in parallel by GitLab and should be used to build the front- and backend
- test → test the application, we don't do this here
- deploy → If all jobs passed the deploy stage to deploy our app is executed
We have only the two stages build and deploy here.
Build & Deploy Frontend to Heroku
This job builds and deploys our frontend to Heroku. It would be cleaner to have two separate jobs to build and deploy the frontend. However, I' satisfied with it being this way for now.
- As the job does both it doesn't really matter if we set the stage to build or deploy
- As we will execute docker commands we use the docker image as a base for or job with the docker in docker (dind) service
- The job should only be executed on the master branch
The script contains
- The login to our Heroku account - as we did in the last tutorial
- Building the docker image (remember the build process of the frontend is part of the dockerfile) - as we did in the last tutorial
- Pushing the image to Heroku - as we did in the last tutorial
- Lastly we release the app. What's new here is that we use a predefined image as we need the heroku cli.
This job builds our backend to Heroku.
- As the job build the backend, the stage is set to build
- We will need gradle, so we select the image needed
- Next we need to tell gradle where its home directory is
The script contains
- Navigating into the backend folder
- Building the app using gradle
- Since we are building a .jar file, we declare it as artefact so we could download it from gitlab if needed. We also set it to expire in one week
- We also cache the artifact
- Lastly, we specify that the job should only be executed on the master branch
Deploy Backend to Heroku
- This is where we depoy the backend
- This is basically the same procedure as for the frontend
Setting the Heroku API Key
In our gitlab-ci file we reference to the heroku API key. We wouldn't want to store this in our file for security reasons. That is why we store it as a gitlab variable. Go to settings → CI /CD and then to variables to create a new one.
If you commit this file to your gitlab repository you will see a new pipeline running your jobs. After there are all done your application was deployed successfully to heroku. From now on you can develop locally using and then build and deploy your app by simply committing your changes to the master branch.
The complete code after completing all parts can be found here.