Github webhook PHP script for automated git pull on linux server

If you have a php website repository on Github and want to immediately deploy it to your server directory whenever you push to that repository from anywhere, then you should consider setting up a webhook in Github that will be triggered whenever anything is pushed to the repository. The webhook is just a php script that will be executed on your server and it will perform a git pull on the repository cloned on your server. You can clone this repository in a webroot so that whenever you push, your changes are immediately reflected on the server.
Following is a php script I use to automate this task. Several things to note before using this script :

  • Script assumes that your Github repository is cloned on your server
  • You may need to install sendmail on linux server since the script sends an email report to you whenever a hook is triggered
  • You need to give read/write access to the server directory where git pull will take place to the user your apache (or any other) server is running as (usually ‘daemon’ on ubuntu server or ‘www’)
  • You need to Setup a ‘Secret’ which is like a passphrase by going to Github–>Your Repo–>Settings–>Webhook. You will also need to configure this secret in the php script or as an environment variable on your server. This is used to authenticate and validate requests from Github and deny any other malicious attempts at triggering your webhook endpoint script

Without further ado, here is the php script code and the corresponding configuration file. Make sure to place the configuration file outside of your webroot so that it is not accessible from outside.

 

Github webhook PHP script :

<?php
/*
 * PHP endpoint for github webhook
 *
 * original: https://help.github.com/articles/post-receive-hooks
 * modified by dynamicguru.com
 */

 // Configuration :
$error_email = "youremail@domain.com"; // The email address to send report to in case an error occurs while executing this script
$php_script_information = "Email triggered by : ".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
$script_email_name = "Github webhook pull script";  // The name of sender used in email reports that will be sent

function run() {
    global $php_script_information;
    global $script_email_name;    
    
    // Configuration :
    $secret = "SECRET_PASSPHRASE";  // The secret set in github->repo->Settings->Webhooks. You should ideally read this from $_ENV (set it using 'export' command on a linux machine)
    $commandsToRun = 'cd /path/to/server/repository; git pull';
    $config_filename = '/path/to/config.json';  // path to the configuration file, recommended to place it outside your website root (htdocs/www etc)
    
    
    if (!file_exists($config_filename)) {
        throw new Exception("Could not find ".$config_filename);    // raise an Exception if configuration file is not found
    }

    $config = json_decode(file_get_contents($config_filename), true);   // parse the json configuration



    $postBody = $_POST['payload'];  // the payload sent by github
    $payload = json_decode($postBody);  // decode the payload sent by github, see https://developer.github.com/webhooks/#payloads
    $headerSignature = $_SERVER['HTTP_X_HUB_SIGNATURE'];    // the signature/hash of request sent by github
    $signature = 'sha1=' . hash_hmac('sha1', file_get_contents('php://input'), $secret);    // compute the signature/hash of request

    if (isset($config['email'])) {  // if an email address is set in config file, we'll send the report to that address
        $headers = 'From: '.$script_email_name."<".$config['email']['from'].">\r\n";
        $headers .= 'CC: ' . $payload->pusher->email . "\r\n";
        $headers .= "MIME-Version: 1.0\r\n";
        $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";  // HTML email
    }


    if (hash_equals($signature, $headerSignature)) {    // validate if this is a legit request from Github
        foreach ($config['endpoints'] as $endpoint) {
            
            if ($payload->repository->url == 'https://github.com/' . $endpoint['repo']
                && $payload->ref == 'refs/heads/' . $endpoint['branch']) {  // check if the push came from the right repository and branch
                // execute pull script, and record its output
                ob_start();
                
                passthru($commandsToRun); // the commands to run (navigate to your directory, pull from git etc)
                $output = ob_get_contents();    // get output of commands executed
                
                
                if (isset($config['email'])) { // prepare and send the notification email
                    // build the email body, you can add more details to this by referring to the json payload that is sent by github (https://developer.github.com/webhooks/#payloads)
                    $body = 'Request by Github from IP : '.$_SERVER['REMOTE_ADDR']
                    .'<p>User <a href="https://github.com/'. $payload->pusher->name .'">@' . $payload->pusher->name . '</a>'
                    . ' has pushed to ' . $payload->repository->url
                    . ' and consequently, ' . $endpoint['action']
                    . '.</p>';
                    $body .= '<p>Here\'s a brief list of changes:</p>';
                    $body .= '<ul>';
                    foreach ($payload->commits as $commit) {    // add each commit information
                        $body .= '<li>'.$commit->message.'<br />';
                        $body .= '<small style="color:#777"> Added: <b>'.count($commit->added)
                            .'</b> &nbsp; Modified: <b>'.count($commit->modified)
                            .'</b> &nbsp; Removed: <b>'.count($commit->removed)
                            .'</b> &nbsp; <a href="' . $commit->url
                            . '">...</a></small></li>';
                    }
                    $body .= '</ul>';
                    $body .= '<p>Pull script output : </p>';
                    $body .= $output. ' ';
                    $body .= $php_script_information;
                    mail($config['email']['to'], $endpoint['action'], $body, $headers); // send out the email
                }
                return true;
            }
        }
    } else {
        throw new Exception("This does not appear to be a valid request from Github.\nComputed signature was : ".$signature."\n  Github sent : ".$headerSignature." ");
    }
}




try {
    if (!isset($_POST['payload'])) {
        echo "I do nothing when loaded directly :)";
    } else {
        run();
    }
} catch ( Exception $e ) {
    $msg = $e->getMessage();
    mail($error_email,  // the address to send email to
         'Error occured while executing Github webhook endpoint',   // email subject
         $msg."\r\n".$php_script_information,   // the email body
         "From: ".$script_email_name."<script@yourdomain.com>\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=ISO-8859-1\r\n"    // headers
    );
}

Configuration json file :

{
    "email": {
        "from": "script@yourdomain.com",
        "to": "you@yourdomain.com"
    },
    "endpoints": [
        {
            "repo": "github-username/repository-name",
            "branch": "master",
            "action": "Something changed on the server"
        }
    ]
}



For more information, see https://developer.github.com/webhooks/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

2017 All rights reserved | Powered by WordPress
Back to top
Theme by dynamicguru.com