Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?

Deploy your site with git securely

This gist assumes:

  • you have a local git repo
  • you have an online remote repository (github)
  • you have a server running Apache with git already installed

you should be able to do the same with Java, Perl, RoR, JSP etc. however you'll need to recreate the (rather simple) PHP script

1 - On your local machine

Here we add the deployment script and push it to the origin, the deployment script runs git commands to PULL from the origin thus updating your server

Grab a deployment script for your site

See deploy.php

Add, commit and push this to github

git add deploy.php
git commit -m 'Added the git deployment script'
git push -u origin master

2 - On GitHub

Set up service hook

  1. https://github.com/oodavid/server.com/settings/hooks
  2. Select Add webhook
  3. Enter the URL to your deployment script - http://server.com/deploy.php into Payload URL
  4. Generate a highly random string for the Secret
  5. You can keep the default for events that trigger the webhook (push event) or you can customize it yourself
  6. Click Add webhook

3 - On the Server

Here we clone the origin repo into a chmodded /var/www/html folder, you can change the path to your own path

Pull from origin

git clone git@github.com:you/server.git /var/www/html

You need to check that the permission for your deployment script is correct (644)

Rejoice!

Now you're ready to go :-)

Some notes

  • It would be trivial to setup another repo on your server for different branches (develop, release-candidate etc) - repeat most of the steps but checkout a branch after pulling the repo down
  • If you came from the fork you would notice that much had been removed and you will no longer be able to trigger deployment manually. This is changed to fit with github's recommended way of setting up webhooks but with php.
    • You will need to store the random key generated above in your .htaccess file as an environment variable.
    • Never hard code the random key into the code
  • You will not need to add the SSH key to github, that is not neccessary.
  • See Webhooks for more details.

Sources

<?php
/**
* GIT DEPLOYMENT SCRIPT
*
* Used for automatically deploying websites via github securely, more deets here:
*
* https://gist.github.com/limzykenneth/baef1b190c68970d50e1
*/
// The header information which will be verified
$agent=$_SERVER['HTTP_USER_AGENT'];
$signature=$_SERVER['HTTP_X_HUB_SIGNATURE'];
$body=@file_get_contents('php://input');
// The commands
$commands = array(
'git pull origin master',
'git submodule sync',
'git submodule update'
);
base64_encode($agent);
base64_encode($signature);
if (strpos($agent,'GitHub-Hookshot') !== false){
if (hash_equals($signature, verify_request())){
// Run the commands
foreach($commands AS $command){
// Run it
$tmp = shell_exec($command);
}
}else{
header('HTTP/1.1 403 Forbidden');
echo "Invalid request.";
}
}else{
header('HTTP/1.1 403 Forbidden');
echo "Invalid request.";
}
// Generate the hash verification with the request body and the key stored in your .htaccess file
function verify_request(){
$message = $GLOBALS['body'];
$key = $_ENV['GIT_TOKEN'];
$hash = hash_hmac("sha1", $message, $key);
$hash = "sha1=".$hash;
return $hash;
}
// Compares the hash given in the header and the one generated by verify_request()
// "==" is not recommended as it is prone to timing attacks
// This function is built into PHP 5.6++ so if you have it you can ommit the following function
function hash_equals( $a, $b ) {
$a_length = strlen( $a );
if ( $a_length !== strlen( $b ) ) {
return false;
}
$result = 0;
// Do not attempt to "optimize" this.
for ( $i = 0; $i < $a_length; $i++ ) {
$result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] );
}
return $result === 0;
}
echo "Deploy successful."
?>

sky4git commented Jul 22, 2015

verify_request() is generating different string than original secret. is there any reason for it?
That is the bit not working actually. Otherwise everything is fine.

sky4git commented Jul 22, 2015

verify_request() is not generating same string as secret set for webhook.
Any clue what would be the reason?

@sky4git - did you ever get a solution?

christianpetri commented Jul 13, 2016

Thank you for the deploy.php (worked for me :) )

move the echo "Deploy successful."; to (if you remove the function hash_equals(), it would otherwise always get echoed... php 5.5+ )

if (hash_equals($signature, verify_request())){ 
        ...
    echo "Deploy successful.";      
}else{

added to the .htaccess the line (replace Secret with strong Password):
SetEnv GIT_TOKEN Secret

modified for php 5.5++
(sidenote: added a 1 and 2 to the "Invalid request", so that I know which if was not passed)

<?php
    /**
     * GIT DEPLOYMENT SCRIPT
     *
     * Used for automatically deploying websites via github securely, more deets here:
     *
     *      https://gist.github.com/limzykenneth/baef1b190c68970d50e1
     */

    // The header information which will be verified
    $agent=$_SERVER['HTTP_USER_AGENT'];
    $signature=$_SERVER['HTTP_X_HUB_SIGNATURE'];
    $body=@file_get_contents('php://input');

    // The commands
    $commands = array(
        'git pull origin master',
        'git submodule sync',
        'git submodule update'
    );

    base64_encode($agent);
    base64_encode($signature);

    if (strpos($agent,'GitHub-Hookshot') !== false){
        if (hash_equals($signature, verify_request())){
            // Run the commands
            foreach($commands AS $command){
                // Run it
                $tmp = shell_exec($command);
            }
            echo "Deploy successful.";
        }else{
            header('HTTP/1.1 403 Forbidden');
            echo "Invalid request 1.";
        }
    }else{
        header('HTTP/1.1 403 Forbidden');
        echo "Invalid request 2.";
    }

    // Generate the hash verification with the request body and the key stored in your .htaccess file
    function verify_request(){
        $message = $GLOBALS['body'];
        $key     = $_ENV['GIT_TOKEN'];
            $hash    = hash_hmac("sha1", $message, $key);
            $hash = "sha1=".$hash;
            return $hash;
    } 


?>

rionda commented Jul 30, 2016

For reasons that are only partially clear to me, I had to change $key = $_ENV['GIT_TOKEN']; to $key = getenv('GIT_TOKEN');

For me, the if (hash_equals($signature, verify_request())){ keeps failing so I get the 403 error. I verified that the secret code in htaccess is the same as that used when setting up the webhook.

I did make the change rionda had to, changing $key = $_ENV['GIT_TOKEN']; to $key = getenv('GIT_TOKEN'); but either way that I had it it didn't work.

The weird thing is that it did work for about 8 times in the middle of about 30 times that the webhook was called, but I haven't been able to figure out what was different then.

I'm hitting a wall on troubleshooting this...any suggestions?

webassic15 commented Mar 30, 2017

Hi, i am using Ubuntu OS, i cloned repo from bitbucket on local env & set the ssh key access. if i tried to pull / push via terminal then all is working fine. but if i run that script on my local then getting a error : Host key verification failed.
fatal: Could not read from remote repository.

Also whoaim command showing a user - "www-data" but i am using "web-dev" user on my system.

Please help to resolve this issue.

I'm having the same problem as shunluoifong. Don't have time to mess around with it for now, so I just added $key='my secret'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment