Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
GitHub PHP webhook to auto-pull on repo push
<?php
// Use in the "Post-Receive URLs" section of your GitHub repo.
if ( $_POST['payload'] ) {
shell_exec( 'cd /srv/www/git-repo/ && git reset --hard HEAD && git pull' );
}
?>hi
@cowboy

This comment has been minimized.

Show comment Hide comment
@cowboy

cowboy Oct 11, 2010

This seems to be the bare minimum required to get a GitHub post-receive hook up and running with PHP on a webserver, to facilitate automatic site updates with group collaboration on a small static site.

I had to create a private/public key pair, without passphrase. The public key gets added in GitHub as a "deploy key" while the private key gets saved as /var/www/.ssh/id_rsa. I also did sudo touch /var/www/.ssh/known_hosts && sudo chmod www-data /var/www/.ssh/known_hosts, then ran sudo -u www-data git pull from the command line to update the known_hosts file.

(I think that's it)

Have any improvements? Let me know!

Owner

cowboy commented Oct 11, 2010

This seems to be the bare minimum required to get a GitHub post-receive hook up and running with PHP on a webserver, to facilitate automatic site updates with group collaboration on a small static site.

I had to create a private/public key pair, without passphrase. The public key gets added in GitHub as a "deploy key" while the private key gets saved as /var/www/.ssh/id_rsa. I also did sudo touch /var/www/.ssh/known_hosts && sudo chmod www-data /var/www/.ssh/known_hosts, then ran sudo -u www-data git pull from the command line to update the known_hosts file.

(I think that's it)

Have any improvements? Let me know!

@stormbytes

This comment has been minimized.

Show comment Hide comment
@stormbytes

stormbytes Jun 4, 2011

tried every which way and couldn't get this to work. script executed just fine from the command line using php script.php, but wouldn't work using service hooks. Oh well..

tried every which way and couldn't get this to work. script executed just fine from the command line using php script.php, but wouldn't work using service hooks. Oh well..

@webjay

This comment has been minimized.

Show comment Hide comment
@webjay

webjay Oct 18, 2012

@sudo chmod www-data /var/www/.ssh/known_hosts" should be sudo chown www-data /var/www/.ssh/known_hosts.

Also make sure www-data have access to the files in /var/www/.ssh

webjay commented Oct 18, 2012

@sudo chmod www-data /var/www/.ssh/known_hosts" should be sudo chown www-data /var/www/.ssh/known_hosts.

Also make sure www-data have access to the files in /var/www/.ssh

@webjay

This comment has been minimized.

Show comment Hide comment
@webjay

webjay Oct 19, 2012

The permission instructions were very helpfull, but the script doesn't work, so I made one that does:
https://gist.github.com/3915531

webjay commented Oct 19, 2012

The permission instructions were very helpfull, but the script doesn't work, so I made one that does:
https://gist.github.com/3915531

@jorijnsmit

This comment has been minimized.

Show comment Hide comment
@jorijnsmit

jorijnsmit Nov 25, 2012

Here is a similar project with a bit more advanced options: https://github.com/Coppertino/github-webhook.

Here is a similar project with a bit more advanced options: https://github.com/Coppertino/github-webhook.

@dpashkevich

This comment has been minimized.

Show comment Hide comment
@dpashkevich

dpashkevich Sep 2, 2013

I used suPHP to make the php webhook handler execute under my user instead of web server, hence no permissions problems and file ownership changes on pull. May sound scary but if the shell command is hard coded and suPHP is enabled only for that particular script (I can put it in a separate directory) it's pretty safe (feel free to call me out if that's still very bad %))

@jorijnsmit Interesting, a different approach to the problem! Thanks for sharing.

I used suPHP to make the php webhook handler execute under my user instead of web server, hence no permissions problems and file ownership changes on pull. May sound scary but if the shell command is hard coded and suPHP is enabled only for that particular script (I can put it in a separate directory) it's pretty safe (feel free to call me out if that's still very bad %))

@jorijnsmit Interesting, a different approach to the problem! Thanks for sharing.

@dpashkevich

This comment has been minimized.

Show comment Hide comment
@dpashkevich

dpashkevich Sep 3, 2013

I'm concerned about git reset --hard HEAD... is it safe to do so? What if some tracked files are (accidentally) edited on a running server? This shouldn't happen, of course, but if it does, I think I'd rather have the change NOT propagate to the server instead of overwriting something.

I'm concerned about git reset --hard HEAD... is it safe to do so? What if some tracked files are (accidentally) edited on a running server? This shouldn't happen, of course, but if it does, I think I'd rather have the change NOT propagate to the server instead of overwriting something.

@ravenjohn

This comment has been minimized.

Show comment Hide comment
@ravenjohn

ravenjohn Feb 15, 2014

just a little typo error.. it's receive not recieve.

just a little typo error.. it's receive not recieve.

@phedoreanu

This comment has been minimized.

Show comment Hide comment
@phedoreanu

phedoreanu Apr 26, 2014

@webjay It works, you just need to generate the web server's ssh keys. I've merged your security code with this gist and the ssh keys: https://gist.github.com/phedoreanu/11321236.

@webjay It works, you just need to generate the web server's ssh keys. I've merged your security code with this gist and the ssh keys: https://gist.github.com/phedoreanu/11321236.

@Stichoza

This comment has been minimized.

Show comment Hide comment
@Stichoza

Stichoza May 20, 2014

Note that your repo SHOULD NOT include files that are changed on server. Maybe /uploads folder or app/logs or anything. git reset --hard HEAD will delete everything. Put such files and folders in your .gitignore.

