Skip to content

Instantly share code, notes, and snippets.

@mytharcher
Last active March 3, 2021 00:30
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mytharcher/9138422 to your computer and use it in GitHub Desktop.
Save mytharcher/9138422 to your computer and use it in GitHub Desktop.
Used for automated deploy web site code from Bitbucket to Dreamhost test site via git POST hook.
# This config is used for Dreamhost which HTTP basic authoriztion is forbidden.
# The PHP Server variable 'PHP_AUTH_USER' & 'PHP_AUTH_PW' could be implement by
# this config via a CGI environment variable.
# From [HTTP Authentication on PHP as CGI (like Dreamhost)](http://planetozh.com/blog/2009/04/http-authentication-on-php-as-cgi-like-dreamhost/)
RewriteEngine on
RewriteRule ^git-hook.*\.php - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

PHP git hook deploy

Used for automated deploy web site code from Bitbucket to Dreamhost test site.

When push branch 'master' to bitbucket, the post hook will invoke this url:

http://git:pass@yourdomain.com/git-hook.php

then this script will pull the origin/master down.

Usage

  1. Copy the both files to your Dreamhost site web root.
  2. Add post hook URL into your bitbucket repo admin panel.
  3. Initialize your git repo on Dreamhost, including add remote as a ssh protocol.
  4. Add the ssh public key to Bitbucket deploy keys.
  5. Checkout the branch you want to deploy So that the pulled code will be present on your site.

Thanks

The Deploy class is from Brandon's post:

Using Bitbucket for Automated Deployments

