* @package PHPCI * @subpackage Console */ class RunCommand extends Command { /** * @var OutputInterface */ protected $output; /** * @var Logger */ protected $logger; /** * @var int */ protected $maxBuilds = null; /** * @param \Monolog\Logger $logger * @param string $name */ public function __construct(Logger $logger, $name = null) { parent::__construct($name); $this->logger = $logger; } protected function configure() { $this ->setName('phpci:run-builds') ->setDescription('Run all pending PHPCI builds.') ->addOption('verbose', 'v', InputOption::VALUE_NONE); } /** * Pulls all pending builds from the database and runs them. */ protected function execute(InputInterface $input, OutputInterface $output) { $this->output = $output; // For verbose mode we want to output all informational and above // messages to the symphony output interface. if ($input->getOption('verbose')) { $this->logger->pushHandler( new OutputLogHandler($this->output, Logger::INFO) ); } $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); $this->logger->addInfo(sprintf("Found %d builds", count($result['items']))); $builds = 0; foreach ($result['items'] as $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 { // Logging relevant to this build should be stored // against the build itself. $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); $this->logger->pushHandler($buildDbLog); $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($buildDbLog); } catch (\Exception $ex) { $build->setStatus(Build::STATUS_FAILED); $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); $store->save($build); } } $this->logger->addInfo("Finished processing builds"); return $builds; } 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); } } }