How to deploy a Laravel app to Heroku with GitLab CI/CD
By: Olivier Picault
Tags: Laravel, Gitlab Ci, Heroku, Gitlab

At this point I had several questions:

How to handle Laravel .env file in staging/production ?

How to launch tests every time a MR is open to our develop or master branch ?

How to run the database migrations when deploying ?

Laravel .env file

This one is easy. Heroku has a key/value system that acts as your .env file. All you need to do is filling it. You obviously have to do it for every environment or app you want to have.

To do so, go to your project then under the Settings tab click on Reveal config vars .

Heroku key/value system

Test

Here things are getting tricky.

Gitlab handle CI/CD processes through a .gitlab-ci.yml file that has to be a the root of your project. Let’s create it:

touch .gitlab-ci.yml

The first thing to do is to specify what Docker image we will be using:

image: php:7.2

In order to test our app we actually only need one line:

vendor/bin/phpunit tests

But as you can expect, it isn’t that easy.

The Docker image we got is “empty” for now. We thus need to install our app dependencies, configure it, etc. as you would do if you were to clone the project for the first time.

Gitlab CI/CD works with what is called jobs . As stated in the Gitlab CI/CD official documentation jobs are the most fundamental element of a Gitlab CI/CD pipeline. We are thus going to create a job which will:

Install git and composer

Install our app dependencies

Configure our Laravel app: create an .env file and generate an application key

file and generate an application key Run the tests

phpunit tests:

stage: test

script:

- apt-get update -qq

- apt-get install -y -qq git

- curl -sS https://getcomposer.org/installer | php

- php composer.phar install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts

- cp .env.example .env

- php artisan key:generate

- vendor/bin/phpunit tests

Few things here. The first line phpunit tests is actually the job’s name. You can put whatever you want here.

The second one defines at what stage the job should be run. GitLab CI/CD provides 3 stages: build , test and deploy . Build jobs will be run first then test jobs only if all the build ones succeeded, then deploy jobs only if the tests ones all succeeded. Note that jobs with the same stage will run in parallel. If you want to know more about jobs please refer to the official documentation.

Once done, git push it. Now back to GitLab head to CI/CD then Jobs . You should see a running job. You can click on it to see the details.

GitLab CI/CD jobs overview

Once finished, the status is set to passed which means the job has succeeded: the test job has completely run without any error hence the tests passed.

Deploy

Now that the tests are setup we want our app to be automatically deployed when the test stage is ok. We thus need to head back to our .gitlab-ci.yml file and create a new job. (There are probably different ways to do this but here is how I did it).

deploy to heroku:

stage: deploy

script:

- apt-get update -qq

- apt-get install -y -qq git

- apt-get install -y ruby

- gem install dpl

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

To deploy our app to Heroku we will be using DPL. It’s a tool created by the guys at Travis (remember I talked about them earlier in this post). To use DPL we need to install ruby so we can install gem. This is what the first two new lines of the job are doing. Only the last one is properly responsible for doing the deployment.

The cool thing about GitLab CI/CD is that you can use variables to not hardcode sensitive information. Here I am using two variables:

$HEROKU_APP_STAGING

$HEROKU_API_KEY

These are the app name on Heroku and the Heroku API key.

In order to create/edit them you have to go to Settings , CI/CD , then Variables .

GitLab CI/CD variables

The Heroku API key is a key tight to your Heroku account and can be found in Account settings , API Key . It allows third parties to access your Heroku account in a secure way.

Heroku API Key

As you can see we now have repeated instructions between the test and the deploy jobs. This can be easily avoid by using before_script . This will tell what commands to run before any job. This is thus how our .gitlab-ci.yml file looks like now:

image: php:7.2 before_script:

- apt-get update -qq

- apt-get install -y -qq git phpunit tests:

stage: test

script:

- curl -sS https://getcomposer.org/installer | php

- php composer.phar install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts

- cp .env.example .env

- php artisan key:generate

- vendor/bin/phpunit tests deploy to heroku:

stage: deploy

script:

- apt-get install -y ruby

- gem install dpl

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

Same as before. Git push the changes. You should now see a deploy job appeared after the test one succeeded.

Last but not least: the jobs we created will be triggered for every change in every branch. This is a behaviour you might not be happy with: you probably don’t want your app to be deploy when a new feature branch is pushed for example. Hopefully there is a way to restrict for which branch which job should be run. This is done thanks to the only keyword:

deploy to heroku:

stage: deploy

script:

- apt-get install -y ruby

- gem install dpl

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

only:

- master

Here the deploy job will only be run if a change is made on the master branch. You can specify more than one branch if wanted and if none is specified the job will run for all of them.

Heroku Procfile

If you go to your Heroku application you should have an 403 error. This is because you attempting to access the wrong folder. The Laravel application entry point is located at /public/index.php where we’re trying to launch the app from the root of our project which obviously doesn’t work.

Second thing there, there is currently no web server running.

These will be solved thanks to a Procfile file: it’s a Heroku specific file that contains commands to be executed when the app starts. More about it here.

Create a file name Procfile and the root of your project

touch Procfile

and add the following

web: vendor/bin/heroku-php-apache2 public/

This tells Heroku we want an apache web server to run when the application start + we specify the document root.

Git push the changes and reload your application. It should now be running.

Run the migrations

One cool thing about Laravel (others are probably doing this as well) is that you can define migrations which describe the database. When you want to create a new table for example you can write a migration file and then run it so the changes are applied.

For our project we wanted these to be automatically run when deploying as well. This is done thanks to Heroku Procfile.

We already have a web command. But this isn’t the only command you can use, there are several others. The one we need is release . This allow us to execute commands just before the releasing of our app. Exactly what we want! Here is the command:

release: php artisan migrate --force

The --force option allows us to bypass the confirmation prompt.

And this is DONE!