Skip to content

Instantly share code, notes, and snippets.

@webjay
Last active January 7, 2023 11:54
Show Gist options
  • Save webjay/3915531 to your computer and use it in GitHub Desktop.
Save webjay/3915531 to your computer and use it in GitHub Desktop.
Php hook script that can git pull, apc_clear_cache() etc
<?php
ignore_user_abort(true);
function syscall ($cmd, $cwd) {
$descriptorspec = array(
1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
2 => array('pipe', 'w') // stderr
);
$resource = proc_open($cmd, $descriptorspec, $pipes, $cwd);
if (is_resource($resource)) {
$output = stream_get_contents($pipes[2]);
$output .= PHP_EOL;
$output .= stream_get_contents($pipes[1]);
$output .= PHP_EOL;
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($resource);
return $output;
}
}
function git_current_branch ($cwd) {
$result = syscall('git branch', $cwd);
if (preg_match('/\\* (.*)/', $result, $matches)) {
return $matches[1];
}
}
// make sure the request is coming from GitHub
// https://help.github.com/articles/what-ip-addresses-does-github-use-that-i-should-whitelist
/*
$gh_ips = array('207.97.227.253', '50.57.128.197', '108.171.174.178');
if (in_array($_SERVER['REMOTE_ADDR'], $gh_ips) === false) {
header('Status: 403 Your IP is not on our list; bugger off', true, 403);
mail('root', 'GitHub hook error: bad ip', $_SERVER['REMOTE_ADDR']);
exit();
}
*/
// cd ..
// $cwd = dirname(__DIR__);
// GitHub will hit us with POST (http://help.github.com/post-receive-hooks/)
if (!empty($_POST['payload'])) {
$payload = json_decode($_POST['payload']);
// which branch was committed?
$branch = substr($payload->ref, strrpos($payload->ref, '/') + 1);
// If your website directories have the same name as your repository this would work.
$repository = $payload->repository->name;
$cwd = '/var/www/'.$repository;
// only pull if we are on the same branch
if ($branch == git_current_branch($cwd)) {
// pull from $branch
$cmd = sprintf('git pull origin %s', $branch);
$result = syscall($cmd, $cwd);
$output = '';
// append commits
foreach ($payload->commits as $commit) {
$output .= $commit->author->name.' a.k.a. '.$commit->author->username;
$output .= PHP_EOL;
foreach (array('added', 'modified', 'removed') as $action) {
if (count($commit->{$action})) {
$output .= sprintf('%s: %s; ', $action, implode(',', $commit->{$action}));
}
}
$output .= PHP_EOL;
$output .= sprintf('because: %s', $commit->message);
$output .= PHP_EOL;
$output .= $commit->url;
$output .= PHP_EOL;
}
// append git result
$output .= PHP_EOL;
$output .= $result;
// send us the output
mail('root', 'GitHub hook `'.$cmd.'` result', $output);
// if you use APC, especially if you use apc.stat=0, we should clear APC
// if (apc_clear_cache('opcode') == false || apc_clear_cache('user') == false) {
// mail('root', 'Unable to apc_clear_cache', '');
// }
}
}
?>
@webjay
Copy link
Author

webjay commented Oct 19, 2012

@gosuto-inzasheru
Copy link

Using this project you can also use ssh or ftp to sync remotely: https://github.com/Coppertino/github-webhook.

@joefresco
Copy link

All I ever get from this is a blank $result and no results from the git pull. I've even modified your code to help me try to diagnose the problem. I followed your guide but used my own name (smart) in place of www-data as that's who already owns the repo.

   $descriptorspec = array(
        1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
        2 => array('pipe', 'w')
    );
    $resource = proc_open($cmd, $descriptorspec, $pipes, $cwd);
    if (is_resource($resource)) {
        stream_set_blocking($pipes[2], 0);
        if ($err = stream_get_contents($pipes[2]))
        {
            return 'Process could not be started [' . $err . ']';
        }
        $output = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        proc_close($resource);
        return $output;
    } else {
        return "No resource provided.";
    }

@joefresco
Copy link

Figured it out. I had several things going on.

  1. Copied .ssh folder to /var/www so apache could connect to github
  2. git repo-config core.sharedRepository true
  3. chown/chmod g+rw .git folder specifically (-R . or -R * doesn't hit the .git folder)

I also modified the function above to have better error output if there is a fatal error in the command executed.

private function _syscall ($cmd, $cwd)
{
    $descriptorspec = array(
        1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
        2 => array('pipe', 'w') //stderr will pipe back
    );
    $resource = proc_open($cmd, $descriptorspec, $pipes, $cwd);
    if (is_resource($resource)) {
        stream_set_blocking($pipes[2], 0);
        if ($err = stream_get_contents($pipes[2]))
        {
            return 'Process could not be started [' . $err . ']';
        }
        $output = stream_get_contents($pipes[1]);
        if ($err = stream_get_contents($pipes[2]))
        {
            return $err;
        }
        fclose($pipes[1]);
        proc_close($resource);
        return $output;
    } else {
        return "No resource provided.";
    }
}

@dpashkevich
Copy link

@joefresco Or you can just use [suPHP|http://www.suphp.org/Home.html] to make your php script that does git pull execute under your user instead of apache. No more crazy chmod/chown :)

@webjay
Copy link
Author

webjay commented Jan 16, 2014

Thanks for your feedback @joefresco.

I have just recently done this again, and updated my guide.

@phedoreanu
Copy link

Hey guys, I've made a stripped down version https://gist.github.com/phedoreanu/11321236.

@cballou
Copy link

cballou commented Jul 9, 2014

It's worth noting that in your guide there's a more straightforward way to update your known_hosts file that more closely follows github documentation:

sudo -u www-data ssh -T [email protected]

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