Remove product literal strings in "pht()", part 6
[phabricator.git] / src / applications / harbormaster / step / HarbormasterBuildStepImplementation.php
blob3bbb72cd9549dfd4ce61243a72a462f5c20572e3
1 <?php
3 /**
4 * @task autotarget Automatic Targets
5 */
6 abstract class HarbormasterBuildStepImplementation extends Phobject {
8 private $settings;
9 private $currentWorkerTaskID;
11 public function setCurrentWorkerTaskID($id) {
12 $this->currentWorkerTaskID = $id;
13 return $this;
16 public function getCurrentWorkerTaskID() {
17 return $this->currentWorkerTaskID;
20 public static function getImplementations() {
21 return id(new PhutilClassMapQuery())
22 ->setAncestorClass(__CLASS__)
23 ->execute();
26 public static function getImplementation($class) {
27 $base = idx(self::getImplementations(), $class);
29 if ($base) {
30 return (clone $base);
33 return null;
36 public static function requireImplementation($class) {
37 if (!$class) {
38 throw new Exception(pht('No implementation is specified!'));
41 $implementation = self::getImplementation($class);
42 if (!$implementation) {
43 throw new Exception(pht('No such implementation "%s" exists!', $class));
46 return $implementation;
49 /**
50 * The name of the implementation.
52 abstract public function getName();
54 public function getBuildStepGroupKey() {
55 return HarbormasterOtherBuildStepGroup::GROUPKEY;
58 /**
59 * The generic description of the implementation.
61 public function getGenericDescription() {
62 return '';
65 /**
66 * The description of the implementation, based on the current settings.
68 public function getDescription() {
69 return $this->getGenericDescription();
72 public function getEditInstructions() {
73 return null;
76 /**
77 * Run the build target against the specified build.
79 abstract public function execute(
80 HarbormasterBuild $build,
81 HarbormasterBuildTarget $build_target);
83 /**
84 * Gets the settings for this build step.
86 public function getSettings() {
87 return $this->settings;
90 public function getSetting($key, $default = null) {
91 return idx($this->settings, $key, $default);
94 /**
95 * Loads the settings for this build step implementation from a build
96 * step or target.
98 final public function loadSettings($build_object) {
99 $this->settings = $build_object->getDetails();
100 return $this;
104 * Return the name of artifacts produced by this command.
106 * Future steps will calculate all available artifact mappings
107 * before them and filter on the type.
109 * @return array The mappings of artifact names to their types.
111 public function getArtifactInputs() {
112 return array();
115 public function getArtifactOutputs() {
116 return array();
119 public function getDependencies(HarbormasterBuildStep $build_step) {
120 $dependencies = $build_step->getDetail('dependsOn', array());
122 $inputs = $build_step->getStepImplementation()->getArtifactInputs();
123 $inputs = ipull($inputs, null, 'key');
125 $artifacts = $this->getAvailableArtifacts(
126 $build_step->getBuildPlan(),
127 $build_step,
128 null);
130 foreach ($artifacts as $key => $type) {
131 if (!array_key_exists($key, $inputs)) {
132 unset($artifacts[$key]);
136 $artifact_steps = ipull($artifacts, 'step');
137 $artifact_steps = mpull($artifact_steps, 'getPHID');
139 $dependencies = array_merge($dependencies, $artifact_steps);
141 return $dependencies;
145 * Returns a list of all artifacts made available in the build plan.
147 public static function getAvailableArtifacts(
148 HarbormasterBuildPlan $build_plan,
149 $current_build_step,
150 $artifact_type) {
152 $steps = id(new HarbormasterBuildStepQuery())
153 ->setViewer(PhabricatorUser::getOmnipotentUser())
154 ->withBuildPlanPHIDs(array($build_plan->getPHID()))
155 ->execute();
157 $artifacts = array();
159 $artifact_arrays = array();
160 foreach ($steps as $step) {
161 if ($current_build_step !== null &&
162 $step->getPHID() === $current_build_step->getPHID()) {
164 continue;
167 $implementation = $step->getStepImplementation();
168 $array = $implementation->getArtifactOutputs();
169 $array = ipull($array, 'type', 'key');
170 foreach ($array as $name => $type) {
171 if ($type !== $artifact_type && $artifact_type !== null) {
172 continue;
174 $artifacts[$name] = array('type' => $type, 'step' => $step);
178 return $artifacts;
182 * Convert a user-provided string with variables in it, like:
184 * ls ${dirname}
186 * ...into a string with variables merged into it safely:
188 * ls 'dir with spaces'
190 * @param string Name of a `vxsprintf` function, like @{function:vcsprintf}.
191 * @param string User-provided pattern string containing `${variables}`.
192 * @param dict List of available replacement variables.
193 * @return string String with variables replaced safely into it.
195 protected function mergeVariables($function, $pattern, array $variables) {
196 $regexp = '@\\$\\{(?P<name>[a-z\\./_-]+)\\}@';
198 $matches = null;
199 preg_match_all($regexp, $pattern, $matches);
201 $argv = array();
202 foreach ($matches['name'] as $name) {
203 if (!array_key_exists($name, $variables)) {
204 throw new Exception(pht("No such variable '%s'!", $name));
206 $argv[] = $variables[$name];
209 $pattern = str_replace('%', '%%', $pattern);
210 $pattern = preg_replace($regexp, '%s', $pattern);
212 return call_user_func($function, $pattern, $argv);
215 public function getFieldSpecifications() {
216 return array();
219 protected function formatSettingForDescription($key, $default = null) {
220 return $this->formatValueForDescription($this->getSetting($key, $default));
223 protected function formatValueForDescription($value) {
224 if (strlen($value)) {
225 return phutil_tag('strong', array(), $value);
226 } else {
227 return phutil_tag('em', array(), pht('(null)'));
231 public function supportsWaitForMessage() {
232 return false;
235 public function shouldWaitForMessage(HarbormasterBuildTarget $target) {
236 if (!$this->supportsWaitForMessage()) {
237 return false;
240 $wait = $target->getDetail('builtin.wait-for-message');
241 return ($wait == 'wait');
244 protected function shouldAbort(
245 HarbormasterBuild $build,
246 HarbormasterBuildTarget $target) {
248 return $build->getBuildGeneration() !== $target->getBuildGeneration();
251 protected function resolveFutures(
252 HarbormasterBuild $build,
253 HarbormasterBuildTarget $target,
254 array $futures) {
256 $did_close = false;
257 $wait_start = PhabricatorTime::getNow();
259 $futures = new FutureIterator($futures);
260 foreach ($futures->setUpdateInterval(5) as $key => $future) {
261 if ($future !== null) {
262 continue;
265 $build->reload();
266 if ($this->shouldAbort($build, $target)) {
267 throw new HarbormasterBuildAbortedException();
270 // See PHI916. If we're waiting on a remote system for a while, clean
271 // up database connections to reduce the cost of having a large number
272 // of processes babysitting an `ssh ... ./run-huge-build.sh` process on
273 // a build host.
274 if (!$did_close) {
275 $now = PhabricatorTime::getNow();
276 $elapsed = ($now - $wait_start);
277 $idle_limit = 5;
279 if ($elapsed >= $idle_limit) {
280 LiskDAO::closeIdleConnections();
281 $did_close = true;
288 protected function logHTTPResponse(
289 HarbormasterBuild $build,
290 HarbormasterBuildTarget $build_target,
291 BaseHTTPFuture $future,
292 $label) {
294 list($status, $body, $headers) = $future->resolve();
296 $header_lines = array();
298 // TODO: We don't currently preserve the entire "HTTP" response header, but
299 // should. Once we do, reproduce it here faithfully.
300 $status_code = $status->getStatusCode();
301 $header_lines[] = "HTTP {$status_code}";
303 foreach ($headers as $header) {
304 list($head, $tail) = $header;
305 $header_lines[] = "{$head}: {$tail}";
307 $header_lines = implode("\n", $header_lines);
309 $build_target
310 ->newLog($label, 'http.head')
311 ->append($header_lines);
313 $build_target
314 ->newLog($label, 'http.body')
315 ->append($body);
318 protected function logSilencedCall(
319 HarbormasterBuild $build,
320 HarbormasterBuildTarget $build_target,
321 $label) {
323 $build_target
324 ->newLog($label, 'silenced')
325 ->append(
326 pht(
327 'Declining to make service call because `phabricator.silent` is '.
328 'enabled in configuration.'));
331 public function willStartBuild(
332 PhabricatorUser $viewer,
333 HarbormasterBuildable $buildable,
334 HarbormasterBuild $build,
335 HarbormasterBuildPlan $plan,
336 HarbormasterBuildStep $step) {
337 return;
341 /* -( Automatic Targets )-------------------------------------------------- */
344 public function getBuildStepAutotargetStepKey() {
345 return null;
348 public function getBuildStepAutotargetPlanKey() {
349 throw new PhutilMethodNotImplementedException();
352 public function shouldRequireAutotargeting() {
353 return false;