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

View file

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

View file

@ -9,6 +9,7 @@
namespace PHPCI\Command; namespace PHPCI\Command;
use b8\Config;
use Monolog\Logger; use Monolog\Logger;
use PHPCI\Logging\BuildDBLogHandler; use PHPCI\Logging\BuildDBLogHandler;
use PHPCI\Logging\LoggedBuildContextTidier; use PHPCI\Logging\LoggedBuildContextTidier;
@ -73,14 +74,15 @@ class RunCommand extends Command
// For verbose mode we want to output all informational and above // For verbose mode we want to output all informational and above
// messages to the symphony output interface. // messages to the symphony output interface.
if ($input->hasOption('verbose')) { if ($input->getOption('verbose')) {
$this->logger->pushHandler( $this->logger->pushHandler(
new OutputLogHandler($this->output, Logger::INFO) 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"); $this->logger->addInfo("Finding builds to process");
$store = Factory::getStore('Build'); $store = Factory::getStore('Build');
$result = $store->getByStatus(0, $this->maxBuilds); $result = $store->getByStatus(0, $this->maxBuilds);
@ -89,10 +91,17 @@ class RunCommand extends Command
$builds = 0; $builds = 0;
foreach ($result['items'] as $build) { foreach ($result['items'] as $build) {
$builds++;
$build = BuildFactory::getBuild($build); $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 { try {
// Logging relevant to this build should be stored // Logging relevant to this build should be stored
// against the build itself. // against the build itself.
@ -118,8 +127,53 @@ class RunCommand extends Command
return $builds; return $builds;
} }
public function setBaxBuilds($numBuilds) public function setMaxBuilds($numBuilds)
{ {
$this->maxBuilds = (int)$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']; $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->github = $this->getGithubForm();
$this->view->emailSettings = $this->getEmailForm($emailSettings); $this->view->emailSettings = $this->getEmailForm($emailSettings);
$this->view->buildSettings = $this->getBuildForm($buildSettings);
$this->view->isWriteable = $this->canWriteConfig(); $this->view->isWriteable = $this->canWriteConfig();
if (!empty($this->settings['phpci']['github']['token'])) { if (!empty($this->settings['phpci']['github']['token'])) {
@ -87,6 +93,21 @@ class SettingsController extends Controller
die; 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 * 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'); 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> </p>
<?php endif; ?> <?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 panel-default">
<div class="panel-heading"><h3 class="panel-title">Github Application</h3></div> <div class="panel-heading"><h3 class="panel-title">Github Application</h3></div>
<div class="panel-body"> <div class="panel-body">