©2017 by Michael Duquette. Proudly created with

  • Michael Duquette

**Gitlab Review Apps with Heroku**

First a couple of assumptions: you currently have an app setup that deploys from Gitlab to Heroku. You have variables setup in gitlab under CI/CD for your HEROKU_API_KEY and your organization has identified a need to review code before submitting to your QA or pre-production environment. With the help of the gitlab-ci.yml file we can setup a review environment that will automatically build, deploy and tear down a review environment.

So how do we accomplish this? Magic! Well, OK maybe not magic but it’s a pretty slick process of using branches and the Heroku API. Ready to take a peak behind the curtain? Let’s get started!

**Heroku setup steps:**

There aren’t any! All the work to be performed is done within git. The review app (test app or whatever you want to call it) is all handled in the gitlab-ci.yml and by creating a branch of your repository.

**Gitlab setup steps:**

We need to make a few modifications to the .gitlab-ci.yml. Open the .gitlab-ci.yml in your favorite text editor. I prefer to use DPL to deploy from Gitlab to Heroku. The syntax is easy to follow and much cleaner.

Add this near the top of the YAML:


- apt-get update -qy

- apt-get install -y ruby-dev curl

- gem install dpl

This tells gitlab to check for updates, install ruby (needed for dpl) and install dpl.

I like to use a variable for passing around the review app name. Add a variable section to the YAML like this:



The $CI_COMMIT_REF_SLUG is the name of the branch the commit was performed from and the $CI_COMMIT_SHORT_SHA is the 7 digit commit id. Now we can use REVIEW_APP_NAME wherever we need to.

***Note that using the $CI_COMMIT_SHORT_SHA will allow multiple review apps to run without collision.

Now let’s look at the stages section of the YAML. This is what mine looks like:


- review

- staging

- production

On Heroku I have both a staging and a production app defined and the push to both is based on the branch I am working in. I also created a pipeline in Heroku that both apps are assigned to. With a click of a button I can promote the app in staging to production. Add the – review to your stages and we’ll setup the start_review and stop_review sections of the YAML next.

**Review Magic:**

Copy and paste the following into your YAML below the stages section:


stage: review


- cd ./frontend


- >-




-d '


"name": "'"$REVIEW_APP_NAME"'",

"region": "us"



-H "Content-Type: application/json"

-H "Accept: application/vnd.heroku+json; version=3"

-H "Authorization: Bearer $HEROKU_API_KEY"

- dpl --provider=heroku --app=$REVIEW_APP_NAME --api-key=$HEROKU_API_KEY


name: review/$CI_COMMIT_REF_NAME

url: https://$CI_COMMIT_REF_SLUG-$

on_stop: stop_review


- branches


- master

- staging

Let’s review what’s going on here. First we are specifying that this is a review stage. Since I am deploying an Angular app on Heroku and have both a frontend and a backend in my repository (I know nested repositories aren’t best practice but it’s what I have to work with on this project) I first cd into my frontend folder so that the deployment takes place from there. You may not need to do this based on your configuration. I echo the $REVIEW_APP_NAME so that it is in the gitlab logs when the pipeline is run. The curl (Client URL) statement creates the API Post request to create the empty app named $REVIEW_APP_NAME. The – dpl statement performs the gitlab deployment to the newly created Heroku App. The environment settings are for gitlab and define what happens once the deployment completes. The on_stop section calls the stop_review section of the YAML. You will notice I tell gitlab that only branches are used and to exclude the master and staging branches.

Now copy this into the YAML:


stage: review


- echo "environment is being stopped!"

- >-




-H "Content-Type: application/json"

-H "Accept: application/vnd.heroku+json; version=3"

-H "Authorization: Bearer $HEROKU_API_KEY"



when: manual


name: review/$CI_COMMIT_REF_NAME

action: stop

This is all pretty straight forward. The stop_review only runs on the review stage. The curl statement creates and passes the DELETE API call to delete the app named $REVIEW_APP_NAME. It is processed manually either by stopping the job in the gitlab pipeline or stopping it in the environments section of the project repository. Once you click ‘stop’ the delete command is sent to Heroku and the app is torn down and deleted.

