<?php
namespace tlf;
class GithubWebhook {
protected $payload;
protected $keyfile;
protected $workDir;
protected $outDir;
protected $repos;
protected $users;
protected $body;
protected $logging = false;
protected $user;
protected $repo;
protected $userDir;
protected $repoDir;
protected $defaultBranch;
protected $currentBranch;
public function __construct($payload, $privateKey, $workingDir, $outputDir,$allowedRepos, $validOwners){
if (!is_dir($workingDir)){
throw new \Exception("Working dir '{$workingDir}' must exist.");
}
if (!is_dir($outputDir)){
throw new \Exception("Output dir '{$outputDir}' must exist.");
}
$this->payload = json_decode($payload,true);
$this->key = $privateKey;
$this->workDir = $workingDir;
$this->outDir = $outputDir;
$this->repos = $allowedRepos;
//@TODO should we also check against the user who did the push?
$this->users = $validOwners;
$this->body = trim(file_get_contents("php://input"));
$headers = getallheaders();
$this->signature = $headers['X-Hub-Signature'];
//@TODO implement logging
$this->logFile = $this->workingDir.'/webhook-log';
$this->repo = $this->payload['repository']['name'];
$this->user = $this->payload['repository']['owner']['name'];
$this->userDir = $this->workDir.'/'.$this->user.'/';
$this->repoDir = $this->userDir.'/'.$this->repo.'/';
$this->defaultBranch = $this->payload['repository']['default_branch'];
$ref = $this->payload['ref'];
if (strpos($ref, $prefix='refs/heads/')===0){
$this->currentBranch = substr($ref,strlen($prefix));
} else {
echo "The current branch is null. Ref was '{$ref}' and should have started with 'refs/heads/' followed by the branch name.";
echo "We will update the default branch since we don't know what was pushed.";
$this->currentBranch = $this->defaultBranch;
}
}
// public function enableLogging(){
// $this->logging = true;
// }
public function isValidRequest(){
$body = $this->body;
$headers = getallheaders();
$log = $this->logFile;
$user = $this->user;
$repo = $this->repo;
if (!in_array($user,$this->users)){
echo "The user '{$user}' who owns the repo '{$repo}' is authorized.\n";
return false;
}
if (!in_array($repo,$this->repos)){
echo "The repo '{$repo}' is not authorized.\n";
return false;
}
$signature = $this->signature;
// $keyFile = $this->keyFile;
// trim(shell_exec('ssh-keygen -y -f '.$keyFile));
$body = $this->body;
$pubKey = $this->key;
$hash = 'sha1='.hash_hmac('sha1',$body,$pubKey);
$success = hash_equals($hash,$signature);
if ($success===true)return true;
echo "local hash:\n{$has}\n\nReceived:\n{$signature}";
return false;
}
public function retrieveCurrentRepo(){
if (!$this->isValidRequest()){
echo "The request was invalid or is not from github.\n";
return;
}
$userDir = $this->userDir;
$repoDir = $this->repoDir;
if (!is_dir($userDir))mkdir($userDir,0771,false);
if (is_dir($repoDir)&&count(scandir($repoDir))>2){
if (!is_dir($repoDir.'/.git')){
throw new \Exception("Your repo dir '{$repoDir}' exists, but does not contain a .git folder. Delete the directory & start fresh.");
}
$this->pullChanges();
return;
}
if (is_dir($repoDir)){
rmdir($this->repoDir);
}
$this->cloneRepo();
}
private function pullChanges(){
$dir = $this->repoDir;
echo "pull changes into '{$dir}'\n";
$command = "cd {$dir};\ngit pull;";
$output = shell_exec($command);
sleep(1);
}
private function cloneRepo(){
$dir = $this->userDir;
$cloneUrl = 'https://github.com/'.$this->user.'/'.$this->repo.'.git';
echo "clone chagnes into '{$dir}' from {$cloneUrl}\n";
$command = "cd {$dir};\ngit clone {$cloneUrl};";
$output = shell_exec($command);
sleep(1);
}
public function outputCurrentBranch(){
if (!$this->isValidRequest()){
echo "The request or keys were invalid... It may not be from github, or your key & webhook secret are configured wrong.\n";
return;
}
$this->retrieveCurrentRepo();
$repo = $this->repo;
$branch = $this->currentBranch;
$repoDir = $this->repoDir;
$outDir = $this->outDir.'/'.$this->user;
$repoOutDir = $outDir.'/'.$repo;
$branchOutDir = $repoOutDir.'/'.$branch;
if (!is_dir($outDir))mkdir($outDir,0771,false);
if (!is_dir($repoOutDir))mkdir($repoOutDir,0771,false);
if (!is_dir($branchOutDir))mkdir($branchOutDir,0771,false);
echo "Preparing to rsync files. Repo dir is '{$repoDir}'";
echo "Rsync files into '{$branchOutDir}'\n";
$inFiles = print_r(scandir($repoDir),true);
echo "Input Files ('{$repoDir}'):\n".$inFiles."\n";
$command =
<<<BASH
cd {$repoDir};
git checkout {$branch};
rsync -a --exclude=.git --exclude=".git" --exclude=".git/*" {$repoDir}/ {$branchOutDir};
BASH;
$outFiles = scandir($branchOutDir);
echo "Output Files ('{$branchOutDir}'):\n".print_r($files,true)."\n";
shell_exec($command);
}
}