Allow PHPCI to mark builds as failed if the builder crashed, after a user-defined timeout. Limit to one build per project in RunCommand.

Fixes #623
This commit is contained in:
Dan Cryer 2014-12-01 17:04:03 +00:00
parent 28292967a8
commit f562de55c7
5 changed files with 120 additions and 14 deletions

View file

@ -36,11 +36,6 @@ class Builder implements LoggerAwareInterface
*/
public $ignore = array();
/**
* @var string
*/
protected $ciDir;
/**
* @var string
*/
@ -287,10 +282,7 @@ class Builder implements LoggerAwareInterface
*/
protected function setupBuild()
{
$buildId = 'project' . $this->build->getProject()->getId()
. '-build' . $this->build->getId();
$this->ciDir = dirname(dirname(__FILE__) . '/../') . '/';
$this->buildPath = $this->ciDir . 'build/' . $buildId . '/';
$this->buildPath = PHPCI_DIR . 'PHPCI/build/' . $this->build->getId() . '/';
$this->build->currentBuildPath = $this->buildPath;
$this->interpolator->setupInterpolationVars(

View file

@ -75,7 +75,7 @@ class DaemoniseCommand extends Command
$this->run = true;
$this->sleep = 0;
$runner = new RunCommand($this->logger);
$runner->setBaxBuilds(1);
$runner->setMaxBuilds(1);
$emptyInput = new ArgvInput(array());

View file

@ -9,6 +9,7 @@
namespace PHPCI\Command;
use b8\Config;
use Monolog\Logger;
use PHPCI\Logging\BuildDBLogHandler;
use PHPCI\Logging\LoggedBuildContextTidier;
@ -73,14 +74,15 @@ class RunCommand extends Command
// For verbose mode we want to output all informational and above
// messages to the symphony output interface.
if ($input->hasOption('verbose')) {
if ($input->getOption('verbose')) {
$this->logger->pushHandler(
new OutputLogHandler($this->output, Logger::INFO)
);
}
$this->logger->pushProcessor(new LoggedBuildContextTidier());
$running = $this->validateRunningBuilds();
$this->logger->pushProcessor(new LoggedBuildContextTidier());
$this->logger->addInfo("Finding builds to process");
$store = Factory::getStore('Build');
$result = $store->getByStatus(0, $this->maxBuilds);
@ -89,10 +91,17 @@ class RunCommand extends Command
$builds = 0;
foreach ($result['items'] as $build) {
$builds++;
$build = BuildFactory::getBuild($build);
// Skip build (for now) if there's already a build running in that project:
if (in_array($build->getProjectId(), $running)) {
$this->logger->addInfo('Skipping Build #'.$build->getId() . ' - Project build already in progress.');
continue;
}
$builds++;
try {
// Logging relevant to this build should be stored
// against the build itself.
@ -118,8 +127,53 @@ class RunCommand extends Command
return $builds;
}
public function setBaxBuilds($numBuilds)
public function setMaxBuilds($numBuilds)
{
$this->maxBuilds = (int)$numBuilds;
}
protected function validateRunningBuilds()
{
/** @var \PHPCI\Store\BuildStore $store */
$store = Factory::getStore('Build');
$running = $store->getByStatus(1);
$rtn = array();
$timeout = Config::getInstance()->get('phpci.build.failed_after', 1800);
foreach ($running['items'] as $build) {
/** @var \PHPCI\Model\Build $build */
$build = BuildFactory::getBuild($build);
$now = time();
$start = $build->getStarted()->getTimestamp();
if (($now - $start) > $timeout) {
$this->logger->addInfo('Build #'.$build->getId().' marked as failed due to timeout.');
$build->setStatus(Build::STATUS_FAILED);
$store->save($build);
$this->removeBuildDirectory($build);
continue;
}
$rtn[$build->getProjectId()] = true;
}
return $rtn;
}
protected function removeBuildDirectory($build)
{
$buildPath = PHPCI_DIR . 'PHPCI/build/' . $build->getId() . '/';
if (is_dir($buildPath)) {
$cmd = 'rm -Rf "%s"';
if (IS_WIN) {
$cmd = 'rmdir /S /Q "%s"';
}
shell_exec($cmd);
}
}
}

View file

@ -45,8 +45,14 @@ class SettingsController extends Controller
$emailSettings = $this->settings['phpci']['email_settings'];
}
$buildSettings = array();
if (isset($this->settings['phpci']['build'])) {
$buildSettings = $this->settings['phpci']['build'];
}
$this->view->github = $this->getGithubForm();
$this->view->emailSettings = $this->getEmailForm($emailSettings);
$this->view->buildSettings = $this->getBuildForm($buildSettings);
$this->view->isWriteable = $this->canWriteConfig();
if (!empty($this->settings['phpci']['github']['token'])) {
@ -87,6 +93,21 @@ class SettingsController extends Controller
die;
}
public function build()
{
$this->settings['phpci']['build'] = $this->getParams();
$error = $this->storeSettings();
if ($error) {
header('Location: ' . PHPCI_URL . 'settings?saved=2');
} else {
header('Location: ' . PHPCI_URL . 'settings?saved=1');
}
die;
}
/**
* Github redirects users back to this URL when t
*/
@ -249,4 +270,36 @@ class SettingsController extends Controller
{
return is_writeable(APPLICATION_PATH . 'PHPCI/config.yml');
}
protected function getBuildForm($values = array())
{
$form = new Form();
$form->setMethod('POST');
$form->setAction(PHPCI_URL . 'settings/build');
$field = new Form\Element\Select('failed_after');
$field->setRequired(false);
$field->setLabel('Consider a build failed after');
$field->setClass('form-control');
$field->setContainerClass('form-group');
$field->setOptions([
300 => '5 Minutes',
900 => '15 Minutes',
1800 => '30 Minutes',
3600 => '1 Hour',
10800 => '3 Hours',
]);
$field->setValue(1800);
$form->addField($field);
$field = new Form\Element\Submit();
$field->setValue('Save »');
$field->setClass('btn btn-success pull-right');
$form->addField($field);
$form->setValues($values);
return $form;
}
}

View file

@ -28,6 +28,13 @@
</p>
<?php endif; ?>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Build Settings</h3></div>
<div class="panel-body">
<?php print $buildSettings; ?>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Github Application</h3></div>
<div class="panel-body">