Now save the YAML and open up a terminal session. Create a review branch and push it up to the upstream repository:

git checkout -b review

git add .

git commit -m ‘Review App setup’

git push -u origin review

Log back into Gitlab and check your repository pipelines. You should see your latest job running. Log into Heroku and check your dashboard. You will see the new deployment popup once Gitlab finishes processing it. After reviewing the app on Heroku when you are ready to tear it down go back in to gitlab and either go to the Operations/Environment section of the repository or the CI-CD/Pipelines section.

If you choose to stop the app under Operations/Environments you will see the review app listed there/ On the right hand side of the screen will be a big red stop square. Click the square and answer the popup confirmation prompt to stop the app. Confirm the app has been closed/torn down by checking on Heroku (you may need to refresh your browser to see it drop off).

If you choose to stop the app under the CI-CD/Pipeline you will see the pipeline running. Click the running stage under the Stages column and click the stop button to the right of stop_review. Confirm the app has been closed/torn down by checking on Heroku (you may need to refresh your browser to see it drop off).

There, now you magically have a review app that get’s served and torn down automatically.

#CS@Worcester #CS-448

So, you’ve built this really awesome Angular app, now what? Oh, you want to deploy it using Heroku. Easy peasey but there are a few gotcha’s to look out for. Running an Angular app locally we just run:

ng serve -o

But Heroku doesn’t work that way it needs to know how to serve up the app. We need to install a couple of packages and modify our project to make this work. The following steps should get the app up and running on Heroku just fine.

First let’s make sure you are running the latest version of Angular CLI and the Angular Compiler CLI. Open a terminal session in the root folder of your project and run this:

npm install @angular/cli@latest @angular/compiler-cli --save-dev

So what did we just do? We installed (or updated) Angular CLI and the Angular CLI Compiler to the latest version the

--save-dev added the necessary entries to the package.json which we now need to modify. Open the package.json with your favorite editor and scroll down to the devDependencies section. Move these two entries up into the Depencies section:

"@angular/cli": "^7.3.9",

"@angular/compiler-cli": "^7.2.15",

Pay attention to the formatting and make sure you have commas where commas are needed etc. Notice the ^ symbol before the version numbers? That indicates that the version number shown is the lowest version that can be used but higher versions are allowed. Since this is an Angular app we also want to move the typescript entry up from the devDependecies to the Dependencies section. In my package.json it looks like this:

"typescript": "~3.2.2",

Now we need to tell Heroku how to build the app when it’s deployed. In the scripts section of the package.json add the following line:

"postinstall": "ng build --aot --prod"

This tells Heroku to use the Ahead Of Time compiler and make the app production ready. This will create the /dist folder where all the javascripts and html are launched from.

Ok now back in terminal type in:

npm -v followed by node -v

Make a note of the version numbers for both. We need to create an entry at the bottom of the package.json to tell Heroku what engines to use. Format the engine entry like this:

"engines": {

"node": "8.9.0",

"npm": "6.10.2"


Paste this into the package.json right after the devDepencies section. Be sure to add a comma after the closing } for the devDepencies section. My package.json is pasted way below as an example. Since we can’t run ng serve on Heroku we need to a way to serve the app. We’ll install Express server to handle that for us. Back in terminal run:

npm install express path –save

This will install Express and create the entry in the package.json for us. Verify that the entry has been created in package.json it should like similar to mine:

"express": "^4.17.1",

Now go back to terminal and type in:

touch server.js

This creates an empty server javascript file. Open server.js with your favorite editor and paste the following into it:

//Install express server

const express = require('express');

const path = require('path');

const app = express();

// Serve only the static files form the dist directory

app.use(express.static(__dirname + '/dist/your-app'));

