deploy.php
<?php
/**
* simple php git deploy script
*
* automatically deploy the code using php and git.
*
* @version 1.3.1
* @link https://github.com/476552238li/deploy
*/
// =========================================[ configuration start ]===
/**
* it's preferable to configure the script using `deploy-config.php` file.
*
* rename `deploy-config.example.php` to `deploy-config.php` and edit the
* configuration options there instead of here. that way, you won't have to edit
* the configuration again if you download the new version of `deploy.php`.
*/
if (file_exists(basename(__file__, '.php').'-config.php')) require_once basename(__file__, '.php').'-config.php';
/**
* protect the script from unauthorized access by using a secret access token.
* if it's not present in the access url as a get variable named `sat`
* e.g. deploy.php?sat=bett...s the script is not going to deploy.
*
* @var string
*/
if (!defined('secret_access_token')) define('secret_access_token', '6a604a35d5a7c3fd8786f5ee94991a8c');
/**
* the address of the remote git repository that contains the code that's being
* deployed.
* if the repository is private, you'll need to use the ssh address.
*
* @var string
*/
if (!defined('remote_repository')) define('remote_repository', 'https://github.com/476552238li/php-sql-parser.git');
/**
* the branch that's being deployed.
* must be present in the remote repository.
*
* @var string
*/
if (!defined('branch')) define('branch', 'master');
/**
* the location that the code is going to be deployed to.
* don't forget the trailing slash!
*
* @var string full path including the trailing slash
*/
if (!defined('target_dir')) define('target_dir', '/var/www/php-sql-parser');
/**
* whether to delete the files that are not in the repository but are on the
* local (server) machine.
*
* !!! warning !!! this can lead to a serious loss of data if you're not
* careful. all files that are not in the repository are going to be deleted,
* except the ones defined in exclude section.
* be careful!
*
* @var boolean
*/
if (!defined('delete_files')) define('delete_files', false);
/**
* the directories and files that are to be excluded when updating the code.
* normally, these are the directories containing files that are not part of
* code base, for example user uploads or server-specific configuration files.
* use rsync exclude pattern syntax for each element.
*
* @var serialized array of strings
*/
if (!defined('exclude')) define('exclude', serialize(array(
'.git',
)));
/**
* temporary directory we'll use to stage the code before the update. if it
* already exists, script assumes that it contains an already cloned copy of the
* repository with the correct remote origin and only fetches changes instead of
* cloning the entire thing.
*
* @var string full path including the trailing slash
*/
if (!defined('tmp_dir')) define('tmp_dir', '/tmp/spgd-'.md5(remote_repository).'/');
/**
* whether to remove the tmp_dir after the deployment.
* it's useful not to clean up in order to only fetch changes on the next
* deployment.
*/
if (!defined('clean_up')) define('clean_up', true);
/**
* output the version of the deployed code.
*
* @var string full path to the file name
*/
if (!defined('version_file')) define('version_file', tmp_dir.'version');
/**
* time limit for each command.
*
* @var int time in seconds
*/
if (!defined('time_limit')) define('time_limit', 30);
/**
* optional
* backup the target_dir into backup_dir before deployment.
*
* @var string full backup directory path e.g. `/tmp/`
*/
if (!defined('backup_dir')) define('backup_dir', false);
/**
* optional
* whether to invoke composer after the repository is cloned or changes are
* fetched. composer needs to be available on the server machine, installed
* globaly (as `composer`). see http://getcomposer.org/doc/00-intro.md#globally
*
* @var boolean whether to use composer or not
* @link http://getcomposer.org/
*/
if (!defined('use_composer')) define('use_composer', false);
/**
* optional
* the options that the composer is going to use.
*
* @var string composer options
* @link http://getcomposer.org/doc/03-cli.md#install
*/
if (!defined('composer_options')) define('composer_options', '--no-dev');
/**
* optional
* the composer_home environment variable is needed only if the script is
* executed by a system user that has no home defined, e.g. `www-data`.
*
* @var string path to the composer_home e.g. `/tmp/composer`
* @link https://getcomposer.org/doc/03-cli.md#composer-home
*/
if (!defined('composer_home')) define('composer_home', false);
/**
* optional
* email address to be notified on deployment failure.
*
* @var string email address
*/
if (!defined('email_on_error')) define('email_on_error', false);
// ===========================================[ configuration end ]===
// if there's authorization error, set the correct http header.
if (!isset($_get['sat']) || $_get['sat'] !== secret_access_token || secret_access_token === '6a604a35d5a7c3fd8786f5ee94991a8c') {
header('http/1.0 403 forbidden');
}
ob_start();
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<title>simple php git deploy script</title>
<style>
body { padding: 0 1em; background: #222; color: #fff; }
h2, .error { color: #c33; }
.prompt { color: #6be234; }
.command { color: #729fcf; }
.output { color: #999; }
</style>
</head>
<body>
<?php
if (!isset($_get['sat']) || $_get['sat'] !== secret_access_token) {
die('<h2>access denied!</h2>');
}
if (secret_access_token === 'betterchangemenoworsuffertheconsequences') {
die("<h2>you're suffering the consequences!<br>change the secret_access_token from it's default value!</h2>");
}
?>
<pre>
checking the environment ...
running as <b><?php echo trim(shell_exec('whoami')); ?></b>.
<?php
// check if the required programs are available
$requiredbinaries = array('git', 'rsync');
if (defined('backup_dir') && backup_dir !== false) {
$requiredbinaries[] = 'tar';
if (!is_dir(backup_dir) || !is_writable(backup_dir)) {
die(sprintf('<div>backup_dir `%s` does not exists or is not writeable.</div>', backup_dir));
}
}
if (defined('use_composer') && use_composer === true) {
$requiredbinaries[] = 'composer --no-ansi';
}
foreach ($requiredbinaries as $command) {
$path = trim(shell_exec('which '.$command));
if ($path == '') {
die(sprintf('<div><b>%s</b> not available. it needs to be installed on the server for this script to work.</div>', $command));
} else {
$version = explode("\n", shell_exec($command.' --version'));
printf('<b>%s</b> : %s'."\n"
, $path
, $version[0]
);
}
}
?>
environment ok.
deploying <?php echo remote_repository; ?> <?php echo branch."\n"; ?>
to <?php echo target_dir; ?> ...
<?php
// the commands
$commands = array();
// ========================================[ pre-deployment steps ]===
if (!is_dir(tmp_dir)) {
// clone the repository into the tmp_dir
$commands[] = sprintf(
'git clone --depth=1 --branch %s %s %s'
, branch
, remote_repository
, tmp_dir
);
} else {
// tmp_dir exists and hopefully already contains the correct remote origin
// so we'll fetch the changes and reset the contents.
$commands[] = sprintf(
'git --git-dir="%s.git" --work-tree="%s" fetch origin %s'
, tmp_dir
, tmp_dir
, branch
);
$commands[] = sprintf(
'git --git-dir="%s.git" --work-tree="%s" reset --hard fetch_head'
, tmp_dir
, tmp_dir
);
}
// update the submodules
$commands[] = sprintf(
'git submodule update --init --recursive'
);
// describe the deployed version
if (defined('version_file') && version_file !== '') {
$commands[] = sprintf(
'git --git-dir="%s.git" --work-tree="%s" describe --always > %s'
, tmp_dir
, tmp_dir
, version_file
);
}
// backup the target_dir
// without the backup_dir for the case when it's inside the target_dir
if (defined('backup_dir') && backup_dir !== false) {
$commands[] = sprintf(
"tar --exclude='%s*' -czf %s/%s-%s-%s.tar.gz %s*"
, backup_dir
, backup_dir
, basename(target_dir)
, md5(target_dir)
, date('ymdhis')
, target_dir // we're backing up this directory into backup_dir
);
}
// invoke composer
if (defined('use_composer') && use_composer === true) {
$commands[] = sprintf(
'composer --no-ansi --no-interaction --no-progress --working-dir=%s install %s'
, tmp_dir
, (defined('composer_options')) ? composer_options : ''
);
if (defined('composer_home') && is_dir(composer_home)) {
putenv('composer_home='.composer_home);
}
}
// ==================================================[ deployment ]===
// compile exclude parameters
$exclude = '';
foreach (unserialize(exclude) as $exc) {
$exclude .= ' --exclude='.$exc;
}
// deployment command
$commands[] = sprintf(
'rsync -rltgodzvo %s %s %s %s'
, tmp_dir
, target_dir
, (delete_files) ? '--delete-after' : ''
, $exclude
);
// =======================================[ post-deployment steps ]===
// remove the tmp_dir (depends on clean_up)
if (clean_up) {
$commands['cleanup'] = sprintf(
'rm -rf %s'
, tmp_dir
);
}
// =======================================[ run the command steps ]===
$output = '';
foreach ($commands as $command) {
set_time_limit(time_limit); // reset the time limit for each command
if (file_exists(tmp_dir) && is_dir(tmp_dir)) {
chdir(tmp_dir); // ensure that we're in the right directory
}
$tmp = array();
exec($command.' 2>&1', $tmp, $return_code); // execute the command
// output the result
printf('
<span>$</span> <span>%s</span>
<div>%s</div>
'
, htmlentities(trim($command))
, htmlentities(trim(implode("\n", $tmp)))
);
$output .= ob_get_contents();
ob_flush(); // try to output everything as it happens
// error handling and cleanup
if ($return_code !== 0) {
printf('
<div>
error encountered!
stopping the script to prevent possible data loss.
check the data in your target dir!
</div>
'
);
if (clean_up) {
$tmp = shell_exec($commands['cleanup']);
printf('
cleaning up temporary files ...
<span>$</span> <span>%s</span>
<div>%s</div>
'
, htmlentities(trim($commands['cleanup']))
, htmlentities(trim($tmp))
);
}
$error = sprintf(
'deployment error on %s using %s!'
, $_server['http_host']
, __file__
);
error_log($error);
if (email_on_error) {
$output .= ob_get_contents();
$headers = array();
$headers[] = sprintf('from: simple php git deploy script <simple-php-git-deploy@%s>', $_server['http_host']);
$headers[] = sprintf('x-mailer: php/%s', phpversion());
mail(email_on_error, $error, strip_tags(trim($output)), implode("\r\n", $headers));
}
break;
}
}
?>
done.
</pre>
</body>
</html>