Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/PHPCensor/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,16 @@ public function getBuildProjectTitle()
*/
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();
Expand Down
8 changes: 8 additions & 0 deletions src/PHPCensor/BuilderException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace PHPCensor;

class BuilderException extends \Exception {
/** Fail start build - non fatal */
const FAIL_START = 1;
}
44 changes: 29 additions & 15 deletions src/PHPCensor/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use b8\Store\Factory;
use PHPCensor\Builder;
use PHPCensor\BuilderException;
use PHPCensor\BuildFactory;
use PHPCensor\Model\Build;

Expand Down Expand Up @@ -95,38 +96,51 @@ protected function execute(InputInterface $input, OutputInterface $output)
$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());
$build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage());
$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.');
Expand Down
21 changes: 21 additions & 0 deletions src/PHPCensor/Model/Build.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,27 @@ public function setStatus($value)
$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.
*
Expand Down
20 changes: 20 additions & 0 deletions src/PHPCensor/Store/BuildStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,24 @@ public function setMeta($projectId, $buildId, $key, $value)
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;
}
}
}
44 changes: 33 additions & 11 deletions src/PHPCensor/Worker/BuildWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Pheanstalk\Job;
use Pheanstalk\Pheanstalk;
use PHPCensor\Builder;
use PHPCensor\BuilderException;
use PHPCensor\BuildFactory;
use PHPCensor\Logging\BuildDBLogHandler;
use PHPCensor\Model\Build;
Expand Down Expand Up @@ -107,27 +108,40 @@ public function startWorker()
} 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:

@corpsee corpsee Apr 5, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started 4 builds and all builds hangs in status Build::STATUS_RUNNING with message 'Can`t build - status is not pending' in the middle of build log.

// 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());
Expand All @@ -136,13 +150,21 @@ public function startWorker()
$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);
}
}
}

Expand Down