app.get('/*', function(req,res) {



// Start the app by listening on the default Heroku port

app.listen(process.env.PORT || 8080);

Replace your-app with the name of your app. (you will use the name of the sub-folder located in /dist )

Now back in package.json modify (or add) the start entry located in the scripts section to this:

"start": "node server.js"

Now save your changes and push them back Gitlab:

git add . git commit -m "updates to deploy to heroku" git push

Following your push check your pipelines and see if the deployment to Heroku was successful. If so go to Heroku and check out your awesome app! If not rewind, start from the top , and double check everything.

My package.json:


"name": "my-app",

"version": "0.0.0",

"scripts": {

"ng": "ng",

"start": "node server.js",

"build": "ng build",

"test": "ng test",

"lint": "ng lint",

"e2e": "ng e2e",

"postinstall": "ng build --aot --prod"


"private": true,

"dependencies": {

"@angular/animations": "^7.2.15",

"@angular/cli": "^7.3.9",

"@angular/common": "^7.2.15",

"@angular/compiler": "^7.2.15",

"@angular/compiler-cli": "^7.2.15",

"@angular/core": "^7.2.15",

"@angular/forms": "^7.2.15",

"@angular/platform-browser": "^7.2.15",

"@angular/platform-browser-dynamic": "^7.2.15",

"@angular/router": "^7.2.15",

"bootstrap": "^3.3.7",

"core-js": "^2.6.10",

"express": "^4.17.1",

"rxjs": "~6.3.3",

"tslib": "^1.10.0",

"typescript": "~3.2.2",

"zone.js": "~0.8.26"


"devDependencies": {

"@angular-devkit/build-angular": "^0.13.9",

"@angular/language-service": "^7.2.15",

"@types/jasmine": "~2.8.8",

"@types/jasminewd2": "^2.0.8",

"@types/node": "~8.9.4",

"codelyzer": "~4.5.0",

"jasmine-core": "~2.99.1",

"jasmine-spec-reporter": "~4.2.1",

"karma": "~4.0.0",

"karma-chrome-launcher": "~2.2.0",

"karma-coverage-istanbul-reporter": "^2.0.6",

"karma-jasmine": "~1.1.2",

"karma-jasmine-html-reporter": "^0.2.2",

"protractor": "~5.4.0",

"ts-node": "~7.0.0",

"tslint": "~5.11.0"


"engines": {

"node": "8.9.0",

"npm": "6.10.2"



#CS@Worcester #CS448

  • Michael Duquette

Updated: Nov 17, 2019

This week let's take another peek at the FoodKeeper API. We’ll review the endpoints and how to submit a request to retrieve data. Before we jump into this you need to download and install a REST client. I highly recommend Insomnia, the app not the sleep disorder, (download it here) I’ll wait…

Now that you have Insomnia installed let’s talk about endpoints. Last week we covered the four primary HTTP verbs used with REST. Did you have a chance to visit despite the name of the website these aren’t tutorials on how to make REST calls but more like development guidelines for developers writing REST API’s. So why did I recommend it? Because understanding how a good RESTful API is designed will make it easier to craft your REST calls. So the 4 basic HTTP verbs (POST, GET, PUT and DELETE) are CRUD (Create, Read, Update, Delete) operations.

· POST = Create

· GET = Read

· PUT = Update

· DELETE = Delete

Kind of makes sense, right?

These also make up the endpoints you access with your REST client. In JAVA we add an annotation to the class and the getter and setter methods to define the endpoint. For the FoodKeeper API we are using the Spring Framework ( to help setup the Rest components.

After all the imports are declared the class must be annotated with @RestController so that the compiler knows how to process it. Each endpoint must be annotated to reflect what it does. A POST endpoint would be annotated like this:


The part in parenthesis indicates the URL path that you would pass as part of the REST Client call and is the actual endpoint. Let’s look at an online example. In a new tab on your browser go to:

The site lists 5 public endpoints you can practice against:

· GET /employee

· GET /employee/{id}

· POST /create

· PUT /update/{id}

· DELETE /delete/{id}

Note the curly braces with id in between them – this indicates that you will be passing a value. Open your REST client, in Insomnia you would type in the URL in the White field in the top center of the screen and select the REST Method to the left of it:

Click the Send button and watch the Right hand column you will see it populate with employees. This is the first entry in the list that was returned for me:

Play around and get familiar with your REST Client and hit some of the other endpoints at

TIP: For the endpoints above with {id} just append the url string with a number like this:

All right go play and learn something!

#CS@Worcester #CS448