<?php
$user = 'git';
$pass = 'pass';
$branch = $_GET['branch'] ? $_GET['branch'] : 'master';
$log_path = "deployments.log";
class Deploy {
/**
* A callback function to call after the deploy has finished.
*
* @var callback
*/
public $post_deploy;
/**
* The name of the file that will be used for logging deployments. Set to
* FALSE to disable logging.
*
* @var string
*/
private $_log = 'deployments.log';
/**
* The timestamp format used for logging.
*
* @link http://www.php.net/manual/en/function.date.php
* @var string
*/
private $_date_format = 'Y-m-d H:i:sP';
/**
* The name of the branch to pull from.
*
* @var string
*/
private $_branch = 'master';
/**
* The name of the remote to pull from.
*
* @var string
*/
private $_remote = 'origin';
/**
* The directory where your website and git repository are located, can be
* a relative or absolute path
*
* @var string
*/
private $_directory;
/**
* Sets up defaults.
*
* @param string $directory Directory where your website is located
* @param array $data Information about the deployment
*/
public function __construct($directory, $options = array())
{
// Determine the directory path
$this->_directory = realpath($directory).DIRECTORY_SEPARATOR;
$available_options = array('log', 'date_format', 'branch', 'remote');
foreach ($options as $option => $value)
{
if (in_array($option, $available_options))
{
$this->{'_'.$option} = $value;
}
}
$this->log('Attempting deployment...');
}
/**
* Writes a message to the log file.
*
* @param string $message The message to write
* @param string $type The type of log message (e.g. INFO, DEBUG, ERROR, etc.)
*/
public function log($message, $type = 'INFO')
{
if ($this->_log)
{
// Set the name of the log file
$filename = $this->_log;
if ( ! file_exists($filename))
{
// Create the log file
file_put_contents($filename, '');
// Allow anyone to write to log files
chmod($filename, 0666);
}
// Write the message into the log file
// Format: time --- type: message
file_put_contents($filename, date($this->_date_format).' --- '.$type.': '.$message.PHP_EOL, FILE_APPEND);
}
}
/**
* Executes the necessary commands to deploy the website.
*/
public function execute()
{
try
{
// Make sure we're in the right directory
exec('cd '.$this->_directory, $output);
$this->log('Changing working directory... '.implode(' ', $output));
// Discard any changes to tracked files since our last deploy
exec('git reset --hard HEAD', $output);
$this->log('Reseting repository... '.implode(' ', $output));
// Update the local repository
exec('git pull '.$this->_remote.' '.$this->_branch, $output);
$this->log('Pulling in changes... '.implode(' ', $output));
exec('git submodule update --init --recursive 2>&1', $output);
$this->log('Updating submodule changes... '.implode("\n", $output));
// Secure the .git directory
exec('chmod -R og-rx .git');
$this->log('Securing .git directory... ');
if (is_callable($this->post_deploy))
{
call_user_func($this->post_deploy, $this->_data);
}
$this->log('Deployment successful.');
}
catch (Exception $e)
{
$this->log($e, 'ERROR');
}
}
}
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
if ($_SERVER['PHP_AUTH_USER'] == $user &&
$_SERVER['PHP_AUTH_PW'] == $pass) {
if (!file_exists('.deploying')) {
touch('.deploying');
// This is just an example
$deploy = new Deploy('.', array(
'branch' => $branch,
'log' => $log_path
));
$deploy->post_deploy = function() use ($deploy) {
$output = array();
exec('sh build/pack.sh', $output);
$deploy->log(implode("\n", $output));
};
$deploy->execute();
unlink('.deploying');
}
} else {
// $deploy->log('HTTP Basic Authorization failed.', 'ERROR');
header('WWW-Authenticate: Basic realm="'.$_SERVER['HTTP_HOST'].'"');
header('HTTP/1.1 401 Unauthorized');
exit;
}
?>
<?php
$host = 'your.test.domain';
$user = 'git';
$pass = 'pass';
$branch = $_GET['branch'] ? $_GET['branch'] : 'master';
$log_path = "deployments.log";
if ((!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) &&
isset($_SERVER['HTTP_AUTHORIZATION'])) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}
if ($_SERVER['PHP_AUTH_USER'] == $user &&
$_SERVER['PHP_AUTH_PW'] == $pass) {
// log post data;
$data = json_decode($_POST['payload'], true);
if ($data && $data['commits'] && count($data['commits'])) {
$commits = $data['commits'];
$last = array_pop($commits);
if ($last['branch'] == $branch) {
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if ($fp) {
$out = "GET /deploy.php?branch=$branch / HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Authorization: ".$_SERVER['HTTP_AUTHORIZATION']."\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
fclose($fp);
}
}
} else {
// $deploy->log('No commits updated on branch: '.$branch);
}
} else {
// $deploy->log('HTTP Basic Authorization failed.', 'ERROR');
header('WWW-Authenticate: Basic realm="'.$_SERVER['HTTP_HOST'].'"');
header('HTTP/1.1 401 Unauthorized');
exit;
}
?>
@klokie
Copy link

klokie commented Jun 17, 2014

Hi, thanks for the helpful code! I've updated it and used it as the basis for a deployment scheme that can manage multiple repositories and branches with flexible configuration:
https://github.com/klokie/bitbucket-post-hook-deployment
Maybe you'll find it useful.
Cheers

@mytharcher
Copy link
Author

Good work @klokie ! But in my practice there are some problems in this code.

  1. The git-hook and deployment should be divided into 2 scripts. Because BitBucket HTTP request need to return as quick as possible. Some heave deployment task which would take a long time (over 30s BB would send another request) like JS compressing should be async.
  2. The command such as git pull and so on should put the cd <path> && before and in one command, or PHP never find the right path because each command are separated. Now it looks normal because mostly the site document root is just the git path, but in some sites are not.

Just consider this.

@thorakmedichi
Copy link

What do I use as the user and pass for dreamhost? is that my host login information?

@mytharcher
Copy link
Author

@thorakmedichi No. It's only hardcoded in this script to match the basic HTTP authentication you set in post hook service panel.

@mytharcher
Copy link
Author

NOT FOR COMMON SERVER THAN DREAMHOST (PHP based)

I have worked it out to a new project hookagent to handle most deployment on common server rather than Dreamhost (PHP only). And I moved it to node.js and shell based which much simpler.

Hope you will find it useful!

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