How-to-automate-deployment-using-git-and-webhooks

I gave up, x
Greetings humans (I am not a bot 😶) hope you are interfacing properly?

Follow me on a journey as I show you a simple way of continuous deployment on a Laravel project using git and some other things (just read on).

For our tutorial, we’re going to need a few things before we proceed:

  • A Laravel project
  • Some version of git (Github would be used for this tutorial, but the process is just about the same)
  • Access to a server (You can test locally tho but it won’t be the same)
  • Eyes
  • Fingers
  • A brain
  • ok I’ll stop now…
    Let us start from the beginning, a laravel project

Very beautiful framework, (no regrets about cheating on asp.net [this is not a confession 😶]).

The first thing we want to do on our project is use composer to install symfony/process to our project, we are going to need this package later.

You can do that by running this simple command at the root of your project

1
$  composer require symfony/process

This would load the required loadables to your composer.json file, in order to add the package to your project you just simply run this comman

1
$ composer update

That should add the package to your vendors folder and generate the required classes and so on.

Here is a link to the Symfony docs where you can read up more about that.

After this is done, we are going to need a shell script that is going to hold the sauce to our magic.

This is how our shell script looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh
# activate maintenance mode
php artisan down
# update source code
git pull
# update PHP dependencies
composer install --no-interaction --no-dev --prefer-dist
# --no-interaction Do not ask any interactive question
# --no-dev Disables installation of require-dev packages.
# --prefer-dist Forces installation from package dist even for dev versions.
# update database
php artisan migrate --force
# --force Required to run when in production.
# stop maintenance mode
php artisan up

We can call this file deploy.sh

The truth is that this is just a template, you can modify this script to suit whatever needs you might have.

Now you have to make this script executable

1
$ sudo chmod +x deploy.sh

Depending on your production environment, this method is very risky so if you’re one of those “safety” freaks, just clap for me and move on…

But if you are one with the force (Linux) please proceed, it only get’s interesting from here.

Now we have our script ready, we would need to prepare for our git webhook.

On GitHub, on your repository page, select the Settings tab, then Webhooks in the left navigation. Or go directly to the URL:

1
https://github.com/<your account>/<your repository>/settings/hooks

Click Add Webook:

Now we would need to add this webhook to our project (this is where it gets fun)

Firstly we need to add our secret to the project or in Layman’s terms we need make or project understand that there is a secret that a url needs before we proceed.

In config/app.php, add this line:

1
'deploy_secret' => env('APP_DEPLOY_SECRET'),

In your .env file add your webhook secret:

1
APP_DEPLOY_SECRET=changemenoworfacetheconsequences

Now we’re done with the manual part, let’s write some codes

We need to make a controller which would house our logic and process for making our deploy process run. Now let’s make our controller…

1
$ php artisan make:controller DeployController

I’m just going to call this controller DeployController for simplicity sake.

Then we would add all our code, don’t worry I’ll explain most of it. At the end our controller should look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Symfony\Component\Process\Process;
class DeployController extends Controller
{
public function deploy(Request $request)
{
$githubPayload = $request->getContent();
$githubHash = $request->header('X-Hub-Signature');
$localToken = config('app.deploy_secret');
$localHash = 'sha1=' . hash_hmac('sha1', $githubPayload, $localToken, false);
if (hash_equals($githubHash, $localHash)) {
$root_path = base_path();
$process = new Process('cd ' . $root_path . '; ./deploy.sh');
$process->run(function ($type, $buffer) {
echo $buffer;
});
}
}
}

Before I proceed, clap for me, it’s not easy to indent your code here on medium.com.
The code above does the following:

  • Makes sure the post request it coming from GitHub using the X-Hub-Signature unique to github. You can remove this particular verification if you’re feeling adventurous but I recommend you keep it.
    You can always refer to the git version control system documentation you are using for their own X-Signature

  • Makes sure the post request is coming from your github repo by verifying your deploy secret (in a production environment there are other checks before and after this, so don’t bother much about how flimsy the security might look)

  • Uses the symfony process to run the deploy script at the root of the project path in a shell environment

That’s the basic gist about the code above, let’s proceed to adding a route to the webhook we added to github (or whatever proper sounding English that fits, English is hard)

Navigate to route/web.php in your project and add this line

1
Route::post('deploy', 'DeployController@deploy');

The method for this route has to always be a post method because github sends only post requests to webhooks, so you can call this another check if you want.

Secondly after this, to prevent CSRF token validation errors, we add the route above to our excepted route in the Middleware/VerifyCsrfToken.php

Which when done should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'/deploy',
];
}

After this, on your server change the unix group of your project folder to www-data. This is necessary to allow the shell script to run in peace (allow the www-data user to update the project folder) this can be simply done by:

1
$ sudo chgrp -R www-data .

Then after all this, you are done.

With this now you have successfully set up a simple autodeployment (coughs Continuous Deployment) process on your project using git (while Jenkins and Travis are having some alone time).

If you’ve made it this far, Congratulations!!! You made it through series of bad jokes and hopefully learnt something, please a round of applause for yourself (I mean that clap button 😐)

https://medium.com/@gmaumoh/laravel-how-to-automate-deployment-using-git-and-webhooks-9ae6cd8dffae