Note that your repo SHOULD NOT include files that are changed on server. Maybe /uploads folder or app/logs or anything. git reset --hard HEAD will delete everything. Put such files and folders in your .gitignore.

@lionelmann

This comment has been minimized.

Show comment Hide comment
@lionelmann

lionelmann Feb 15, 2015

Just an fyi: I had to change the following before it would work:

if ( $_POST['payload'] ) {

to

if ($_SERVER['HTTP_X_GITHUB_EVENT'] == 'push') {

also, as usually shell_exec is run from the user www-data I had to change permissions by executing the following command inside /var/www/html

sudo chown www-data:www-data -R my-repo/

Just an fyi: I had to change the following before it would work:

if ( $_POST['payload'] ) {

to

if ($_SERVER['HTTP_X_GITHUB_EVENT'] == 'push') {

also, as usually shell_exec is run from the user www-data I had to change permissions by executing the following command inside /var/www/html

sudo chown www-data:www-data -R my-repo/

@izeta

This comment has been minimized.

Show comment Hide comment
@izeta

izeta Aug 24, 2015

Thanks @lmann that worked for me:

if ($_SERVER['HTTP_X_GITHUB_EVENT'] == 'push') {

Just figured out the difference. If you use the json content-type, then $_POST['payload'] will not work. You have to use the application/x-www-form-urlencoded for $_POST['payload'] to work.

izeta commented Aug 24, 2015

Thanks @lmann that worked for me:

if ($_SERVER['HTTP_X_GITHUB_EVENT'] == 'push') {

Just figured out the difference. If you use the json content-type, then $_POST['payload'] will not work. You have to use the application/x-www-form-urlencoded for $_POST['payload'] to work.

@youhide

This comment has been minimized.

Show comment Hide comment
@youhide

youhide Sep 2, 2015

i need to use that on final of string and work nice:
2>&1
so:
shell_exec( 'cd '.$path.' && git reset --hard HEAD && git pull 2>&1' );

youhide commented Sep 2, 2015

i need to use that on final of string and work nice:
2>&1
so:
shell_exec( 'cd '.$path.' && git reset --hard HEAD && git pull 2>&1' );

@leotm

This comment has been minimized.

Show comment Hide comment
@leotm

leotm Jun 1, 2016

@izeta application/x-www-form-urlencoded got $_POST['payload'] working, cheers!
You can also shell_exec("./pull.sh"), remembering to chmod u+x pull.sh.
I'm on SiteGround hosting, so need to worry about www-data .

leotm commented Jun 1, 2016

@izeta application/x-www-form-urlencoded got $_POST['payload'] working, cheers!
You can also shell_exec("./pull.sh"), remembering to chmod u+x pull.sh.
I'm on SiteGround hosting, so need to worry about www-data .

@leymannx

This comment has been minimized.

Show comment Hide comment
@leymannx

leymannx Jul 4, 2017

etc/suoders:

www-data ALL = (myuser) NOPASSWD: /usr/bin/git
www-data ALL = (myuser) NOPASSWD: /usr/bin/node
www-data ALL = (myuser) NOPASSWD: /usr/bin/drush
www-data ALL = (myuser) NOPASSWD: /usr/bin/whoami

github_post_recieve.php:

<?php

if ( isset($_POST['payload']) && $_POST['payload'] ) {
  echo shell_exec('cd /var/www/mydrupal/ && sudo -u myuser git pull');
  echo shell_exec('cd /var/www/mydrupal/sites/all/themes/mytheme/ && sudo -u myuser node ./node_modules/gulp/bin/gulp.js mygulptask');
  // Adding the drush will cause the delivery being displayed as unsuccessful. Means GitHub doesn't wait so long. The command will run nevertheless.
  echo shell_exec('cd /var/www/mydrupal/ && sudo -u myuser drush @sites cc all -y');
}

// Should return www-data
echo shell_exec('whoami');
// Should return myuser
echo shell_exec('sudo -u myuser whoami');

?>

Gist

leymannx commented Jul 4, 2017

etc/suoders:

www-data ALL = (myuser) NOPASSWD: /usr/bin/git
www-data ALL = (myuser) NOPASSWD: /usr/bin/node
www-data ALL = (myuser) NOPASSWD: /usr/bin/drush
www-data ALL = (myuser) NOPASSWD: /usr/bin/whoami

github_post_recieve.php:

<?php

if ( isset($_POST['payload']) && $_POST['payload'] ) {
  echo shell_exec('cd /var/www/mydrupal/ && sudo -u myuser git pull');
  echo shell_exec('cd /var/www/mydrupal/sites/all/themes/mytheme/ && sudo -u myuser node ./node_modules/gulp/bin/gulp.js mygulptask');
  // Adding the drush will cause the delivery being displayed as unsuccessful. Means GitHub doesn't wait so long. The command will run nevertheless.
  echo shell_exec('cd /var/www/mydrupal/ && sudo -u myuser drush @sites cc all -y');
}

// Should return www-data
echo shell_exec('whoami');
// Should return myuser
echo shell_exec('sudo -u myuser whoami');

?>

Gist

@crobinson42

This comment has been minimized.

Show comment Hide comment
@crobinson42

crobinson42 Aug 23, 2017

Thx for the simple gist!

I solved my issue with my shell_exec(... command not running by changing the directory it was in ownership to www-data (from root), ie: sudo chown -R www-data /var/www/

Thx for the simple gist!

I solved my issue with my shell_exec(... command not running by changing the directory it was in ownership to www-data (from root), ie: sudo chown -R www-data /var/www/

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