File: /var/www/vhosts/app.ett-dev.2amigos.us/cgi-bin/deployer.php
<?php
$content = file_get_contents('php://input');
$json = json_decode($content, true);
$token = false;
$sha = false;
$DIR = preg_match("/\/$/", DIR) ? DIR : DIR . '/';
$LOGDIR = preg_match("/\/$/", LOGDIR) ? LOGDIR : LOGDIR . '/';
// retrieve the token
if (!$token && isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) {
list($algo, $token) = explode('=', $_SERVER['HTTP_X_HUB_SIGNATURE'], 2) + array('', '');
} elseif (isset($_SERVER['HTTP_X_GITLAB_TOKEN'])) {
$token = $_SERVER['HTTP_X_GITLAB_TOKEN'];
} elseif (isset($_GET['token'])) {
$token = $_GET['token'];
}
// retrieve the checkout_sha
if (isset($json['checkout_sha'])) {
$sha = $json['checkout_sha'];
} elseif (isset($_SERVER['checkout_sha'])) {
$sha = $_SERVER['checkout_sha'];
} elseif (isset($_GET['sha'])) {
$sha = $_GET['sha'];
}
// specify that the response does not contain HTML
header('Content-Type: text/plain');
// use user-defined max_execution_time
if (!empty(MAX_EXECUTION_TIME)) {
ini_set('max_execution_time', MAX_EXECUTION_TIME);
}
// function to forbid access
function DenyAccess($reason) {
// format the error
$error = '=== ERROR: ' . $reason . ' ===' . PHP_EOL . '*** ACCESS DENIED ***' . PHP_EOL;
// forbid
http_response_code(403);
// write the error to the body
echo $error;
// stop executing
exit;
}
// function to run command in background
function runCommand($cmd, $outputFile = '/dev/null') {
$pid = shell_exec(sprintf("%s >>%s 2>>%s & echo $!", $cmd, $outputFile, $outputFile));
// return the pid of background process
return $pid;
}
// function to check if process is running
function isRunning($pid) {
try{
$result = shell_exec(sprintf("ps %s", $pid));
if( count(preg_split("/\n/", $result)) > 2){
return true;
}
} catch(Exception $e){}
return false;
}
// check for a GitHub signature
if (!empty(TOKEN) && isset($_SERVER['HTTP_X_HUB_SIGNATURE']) && $token !== hash_hmac($algo, $content, TOKEN)) {
DenyAccess('X-Hub-Signature does not match TOKEN');
}
// Check for a GitLab token
if (!empty(TOKEN) && isset($_SERVER['HTTP_X_GITLAB_TOKEN']) && $token !== TOKEN) {
DenyAccess('X-GitLab-Token does not match TOKEN');
}
// check for a $_GET token
if (!empty(TOKEN) && isset($_GET['token'])) {
if (!isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], ALLOWED_IP)) {
DenyAccess($_SERVER['REMOTE_ADDR'] . ' is not in allowed IPs list');
}
if ($token !== TOKEN) {
DenyAccess('$_GET[\'token\'] does not match TOKEN');
}
}
// no token provided
if (!empty(TOKEN) && !isset($_SERVER['HTTP_X_HUB_SIGNATURE']) && !isset($_SERVER['HTTP_X_GITLAB_TOKEN']) && !isset($_GET['token'])) {
DenyAccess('No token detected');
}
// webhook call validation
if (!isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], ALLOWED_IP)) {
// check for remote repository match
if (!(isset($json['repository']['git_url']) && $json['repository']['git_url'] === REMOTE_REPOSITORY ||
isset($json['repository']['ssh_url']) && $json['repository']['ssh_url'] === REMOTE_REPOSITORY ||
isset($json['repository']['clone_url']) && $json['repository']['clone_url'] === REMOTE_REPOSITORY ||
isset($json['repository']['svn_url']) && $jsonn['repository']['svn_url'] === REMOTE_REPOSITORY ||
isset($json['project']['git_ssh_url']) && $json['project']['git_ssh_url'] === REMOTE_REPOSITORY ||
isset($json['project']['git_http_url']) && $json['project']['git_http_url'] === REMOTE_REPOSITORY)) {
DenyAccess('Remote Repository mismatch');
}
// check for branch match, ignore if not
$branch = 'refs/heads/' . BRANCH;
if ($json['ref'] !== $branch) {
$info = '=== INFO: Remote Branch mismatch, skipping ===' . PHP_EOL;
http_response_code(200);
// write the info to the body
echo $info;
// stop executing
exit;
}
}
// ensure directory is a repository
if (!file_exists($DIR . '.git') || !is_dir($DIR)) {
// prepare the generic error
$error = '=== ERROR: DIR `' . DIR . '` is not a repository ===' . PHP_EOL;
// try to detemrine the real error
if (!file_exists(DIR)) {
$error = '=== ERROR: DIR `' . DIR . '` does not exist ===' . PHP_EOL;
} elseif (!is_dir(DIR)) {
$error = '=== ERROR: DIR `' . DIR . '` is not a directory ===' . PHP_EOL;
}
// bad request
http_response_code(403);
// write the error to the body
echo $error;
// stop executing
exit;
}
// check if no deploy running atm
if (file_exists('.ht-deploy')) {
$info = '=== INFO: There is already a deploy process running ===' . PHP_EOL;
http_response_code(200);
if (!isset($_SERVER['HTTP_X_HUB_SIGNATURE']) && !isset($_SERVER['HTTP_X_GITLAB_TOKEN']) && isset($_GET['token'])) {
header('Content-Type: text/html');
?>
<pre> <?php echo $info; ?></pre>
<p>You will be redirected in <span id="counter">3</span> second(s) to deploy log</p>
<script type="text/javascript">
function countdown() {
var i = document.getElementById('counter');
if (parseInt(i.innerHTML)<=0) {
location.href = <?php echo '"' . $_SERVER['PHP_SELF'] . '?viewlog=yes' . '"'; ?>;
}
if (parseInt(i.innerHTML)!=0) {
i.innerHTML = parseInt(i.innerHTML)-1;
}
}
setInterval(function(){ countdown(); },1000);
</script>
<?php
} else { echo $info; }
exit;
}
////
//// we finally finished preliminary checks, now generating deploy script
////
// create tmpfile with deploy commands
$tfile = fopen('.ht-deploy','w');
fwrite($tfile,
'#!/bin/bash' . PHP_EOL .
'trap "exit 1" TERM' . PHP_EOL .
'TOP_PID=$$' . PHP_EOL .
'DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)' . PHP_EOL .
PHP_EOL .
'function alert() {' . PHP_EOL .
' echo "$(date +[%d-%m-%Y\ %H:%M:%S]): === DEPLOY NOT COMPLETE, CHECK ABOVE ERRORS ==="' . PHP_EOL .
' rm -f $DIR/.ht-deploy' . PHP_EOL .
' kill -s TERM $TOP_PID' . PHP_EOL .
'}' . PHP_EOL .
PHP_EOL .
'cd ' . DIR . PHP_EOL
);
// add clean local repo to script, if enabled in settings
if (!empty(CLEAN) && CLEAN === 'YES') {
fwrite($tfile, PHP_EOL . 'echo "$(date +[%d-%m-%Y\ %H:%M:%S]): *** CLEAN REPO STEP ***"' . PHP_EOL);
fwrite($tfile, GIT . ' reset --hard HEAD || alert' . PHP_EOL);
fwrite($tfile, GIT . ' clean -dfx || alert' . PHP_EOL);
}
// add BEFORE_PULL to deploy script, if specified
if (!empty(BEFORE_PULL) && file_exists(BEFORE_PULL)) {
fwrite($tfile, PHP_EOL . 'echo "$(date +[%d-%m-%Y\ %H:%M:%S]): *** BEFORE PULL STEP ***"' . PHP_EOL);
fwrite($tfile, BEFORE_PULL . ' || alert' . PHP_EOL);
}
// add pull to deploy script
fwrite($tfile, PHP_EOL . 'echo "$(date +[%d-%m-%Y\ %H:%M:%S]): *** PULL ORIGIN STEP ***"' . PHP_EOL);
fwrite($tfile, GIT . ' pull origin ' . BRANCH . ' || alert' . PHP_EOL);
// add AFTER_PULL to deploy script, if specified
if (!empty(AFTER_PULL) && file_exists(AFTER_PULL)) {
fwrite($tfile, PHP_EOL . 'echo "$(date +[%d-%m-%Y\ %H:%M:%S]): *** AFTER PULL STEP ***"' . PHP_EOL);
fwrite($tfile, AFTER_PULL . ' || alert' . PHP_EOL);
}
// complete deploy script
fwrite($tfile, PHP_EOL . 'echo "$(date +[%d-%m-%Y\ %H:%M:%S]): === DEPLOY COMPLETE ==="' . PHP_EOL .
'rm -f $DIR/.ht-deploy' . PHP_EOL
);
////
//// last touch, before we deploy, initiate logs
////
// open log file and write the start time to it
unlink($LOGDIR . 'deploy-last.log');
symlink($LOGDIR . 'deploy-' . date('d-m-Y-H-i-s') . '.log', $LOGDIR . 'deploy-last.log');
$file = fopen($LOGDIR . 'deploy-last.log', 'w');
fputs($file, date('[d-m-Y H:i:s]') . ': === START DEPLOY ===' . PHP_EOL);
fclose($tfile);
fclose($file);
////
//// deploy itself
////
// start and run in brackground;
$pid = runCommand('/bin/bash .ht-deploy', $LOGDIR . 'deploy-last.log');
// workaround to be sure deploy script is really started
for ($i = 1; $i <= 10; $i++) {
if (isset($pid) && isRunning($pid)) {
break;
}
sleep(1);
}
// some information to show
$info = '=== INFO: Deploy in progress/complete ===' . PHP_EOL .
'Reposity: ' . REMOTE_REPOSITORY . PHP_EOL .
'Branch: ' . BRANCH . PHP_EOL .
'Directory: ' . DIR . PHP_EOL .
'Deploy Log: ' . $LOGDIR . 'deploy-last.log' . PHP_EOL .
'-----------------------------------------';
////
//// Redirect log deploy tail log, when deploy from browser
////
if (!isset($_SERVER['HTTP_X_HUB_SIGNATURE']) && !isset($_SERVER['HTTP_X_GITLAB_TOKEN']) && isset($_GET['token'])) {
header('Content-Type: text/html');
?>
<pre> <?php echo $info; ?></pre>
<p>You will be redirected in <span id="counter">3</span> second(s) to deploy log</p>
<script type="text/javascript">
function countdown() {
var i = document.getElementById('counter');
if (parseInt(i.innerHTML)<=0) {
location.href = <?php echo '"' . $_SERVER['PHP_SELF'] . '?viewlog=yes' . '"'; ?>;
}
if (parseInt(i.innerHTML)!=0) {
i.innerHTML = parseInt(i.innerHTML)-1;
}
}
setInterval(function(){ countdown(); },1000);
</script>
<?php
} else {
http_response_code(200);
echo $info;
}