Refactoring builds out into separate model types (Github, Bitbucket, Local) and builder to use build->createWorkingCopy() to make build directory. Fixes #29
This commit is contained in:
parent
1a92d1c619
commit
be37a5e821
32
PHPCI/BuildFactory.php
Normal file
32
PHPCI/BuildFactory.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCI;
|
||||
use PHPCI\Model\Build,
|
||||
PHPCI\Model\Build\LocalBuild,
|
||||
PHPCI\Model\Build\GithubBuild,
|
||||
PHPCI\Model\Build\BitbucketBuild;
|
||||
|
||||
class BuildFactory
|
||||
{
|
||||
public static function getBuild(Build $base)
|
||||
{
|
||||
switch($base->getProject()->getType())
|
||||
{
|
||||
case 'local':
|
||||
$type = 'LocalBuild';
|
||||
break;
|
||||
|
||||
case 'github':
|
||||
$type = 'GithubBuild';
|
||||
break;
|
||||
|
||||
case 'bitbucket':
|
||||
$type = 'BitbucketBuild';
|
||||
break;
|
||||
}
|
||||
|
||||
$type = '\\PHPCI\\Model\\Build\\' . $type;
|
||||
|
||||
return new $type($base->getDataArray());
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ class Builder
|
|||
protected $plugins = array();
|
||||
protected $build;
|
||||
protected $logCallback;
|
||||
protected $config;
|
||||
|
||||
public function __construct(Build $build, $logCallback = null)
|
||||
{
|
||||
|
@ -30,11 +31,21 @@ class Builder
|
|||
}
|
||||
}
|
||||
|
||||
public function setConfigArray(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getConfig($key)
|
||||
{
|
||||
return isset($this->config[$key]) ? $this->config[$key] : null;
|
||||
}
|
||||
|
||||
public function execute()
|
||||
{
|
||||
$this->build->setStatus(1);
|
||||
$this->build->setStarted(new \DateTime());
|
||||
$this->build = $this->store->save($this->build);
|
||||
$this->store->save($this->build);
|
||||
$this->build->sendStatusPostback();
|
||||
|
||||
if($this->setupBuild())
|
||||
|
@ -91,7 +102,7 @@ class Builder
|
|||
return ($status == 0) ? true : false;
|
||||
}
|
||||
|
||||
protected function log($message, $prefix = '')
|
||||
public function log($message, $prefix = '')
|
||||
{
|
||||
|
||||
if(is_array($message))
|
||||
|
@ -123,15 +134,15 @@ class Builder
|
|||
|
||||
$this->build->setLog($this->log);
|
||||
$this->build->setPlugins(json_encode($this->plugins));
|
||||
$this->build = $this->store->save($this->build);
|
||||
$this->store->save($this->build);
|
||||
}
|
||||
|
||||
protected function logSuccess($message)
|
||||
public function logSuccess($message)
|
||||
{
|
||||
$this->log("\033[0;32m" . $message . "\033[0m");
|
||||
}
|
||||
|
||||
protected function logFailure($message)
|
||||
public function logFailure($message)
|
||||
{
|
||||
$this->log("\033[0;31m" . $message . "\033[0m");
|
||||
}
|
||||
|
@ -139,102 +150,29 @@ class Builder
|
|||
protected function setupBuild()
|
||||
{
|
||||
$commitId = $this->build->getCommitId();
|
||||
$url = $this->build->getProject()->getGitUrl();
|
||||
$key = $this->build->getProject()->getGitKey();
|
||||
$type = $this->build->getProject()->getType();
|
||||
$reference = $this->build->getProject()->getReference();
|
||||
$reference = substr($reference, -1) == '/' ? substr($reference, 0, -1) : $reference;
|
||||
$buildId = 'project' . $this->build->getProject()->getId() . '-build' . $this->build->getId();
|
||||
$yamlParser = new YamlParser();
|
||||
|
||||
$this->ciDir = realpath(dirname(__FILE__) . '/../') . '/';
|
||||
$this->buildPath = $this->ciDir . 'build/' . $buildId . '/';
|
||||
|
||||
switch ($type)
|
||||
{
|
||||
case 'local':
|
||||
$this->buildPath = substr($this->buildPath, 0, -1);
|
||||
|
||||
if(!is_file($reference . '/phpci.yml'))
|
||||
{
|
||||
$this->logFailure('Project does not contain a phpci.yml file.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$yamlFile = file_get_contents($reference . '/phpci.yml');
|
||||
$this->config = $yamlParser->parse($yamlFile);
|
||||
|
||||
if(array_key_exists('build_settings', $this->config)
|
||||
&& is_array($this->config['build_settings'])
|
||||
&& array_key_exists('prefer_symlink', $this->config['build_settings'])
|
||||
&& true === $this->config['build_settings']['prefer_symlink'])
|
||||
{
|
||||
if(is_link($this->buildPath) && is_file($this->buildPath))
|
||||
{
|
||||
unlink($this->buildPath);
|
||||
}
|
||||
|
||||
$this->log(sprintf('Symlinking: %s to %s',$reference, $this->buildPath));
|
||||
if ( !symlink($reference, $this->buildPath) )
|
||||
{
|
||||
$this->logFailure('Failed to symlink.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->executeCommand(sprintf("cp -Rf %s %s/", $reference, $this->buildPath));
|
||||
}
|
||||
|
||||
$this->buildPath .= '/';
|
||||
break;
|
||||
|
||||
case 'github':
|
||||
case 'bitbucket':
|
||||
mkdir($this->buildPath, 0777, true);
|
||||
|
||||
if(!empty($key))
|
||||
{
|
||||
// Do an SSH clone:
|
||||
$keyFile = $this->ciDir . 'build/' . $buildId . '.key';
|
||||
file_put_contents($keyFile, $key);
|
||||
chmod($keyFile, 0600);
|
||||
$this->executeCommand('ssh-agent ssh-add '.$keyFile.' && git clone -b ' .$this->build->getBranch() . ' ' .$url.' '.$this->buildPath.' && ssh-agent -k');
|
||||
unlink($keyFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do an HTTP clone:
|
||||
$this->executeCommand('git clone -b ' .$this->build->getBranch() . ' ' .$url.' '.$this->buildPath);
|
||||
}
|
||||
|
||||
if(!is_file($this->buildPath . 'phpci.yml'))
|
||||
{
|
||||
$this->logFailure('Project does not contain a phpci.yml file.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$yamlFile = file_get_contents($this->buildPath . 'phpci.yml');
|
||||
$this->config = $yamlParser->parse($yamlFile);
|
||||
break;
|
||||
// Create a working copy of the project:
|
||||
if(!$this->build->createWorkingCopy($this, $this->buildPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isset($this->config['build_settings']['verbose']) || !$this->config['build_settings']['verbose'])
|
||||
{
|
||||
// Does the project's phpci.yml request verbose mode?
|
||||
if(!isset($this->config['build_settings']['verbose']) || !$this->config['build_settings']['verbose']) {
|
||||
$this->verbose = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$this->verbose = true;
|
||||
}
|
||||
|
||||
if(isset($this->config['build_settings']['ignore']))
|
||||
{
|
||||
// Does the project have any paths it wants plugins to ignore?
|
||||
if(isset($this->config['build_settings']['ignore'])) {
|
||||
$this->ignore = $this->config['build_settings']['ignore'];
|
||||
}
|
||||
|
||||
$this->log('Set up build: ' . $this->buildPath);
|
||||
|
||||
$this->logSuccess('Working copy created: ' . $this->buildPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use b8\Store\Factory;
|
||||
use PHPCI\Builder;
|
||||
use PHPCI\Builder,
|
||||
PHPCI\BuildFactory;
|
||||
|
||||
class RunCommand extends Command
|
||||
{
|
||||
|
@ -28,6 +29,8 @@ class RunCommand extends Command
|
|||
|
||||
foreach($result['items'] as $build)
|
||||
{
|
||||
$build = BuildFactory::getBuild($build);
|
||||
|
||||
if ($input->getOption('verbose')) {
|
||||
$builder = new Builder($build, array($this, 'logCallback'));
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ namespace PHPCI\Model;
|
|||
|
||||
require_once(APPLICATION_PATH . 'PHPCI/Model/Base/BuildBase.php');
|
||||
|
||||
use PHPCI\Model\Base\BuildBase;
|
||||
use PHPCI\Model\Base\BuildBase,
|
||||
PHPCI\Builder;
|
||||
|
||||
/**
|
||||
* Build Model
|
||||
|
@ -18,56 +19,20 @@ class Build extends BuildBase
|
|||
{
|
||||
public function getCommitLink()
|
||||
{
|
||||
switch($this->getProject()->getType())
|
||||
{
|
||||
case 'github':
|
||||
return 'https://github.com/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId();
|
||||
}
|
||||
return '#';
|
||||
}
|
||||
|
||||
public function getBranchLink()
|
||||
{
|
||||
switch($this->getProject()->getType())
|
||||
{
|
||||
case 'github':
|
||||
return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch();
|
||||
}
|
||||
return '#';
|
||||
}
|
||||
|
||||
public function sendStatusPostback()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
return;
|
||||
}
|
||||
|
||||
if($project->getType() == 'github' && $project->getToken())
|
||||
{
|
||||
$url = 'https://api.github.com/repos/'.$project->getReference().'/statuses/'.$this->getCommitId();
|
||||
$http = new \b8\HttpClient();
|
||||
|
||||
switch($this->getStatus())
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
$status = 'pending';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$status = 'success';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$status = 'failure';
|
||||
break;
|
||||
|
||||
default:
|
||||
$status = 'error';
|
||||
break;
|
||||
}
|
||||
|
||||
$params = array( 'state' => $status,
|
||||
'target_url' => \b8\Registry::getInstance()->get('install_url') . '/build/view/' . $this->getId());
|
||||
|
||||
$http->setHeaders(array('Authorization: token ' . $project->getToken()));
|
||||
$http->request('POST', $url, json_encode($params));
|
||||
}
|
||||
public function createWorkingCopy(Builder $builder, $buildPath)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
38
PHPCI/Model/Build/BitbucketBuild.php
Normal file
38
PHPCI/Model/Build/BitbucketBuild.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Build model for table: build
|
||||
*/
|
||||
|
||||
namespace PHPCI\Model\Build;
|
||||
use PHPCI\Model\Build;
|
||||
use PHPCI\Model\Build\RemoteGitBuild;
|
||||
|
||||
/**
|
||||
* Build Model
|
||||
* @uses PHPCI\Model\Build
|
||||
*/
|
||||
class BitbucketBuild extends RemoteGitBuild
|
||||
{
|
||||
public function getCommitLink()
|
||||
{
|
||||
return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/commits/' . $this->getCommitId();
|
||||
}
|
||||
|
||||
public function getBranchLink()
|
||||
{
|
||||
return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getBranch();
|
||||
}
|
||||
|
||||
protected function getCloneUrl()
|
||||
{
|
||||
$key = trim($this->getProject()->getGitKey());
|
||||
|
||||
if(!empty($key)) {
|
||||
return 'git@bitbucket.org:' . $this->getProject()->getReference() . '.git';
|
||||
}
|
||||
else {
|
||||
return 'https://bitbucket.org/' . $this->getProject()->getReference() . '.git';
|
||||
}
|
||||
}
|
||||
}
|
76
PHPCI/Model/Build/GithubBuild.php
Normal file
76
PHPCI/Model/Build/GithubBuild.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Build model for table: build
|
||||
*/
|
||||
|
||||
namespace PHPCI\Model\Build;
|
||||
use PHPCI\Model\Build\RemoteGitBuild;
|
||||
|
||||
/**
|
||||
* Build Model
|
||||
* @uses PHPCI\Model\Build
|
||||
*/
|
||||
class GithubBuild extends RemoteGitBuild
|
||||
{
|
||||
public function getCommitLink()
|
||||
{
|
||||
return 'https://github.com/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId();
|
||||
}
|
||||
|
||||
public function getBranchLink()
|
||||
{
|
||||
return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch();
|
||||
}
|
||||
|
||||
public function sendStatusPostback()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
// The postback will only work if we have an access token.
|
||||
if(!$project->getToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = 'https://api.github.com/repos/'.$project->getReference().'/statuses/'.$this->getCommitId();
|
||||
$http = new \b8\HttpClient();
|
||||
|
||||
switch($this->getStatus())
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
$status = 'pending';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$status = 'success';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$status = 'failure';
|
||||
break;
|
||||
|
||||
default:
|
||||
$status = 'error';
|
||||
break;
|
||||
}
|
||||
|
||||
$params = array( 'state' => $status,
|
||||
'target_url' => \b8\Registry::getInstance()->get('install_url') . '/build/view/' . $this->getId());
|
||||
|
||||
$http->setHeaders(array('Authorization: token ' . $project->getToken()));
|
||||
$http->request('POST', $url, json_encode($params));
|
||||
}
|
||||
|
||||
protected function getCloneUrl()
|
||||
{
|
||||
$key = trim($this->getProject()->getGitKey());
|
||||
|
||||
if(!empty($key)) {
|
||||
return 'git@github.com:' . $this->getProject()->getReference() . '.git';
|
||||
}
|
||||
else {
|
||||
return 'https://github.com/' . $this->getProject()->getReference() . '.git';
|
||||
}
|
||||
}
|
||||
}
|
52
PHPCI/Model/Build/LocalBuild.php
Normal file
52
PHPCI/Model/Build/LocalBuild.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Build model for table: build
|
||||
*/
|
||||
|
||||
namespace PHPCI\Model;
|
||||
use PHPCI\Model\Build;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
|
||||
/**
|
||||
* Build Model
|
||||
* @uses PHPCI\Model\Build
|
||||
*/
|
||||
class LocalBuild extends Build
|
||||
{
|
||||
public function createWorkingCopy(Builder $builder, $buildPath)
|
||||
{
|
||||
$reference = $this->getProject()->getReference();
|
||||
$reference = substr($reference, -1) == '/' ? substr($reference, 0, -1) : $reference;
|
||||
$buildPath = substr($buildPath, 0, -1);
|
||||
$yamlParser = new YamlParser();
|
||||
|
||||
if(!is_file($reference . '/phpci.yml')) {
|
||||
$builder->logFailure('Project does not contain a phpci.yml file.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$yamlFile = file_get_contents($reference . '/phpci.yml');
|
||||
$builder->setConfigArray($yamlParser->parse($yamlFile));
|
||||
|
||||
$buildSettings = $builder->getConfig('build_settings');
|
||||
|
||||
if(isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) {
|
||||
if(is_link($buildPath) && is_file($buildPath)) {
|
||||
unlink($buildPath);
|
||||
}
|
||||
|
||||
$builder->log(sprintf('Symlinking: %s to %s',$reference, $buildPath));
|
||||
|
||||
if(!symlink($reference, $buildPath)) {
|
||||
$builder->logFailure('Failed to symlink.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$builder->executeCommand(sprintf("cp -Rf %s %s/", $reference, $buildPath));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
69
PHPCI/Model/Build/RemoteGitBuild.php
Normal file
69
PHPCI/Model/Build/RemoteGitBuild.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Build model for table: build
|
||||
*/
|
||||
|
||||
namespace PHPCI\Model\Build;
|
||||
use PHPCI\Model\Build;
|
||||
use PHPCI\Builder;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
|
||||
/**
|
||||
* Build Model
|
||||
* @uses PHPCI\Model\Build
|
||||
*/
|
||||
abstract class RemoteGitBuild extends Build
|
||||
{
|
||||
abstract protected function getCloneUrl();
|
||||
|
||||
public function createWorkingCopy(Builder $builder, $buildPath)
|
||||
{
|
||||
$yamlParser = new YamlParser();
|
||||
$success = true;
|
||||
$key = trim($this->getProject()->getGitKey());
|
||||
|
||||
if(!empty($key)) {
|
||||
$success = $this->cloneBySsh($builder, $buildPath);
|
||||
}
|
||||
else {
|
||||
$success = $this->cloneByHttp($builder, $buildPath);
|
||||
}
|
||||
|
||||
if(!$success) {
|
||||
$builder->logFailure('Failed to clone remote git repository.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!is_file($buildPath . 'phpci.yml')) {
|
||||
$builder->logFailure('Project does not contain a phpci.yml file.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$yamlFile = file_get_contents($buildPath . 'phpci.yml');
|
||||
$builder->setConfigArray($yamlParser->parse($yamlFile));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function cloneByHttp(Builder $builder, $to)
|
||||
{
|
||||
return $builder->executeCommand('git clone -b ' .$this->getBranch() . ' ' .$this->getCloneUrl().' '.$to);
|
||||
}
|
||||
|
||||
protected function cloneBySsh(Builder $builder, $to)
|
||||
{
|
||||
// Copy the project's keyfile to disk:
|
||||
$keyFile = realpath($to) . '.key';
|
||||
file_put_contents($keyFile, $this->getProject()->getGitKey());
|
||||
chmod($keyFile, 0600);
|
||||
|
||||
// Use the key file to do an SSH clone:
|
||||
$success = $builder->executeCommand('ssh-agent ssh-add '.$keyFile.' && git clone -b ' .$build->getBranch() . ' ' .$this->getCloneUrl().' '.$to.' && ssh-agent -k');
|
||||
|
||||
// Remove the key file:
|
||||
unlink($keyFile);
|
||||
|
||||
return $success;
|
||||
}
|
||||
}
|
|
@ -16,23 +16,4 @@ use PHPCI\Model\Base\ProjectBase;
|
|||
*/
|
||||
class Project extends ProjectBase
|
||||
{
|
||||
public function getGitUrl()
|
||||
{
|
||||
$key = $this->getGitKey();
|
||||
|
||||
switch($this->getType() . '.' . (!empty($key) ? 'ssh' : 'http'))
|
||||
{
|
||||
case 'github.ssh':
|
||||
return 'git@github.com:' . $this->getReference() . '.git';
|
||||
|
||||
case 'github.http':
|
||||
return 'https://github.com/' . $this->getReference() . '.git';
|
||||
|
||||
case 'bitbucket.ssh':
|
||||
return 'git@bitbucket.org:' . $this->getReference() . '.git';
|
||||
|
||||
case 'bitbucket.http':
|
||||
return 'https://bitbucket.org/' . $this->getReference() . '.git';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue