|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* PHPCI - Continuous Integration for PHP. |
|
4
|
|
|
* |
|
5
|
|
|
* @copyright Copyright 2014, Block 8 Limited. |
|
6
|
|
|
* @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md |
|
7
|
|
|
* |
|
8
|
|
|
* @link https://www.phptesting.org/ |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
namespace PHPCI\Command; |
|
12
|
|
|
|
|
13
|
|
|
use b8\Config; |
|
14
|
|
|
use Monolog\Logger; |
|
15
|
|
|
use PHPCI\Helper\Lang; |
|
16
|
|
|
use PHPCI\Logging\BuildDBLogHandler; |
|
17
|
|
|
use PHPCI\Logging\LoggedBuildContextTidier; |
|
18
|
|
|
use PHPCI\Logging\OutputLogHandler; |
|
19
|
|
|
use Symfony\Component\Console\Command\Command; |
|
20
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
|
21
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
|
22
|
|
|
use b8\Store\Factory; |
|
23
|
|
|
use PHPCI\Builder; |
|
24
|
|
|
use PHPCI\BuildFactory; |
|
25
|
|
|
use PHPCI\Model\Build; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* Run console command - Runs any pending builds. |
|
29
|
|
|
* |
|
30
|
|
|
* @author Dan Cryer <[email protected]> |
|
31
|
|
|
*/ |
|
32
|
|
|
class RunCommand extends Command |
|
33
|
|
|
{ |
|
34
|
|
|
/** |
|
35
|
|
|
* @var OutputInterface |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $output; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @var Logger |
|
41
|
|
|
*/ |
|
42
|
|
|
protected $logger; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @var int |
|
46
|
|
|
*/ |
|
47
|
|
|
protected $maxBuilds = 100; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var bool |
|
51
|
|
|
*/ |
|
52
|
|
|
protected $isFromDaemon = false; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @param \Monolog\Logger $logger |
|
56
|
|
|
* @param string $name |
|
57
|
|
|
*/ |
|
58
|
|
|
public function __construct(Logger $logger, $name = null) |
|
59
|
|
|
{ |
|
60
|
|
|
parent::__construct($name); |
|
61
|
|
|
$this->logger = $logger; |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
protected function configure() |
|
65
|
|
|
{ |
|
66
|
|
|
$this |
|
67
|
|
|
->setName('phpci:run-builds') |
|
68
|
|
|
->setDescription(Lang::get('run_all_pending')); |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
/** |
|
72
|
|
|
* Pulls all pending builds from the database and runs them. |
|
73
|
|
|
*/ |
|
74
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
|
75
|
|
|
{ |
|
76
|
|
|
$this->output = $output; |
|
77
|
|
|
|
|
78
|
|
|
// For verbose mode we want to output all informational and above |
|
79
|
|
|
// messages to the symphony output interface. |
|
80
|
|
|
if ($input->hasOption('verbose') && $input->getOption('verbose')) { |
|
81
|
|
|
$this->logger->pushHandler( |
|
82
|
|
|
new OutputLogHandler($this->output, Logger::INFO) |
|
83
|
|
|
); |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
$running = $this->validateRunningBuilds(); |
|
87
|
|
|
|
|
88
|
|
|
$this->logger->pushProcessor(new LoggedBuildContextTidier()); |
|
89
|
|
|
$this->logger->addInfo(Lang::get('finding_builds')); |
|
90
|
|
|
$store = Factory::getStore('Build'); |
|
91
|
|
|
$result = $store->getByStatus(0, $this->maxBuilds); |
|
92
|
|
|
$this->logger->addInfo(Lang::get('found_n_builds', count($result['items']))); |
|
93
|
|
|
|
|
94
|
|
|
$builds = 0; |
|
95
|
|
|
|
|
96
|
|
|
while (count($result['items'])) { |
|
97
|
|
|
$build = array_shift($result['items']); |
|
98
|
|
|
$build = BuildFactory::getBuild($build); |
|
99
|
|
|
|
|
100
|
|
|
// Skip build (for now) if there's already a build running in that project: |
|
101
|
|
|
if (!$this->isFromDaemon && in_array($build->getProjectId(), $running)) { |
|
102
|
|
|
$this->logger->addInfo(Lang::get('skipping_build', $build->getId())); |
|
103
|
|
|
$result['items'][] = $build; |
|
104
|
|
|
|
|
105
|
|
|
// Re-run build validator: |
|
106
|
|
|
$running = $this->validateRunningBuilds(); |
|
107
|
|
|
continue; |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
++$builds; |
|
111
|
|
|
|
|
112
|
|
|
try { |
|
113
|
|
|
// Logging relevant to this build should be stored |
|
114
|
|
|
// against the build itself. |
|
115
|
|
|
$buildDbLog = new BuildDBLogHandler($build, Logger::INFO); |
|
116
|
|
|
$this->logger->pushHandler($buildDbLog); |
|
117
|
|
|
|
|
118
|
|
|
$builder = new Builder($build, $this->logger); |
|
119
|
|
|
$builder->execute(); |
|
120
|
|
|
|
|
121
|
|
|
// After execution we no longer want to record the information |
|
122
|
|
|
// back to this specific build so the handler should be removed. |
|
123
|
|
|
$this->logger->popHandler($buildDbLog); |
|
|
|
|
|
|
124
|
|
|
} catch (\Exception $ex) { |
|
125
|
|
|
$build->setStatus(Build::STATUS_FAILED); |
|
126
|
|
|
$build->setFinished(new \DateTime()); |
|
127
|
|
|
$build->setLog($build->getLog().PHP_EOL.PHP_EOL.$ex->getMessage()); |
|
128
|
|
|
$store->save($build); |
|
129
|
|
|
} |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
$this->logger->addInfo(Lang::get('finished_processing_builds')); |
|
133
|
|
|
|
|
134
|
|
|
return $builds; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
public function setMaxBuilds($numBuilds) |
|
138
|
|
|
{ |
|
139
|
|
|
$this->maxBuilds = (int) $numBuilds; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
public function setDaemon($fromDaemon) |
|
143
|
|
|
{ |
|
144
|
|
|
$this->isFromDaemon = (bool) $fromDaemon; |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
protected function validateRunningBuilds() |
|
148
|
|
|
{ |
|
149
|
|
|
/** @var \PHPCI\Store\BuildStore $store */ |
|
150
|
|
|
$store = Factory::getStore('Build'); |
|
151
|
|
|
$running = $store->getByStatus(1); |
|
152
|
|
|
$rtn = array(); |
|
153
|
|
|
|
|
154
|
|
|
$timeout = Config::getInstance()->get('phpci.build.failed_after', 1800); |
|
155
|
|
|
|
|
156
|
|
|
foreach ($running['items'] as $build) { |
|
157
|
|
|
/** @var \PHPCI\Model\Build $build */ |
|
158
|
|
|
$build = BuildFactory::getBuild($build); |
|
159
|
|
|
|
|
160
|
|
|
$now = time(); |
|
161
|
|
|
$start = $build->getStarted()->getTimestamp(); |
|
162
|
|
|
|
|
163
|
|
|
if (($now - $start) > $timeout) { |
|
164
|
|
|
$this->logger->addInfo(Lang::get('marked_as_failed', $build->getId())); |
|
165
|
|
|
$build->setStatus(Build::STATUS_FAILED); |
|
166
|
|
|
$build->setFinished(new \DateTime()); |
|
167
|
|
|
$store->save($build); |
|
168
|
|
|
$build->removeBuildDirectory(); |
|
169
|
|
|
continue; |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
$rtn[$build->getProjectId()] = true; |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
return $rtn; |
|
176
|
|
|
} |
|
177
|
|
|
} |
|
178
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.