Merge pull request #51 from ss-gxp/fix-builder
Fix build execute - in some cases one build executed with many process
This commit is contained in:
commit
0109e49f8e
|
@ -175,8 +175,16 @@ class Builder implements LoggerAwareInterface
|
|||
*/
|
||||
public function execute()
|
||||
{
|
||||
// check current status
|
||||
if ($this->build->getStatus() != Build::STATUS_PENDING) {
|
||||
throw new BuilderException('Can`t build - status is not pending', BuilderException::FAIL_START);
|
||||
}
|
||||
// set status only if current status pending
|
||||
if (!$this->build->setStatusSync(Build::STATUS_RUNNING)) {
|
||||
throw new BuilderException('Can`t build - unable change status to running', BuilderException::FAIL_START);
|
||||
}
|
||||
|
||||
// Update the build in the database, ping any external services.
|
||||
$this->build->setStatus(Build::STATUS_RUNNING);
|
||||
$this->build->setStarted(new \DateTime());
|
||||
$this->store->save($this->build);
|
||||
$this->build->sendStatusPostback();
|
||||
|
|
8
src/PHPCensor/BuilderException.php
Normal file
8
src/PHPCensor/BuilderException.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace PHPCensor;
|
||||
|
||||
class BuilderException extends \Exception {
|
||||
/** Fail start build - non fatal */
|
||||
const FAIL_START = 1;
|
||||
}
|
|
@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use b8\Store\Factory;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\BuilderException;
|
||||
use PHPCensor\BuildFactory;
|
||||
use PHPCensor\Model\Build;
|
||||
|
||||
|
@ -95,31 +96,36 @@ class RunCommand extends Command
|
|||
$build = BuildFactory::getBuild($build);
|
||||
|
||||
// Skip build (for now) if there's already a build running in that project:
|
||||
if (in_array($build->getProjectId(), $running)) {
|
||||
if (!empty($running[$build->getProjectId()])) {
|
||||
$this->logger->addInfo(sprintf('Skipping Build %d - Project build already in progress.', $build->getId()));
|
||||
$result['items'][] = $build;
|
||||
|
||||
// Re-run build validator:
|
||||
$running = $this->validateRunningBuilds();
|
||||
continue;
|
||||
}
|
||||
|
||||
$builds++;
|
||||
|
||||
try {
|
||||
// Logging relevant to this build should be stored
|
||||
// against the build itself.
|
||||
$buildDbLog = new BuildDBLogHandler($build, Logger::INFO);
|
||||
$this->logger->pushHandler($buildDbLog);
|
||||
// Logging relevant to this build should be stored
|
||||
// against the build itself.
|
||||
$buildDbLog = new BuildDBLogHandler($build, Logger::INFO);
|
||||
$this->logger->pushHandler($buildDbLog);
|
||||
|
||||
try {
|
||||
$builder = new Builder($build, $this->logger);
|
||||
$builder->execute();
|
||||
|
||||
// After execution we no longer want to record the information
|
||||
// back to this specific build so the handler should be removed.
|
||||
$this->logger->popHandler();
|
||||
// destructor implicitly call flush
|
||||
unset($buildDbLog);
|
||||
} catch (BuilderException $ex) {
|
||||
$this->logger->addError($ex->getMessage());
|
||||
switch($ex->getCode()) {
|
||||
case BuilderException::FAIL_START:
|
||||
// non fatal
|
||||
break;
|
||||
default:
|
||||
$build->setStatus(Build::STATUS_FAILED);
|
||||
$build->setFinished(new \DateTime());
|
||||
$build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage());
|
||||
$store->save($build);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (\Exception $ex) {
|
||||
$build->setStatus(Build::STATUS_FAILED);
|
||||
$build->setFinished(new \DateTime());
|
||||
|
@ -127,6 +133,14 @@ class RunCommand extends Command
|
|||
$store->save($build);
|
||||
}
|
||||
|
||||
// After execution we no longer want to record the information
|
||||
// back to this specific build so the handler should be removed.
|
||||
$this->logger->popHandler();
|
||||
// destructor implicitly call flush
|
||||
unset($buildDbLog);
|
||||
|
||||
// Re-run build validator:
|
||||
$running = $this->validateRunningBuilds();
|
||||
}
|
||||
|
||||
$this->logger->addInfo('Finished processing builds.');
|
||||
|
|
|
@ -410,6 +410,27 @@ class Build extends Model
|
|||
$this->setModified('status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of Status / status only if it synced with db. Must not be null.
|
||||
*
|
||||
* @param $value int
|
||||
* @return bool
|
||||
*/
|
||||
public function setStatusSync($value)
|
||||
{
|
||||
$this->validateNotNull('Status', $value);
|
||||
$this->validateInt('Status', $value);
|
||||
|
||||
if ($this->data['status'] !== $value) {
|
||||
$store = Factory::getStore('Build');
|
||||
if ($store->updateStatusSync($this, $value)) {
|
||||
$this->data['status'] = $value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of Log / log.
|
||||
*
|
||||
|
|
|
@ -310,4 +310,24 @@ class BuildStore extends Store
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status only if it synced with db
|
||||
* @param Build $build
|
||||
* @param int $status
|
||||
* @return bool
|
||||
*/
|
||||
public function updateStatusSync($build, $status)
|
||||
{
|
||||
try {
|
||||
$query = 'UPDATE {{build}} SET status = :status_new WHERE {{id}} = :id AND {{status}} = :status_current';
|
||||
$stmt = Database::getConnection('write')->prepareCommon($query);
|
||||
$stmt->bindValue(':id', $build->getId(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':status_current', $build->getStatus(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':status_new', $status, \PDO::PARAM_INT);
|
||||
return ($stmt->execute() and ($stmt->rowCount() == 1));
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use Monolog\Logger;
|
|||
use Pheanstalk\Job;
|
||||
use Pheanstalk\Pheanstalk;
|
||||
use PHPCensor\Builder;
|
||||
use PHPCensor\BuilderException;
|
||||
use PHPCensor\BuildFactory;
|
||||
use PHPCensor\Logging\BuildDBLogHandler;
|
||||
use PHPCensor\Model\Build;
|
||||
|
@ -107,27 +108,40 @@ class BuildWorker
|
|||
} catch (\Exception $ex) {
|
||||
$this->logger->addWarning('Build #' . $jobData['build_id'] . ' does not exist in the database.');
|
||||
$this->pheanstalk->delete($job);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Logging relevant to this build should be stored
|
||||
// against the build itself.
|
||||
$buildDbLog = new BuildDBLogHandler($build, Logger::INFO);
|
||||
$this->logger->pushHandler($buildDbLog);
|
||||
// Logging relevant to this build should be stored
|
||||
// against the build itself.
|
||||
$buildDbLog = new BuildDBLogHandler($build, Logger::INFO);
|
||||
$this->logger->pushHandler($buildDbLog);
|
||||
|
||||
try {
|
||||
$builder = new Builder($build, $this->logger);
|
||||
$builder->execute();
|
||||
|
||||
// After execution we no longer want to record the information
|
||||
// back to this specific build so the handler should be removed.
|
||||
$this->logger->popHandler();
|
||||
// destructor implicitly call flush
|
||||
unset($buildDbLog);
|
||||
} catch (BuilderException $ex) {
|
||||
$this->logger->addError($ex->getMessage());
|
||||
switch($ex->getCode()) {
|
||||
case BuilderException::FAIL_START:
|
||||
// non fatal
|
||||
$this->pheanstalk->release($job);
|
||||
unset($job);
|
||||
break;
|
||||
default:
|
||||
$build->setStatus(Build::STATUS_FAILED);
|
||||
$build->setFinished(new \DateTime());
|
||||
$build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage());
|
||||
$buildStore->save($build);
|
||||
$build->sendStatusPostback();
|
||||
break;
|
||||
}
|
||||
} catch (\PDOException $ex) {
|
||||
// If we've caught a PDO Exception, it is probably not the fault of the build, but of a failed
|
||||
// connection or similar. Release the job and kill the worker.
|
||||
$this->run = false;
|
||||
$this->pheanstalk->release($job);
|
||||
unset($job);
|
||||
} catch (\Exception $ex) {
|
||||
$build->setStatus(Build::STATUS_FAILED);
|
||||
$build->setFinished(new \DateTime());
|
||||
|
@ -136,13 +150,21 @@ class BuildWorker
|
|||
$build->sendStatusPostback();
|
||||
}
|
||||
|
||||
// After execution we no longer want to record the information
|
||||
// back to this specific build so the handler should be removed.
|
||||
$this->logger->popHandler();
|
||||
// destructor implicitly call flush
|
||||
unset($buildDbLog);
|
||||
|
||||
// Reset the config back to how it was prior to running this job:
|
||||
if (!empty($currentConfig)) {
|
||||
Database::reset();
|
||||
}
|
||||
|
||||
// Delete the job when we're done:
|
||||
$this->pheanstalk->delete($job);
|
||||
if (!empty($job)) {
|
||||
$this->pheanstalk->delete($job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue