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:
parent
28292967a8
commit
f562de55c7
|
@ -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(
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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">
|
||||||
|
|
Loading…
Reference in a new issue