Paulund
2017-05-30 #laravel

Deploying With Envoy

In this tutorial we're going to look at how you can use Envoy to make it very easy to deploy your Laravel applications. Envoy provides a nice blade style syntax way of running commands on your remote servers. You can use it for multiple purposes but the most common way I've found is for deployments. It allows you to enter the command envoy run deploy to quickly deploy your code.

Installation

Before you start using Envoy on your Laravel application you first need to install it by using composer.


composer global require "laravel/envoy=~1.0"

Introduction

To get started with Envoy you need to create a file at the root level of your project called Envoy.blade.php Now you can use the normal Laravel blade syntax to define the commands you want to run on the remote server. Everything you do in Envoy needs to be done by tasks. You can also define different servers by using @servers. So, if you want to run a command on a remote server to list the files on the server you can use the following code.


@servers(['web' => ['[email protected]']])

@task('foo', ['on' => 'web'])
    ls -la
@endtask

This defines a new server called web with a task called foo. You can run this command at the root of project by running the command


envoy run foo

If you want to always run some code before you run through the tasks you can use a @setup directive to define variable you can use in your tasks.


@setup
    $now = new DateTime();

    $environment = isset($env) ? $env : "testing";
@endsetup

If you want to pass in different variables into your script you can define this on the command line when calling envoy, for the branch of passing the environment you want to deploy you can use the command.


envoy run deploy --env=production

You'll now be able to pick up this variable in your Envoy script by using the PHP syntax $env. You can also group tasks together by creating stories, for example when you run a deploy script you want to get the code from git and then run an install on composer to bring in the dependencies and you will define them with the following.


@servers(['web' => '192.168.1.1'])

@story('deploy')
    git
    composer
@endstory

@task('git')
    git pull origin master
@endtask

@task('composer')
    composer install
@endtask

Creating A Deployment Script

Now that we have an understanding of how Envoy works we can create a deployment script, the deployment script will need to do multiple activities. - Clone git repository into a new folder

  • Change permissions on storage folder
  • Apply the correct environment file
  • Install composer dependencies
  • Run migration files
  • Restart any queue runners
  • Run artisan optimize
  • Symlink the new release folder with the current version of the site
  • Clean up old code

Setup The Deployment

In the setup section, we need to define the server we want to use, the repository we want to use, the path to deploy into, the timestamp of the deployment, the environment, the branch of the repository and the release folder.


@setup
if(!isset($user))
{
    throw new Exception('User option not set.');
}

$server = $user . '@10.0.4.54';
$repo = '[email protected]:paulund/vagrant-ubuntu-nginx.git';
$path = '/var/www/website';

if ( substr($path, 0, 1) !== '/' ) throw new Exception('Careful - your deployment path does not begin with /');

$date = ( new DateTime )->format('YmdHis');
$env = isset($env) ? $env : "production";

$branch = isset($branch) ? $branch : "master";
$path = rtrim($path, '/');
$release = $path.'/'.$date;
@endsetup

Create Deploy Story

We're going to create a deploy story which will perform all the tasks that we need to do during the deployment.


@story('deploy')
deployment_start
deployment_links
deployment_composer
deployment_migrate
deployment_queue
deployment_cache
deployment_optimize
deployment_finish
deployment_cleanup
@endstory

At the start of the deployment, we need to clone the repository from the required branch into the release folder.


@task('deployment_start')
cd {{ $path }};
echo "Deployment ({{ $date }}) started";
git clone {{ $repo }} --branch={{ $branch }} --depth=1 -q {{ $release }} ;
echo "Repository cloned";
@endtask

After the code has been cloned into the new folder then we can change the permissions of the bootstrap and the storage folder. We'll then take the env variable and copy the required environment to the .env file.


@task('deployment_links')
cd {{ $release }};
chmod 777 bootstrap/*
echo "Bootstrap permissions done";

chmod 777 -R {{ $release }}/storage
echo "Changed storage permission"

cp {{ $release }}/.env.{{ $env }} {{ $release }}/.env
echo "Change Environment link"
@endtask

Then we can install the composer dependencies inside the release folder.


@task('deployment_composer')
cd {{ $release }};
composer install --no-interaction --quiet;
@endtask

We're able to update the database with any new changes by running the migration files for the correct environment.


@task('deployment_migrate')
php {{ $release }}/artisan migrate --env={{ $env }} --force --no-interaction;
@endtask

If you're running any queue workers then you may need to restart the queues.


@task('deployment_queue')
php {{ $release }}/artisan queue:restart --no-interaction;
@endtask

You will need to clear your application cache to make sure everything has refreshed correctly.


@task('deployment_cache')
php {{ $release }}/artisan view:clear --quiet;
php {{ $release }}/artisan cache:clear --quiet;
php {{ $release }}/artisan config:cache --quiet;
echo 'Cache cleared';
@endtask

You can optimize the class loader by running the following command.


@task('deployment_optimize')
php {{ $release }}/artisan optimize --quiet;
@endtask

At the end of the deployment you can add the symlink from the release folder to the current folder


@task('deployment_finish')
ln -nfs {{ $release }} {{ $path }}/current;
echo "Deployment ({{ $date }}) finished";
@endtask

As you clone into a new release folder these can add up over time and you can use the following command to remove any old folders.


@task('deployment_cleanup')
cd {{ $path }};
find . -maxdepth 1 -name "20*" -mmin +2880 | head -n 5 | xargs rm -Rf;
echo "Cleaned up old deployments";
@endtask

To run this envoy script you can use the command


envoy run deploy --env=production --user=paulund

To deploy your application onto your server, if you also want to use this for your UAT server then you simply need to add the server to the web server array and change the environment variable you pass into the script.