Path: blob/master/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
12256 views
<?php12/**3* @task autotarget Automatic Targets4*/5abstract class HarbormasterBuildStepImplementation extends Phobject {67private $settings;8private $currentWorkerTaskID;910public function setCurrentWorkerTaskID($id) {11$this->currentWorkerTaskID = $id;12return $this;13}1415public function getCurrentWorkerTaskID() {16return $this->currentWorkerTaskID;17}1819public static function getImplementations() {20return id(new PhutilClassMapQuery())21->setAncestorClass(__CLASS__)22->execute();23}2425public static function getImplementation($class) {26$base = idx(self::getImplementations(), $class);2728if ($base) {29return (clone $base);30}3132return null;33}3435public static function requireImplementation($class) {36if (!$class) {37throw new Exception(pht('No implementation is specified!'));38}3940$implementation = self::getImplementation($class);41if (!$implementation) {42throw new Exception(pht('No such implementation "%s" exists!', $class));43}4445return $implementation;46}4748/**49* The name of the implementation.50*/51abstract public function getName();5253public function getBuildStepGroupKey() {54return HarbormasterOtherBuildStepGroup::GROUPKEY;55}5657/**58* The generic description of the implementation.59*/60public function getGenericDescription() {61return '';62}6364/**65* The description of the implementation, based on the current settings.66*/67public function getDescription() {68return $this->getGenericDescription();69}7071public function getEditInstructions() {72return null;73}7475/**76* Run the build target against the specified build.77*/78abstract public function execute(79HarbormasterBuild $build,80HarbormasterBuildTarget $build_target);8182/**83* Gets the settings for this build step.84*/85public function getSettings() {86return $this->settings;87}8889public function getSetting($key, $default = null) {90return idx($this->settings, $key, $default);91}9293/**94* Loads the settings for this build step implementation from a build95* step or target.96*/97final public function loadSettings($build_object) {98$this->settings = $build_object->getDetails();99return $this;100}101102/**103* Return the name of artifacts produced by this command.104*105* Future steps will calculate all available artifact mappings106* before them and filter on the type.107*108* @return array The mappings of artifact names to their types.109*/110public function getArtifactInputs() {111return array();112}113114public function getArtifactOutputs() {115return array();116}117118public function getDependencies(HarbormasterBuildStep $build_step) {119$dependencies = $build_step->getDetail('dependsOn', array());120121$inputs = $build_step->getStepImplementation()->getArtifactInputs();122$inputs = ipull($inputs, null, 'key');123124$artifacts = $this->getAvailableArtifacts(125$build_step->getBuildPlan(),126$build_step,127null);128129foreach ($artifacts as $key => $type) {130if (!array_key_exists($key, $inputs)) {131unset($artifacts[$key]);132}133}134135$artifact_steps = ipull($artifacts, 'step');136$artifact_steps = mpull($artifact_steps, 'getPHID');137138$dependencies = array_merge($dependencies, $artifact_steps);139140return $dependencies;141}142143/**144* Returns a list of all artifacts made available in the build plan.145*/146public static function getAvailableArtifacts(147HarbormasterBuildPlan $build_plan,148$current_build_step,149$artifact_type) {150151$steps = id(new HarbormasterBuildStepQuery())152->setViewer(PhabricatorUser::getOmnipotentUser())153->withBuildPlanPHIDs(array($build_plan->getPHID()))154->execute();155156$artifacts = array();157158$artifact_arrays = array();159foreach ($steps as $step) {160if ($current_build_step !== null &&161$step->getPHID() === $current_build_step->getPHID()) {162163continue;164}165166$implementation = $step->getStepImplementation();167$array = $implementation->getArtifactOutputs();168$array = ipull($array, 'type', 'key');169foreach ($array as $name => $type) {170if ($type !== $artifact_type && $artifact_type !== null) {171continue;172}173$artifacts[$name] = array('type' => $type, 'step' => $step);174}175}176177return $artifacts;178}179180/**181* Convert a user-provided string with variables in it, like:182*183* ls ${dirname}184*185* ...into a string with variables merged into it safely:186*187* ls 'dir with spaces'188*189* @param string Name of a `vxsprintf` function, like @{function:vcsprintf}.190* @param string User-provided pattern string containing `${variables}`.191* @param dict List of available replacement variables.192* @return string String with variables replaced safely into it.193*/194protected function mergeVariables($function, $pattern, array $variables) {195$regexp = '@\\$\\{(?P<name>[a-z\\./_-]+)\\}@';196197$matches = null;198preg_match_all($regexp, $pattern, $matches);199200$argv = array();201foreach ($matches['name'] as $name) {202if (!array_key_exists($name, $variables)) {203throw new Exception(pht("No such variable '%s'!", $name));204}205$argv[] = $variables[$name];206}207208$pattern = str_replace('%', '%%', $pattern);209$pattern = preg_replace($regexp, '%s', $pattern);210211return call_user_func($function, $pattern, $argv);212}213214public function getFieldSpecifications() {215return array();216}217218protected function formatSettingForDescription($key, $default = null) {219return $this->formatValueForDescription($this->getSetting($key, $default));220}221222protected function formatValueForDescription($value) {223if (strlen($value)) {224return phutil_tag('strong', array(), $value);225} else {226return phutil_tag('em', array(), pht('(null)'));227}228}229230public function supportsWaitForMessage() {231return false;232}233234public function shouldWaitForMessage(HarbormasterBuildTarget $target) {235if (!$this->supportsWaitForMessage()) {236return false;237}238239$wait = $target->getDetail('builtin.wait-for-message');240return ($wait == 'wait');241}242243protected function shouldAbort(244HarbormasterBuild $build,245HarbormasterBuildTarget $target) {246247return $build->getBuildGeneration() !== $target->getBuildGeneration();248}249250protected function resolveFutures(251HarbormasterBuild $build,252HarbormasterBuildTarget $target,253array $futures) {254255$did_close = false;256$wait_start = PhabricatorTime::getNow();257258$futures = new FutureIterator($futures);259foreach ($futures->setUpdateInterval(5) as $key => $future) {260if ($future !== null) {261continue;262}263264$build->reload();265if ($this->shouldAbort($build, $target)) {266throw new HarbormasterBuildAbortedException();267}268269// See PHI916. If we're waiting on a remote system for a while, clean270// up database connections to reduce the cost of having a large number271// of processes babysitting an `ssh ... ./run-huge-build.sh` process on272// a build host.273if (!$did_close) {274$now = PhabricatorTime::getNow();275$elapsed = ($now - $wait_start);276$idle_limit = 5;277278if ($elapsed >= $idle_limit) {279LiskDAO::closeIdleConnections();280$did_close = true;281}282}283}284285}286287protected function logHTTPResponse(288HarbormasterBuild $build,289HarbormasterBuildTarget $build_target,290BaseHTTPFuture $future,291$label) {292293list($status, $body, $headers) = $future->resolve();294295$header_lines = array();296297// TODO: We don't currently preserve the entire "HTTP" response header, but298// should. Once we do, reproduce it here faithfully.299$status_code = $status->getStatusCode();300$header_lines[] = "HTTP {$status_code}";301302foreach ($headers as $header) {303list($head, $tail) = $header;304$header_lines[] = "{$head}: {$tail}";305}306$header_lines = implode("\n", $header_lines);307308$build_target309->newLog($label, 'http.head')310->append($header_lines);311312$build_target313->newLog($label, 'http.body')314->append($body);315}316317protected function logSilencedCall(318HarbormasterBuild $build,319HarbormasterBuildTarget $build_target,320$label) {321322$build_target323->newLog($label, 'silenced')324->append(325pht(326'Declining to make service call because `phabricator.silent` is '.327'enabled in configuration.'));328}329330public function willStartBuild(331PhabricatorUser $viewer,332HarbormasterBuildable $buildable,333HarbormasterBuild $build,334HarbormasterBuildPlan $plan,335HarbormasterBuildStep $step) {336return;337}338339340/* -( Automatic Targets )-------------------------------------------------- */341342343public function getBuildStepAutotargetStepKey() {344return null;345}346347public function getBuildStepAutotargetPlanKey() {348throw new PhutilMethodNotImplementedException();349}350351public function shouldRequireAutotargeting() {352return false;353}354355}356357358