public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $uri = $settings['uri'] . '/httpAuth/app/rest/buildQueue'; $method = 'POST'; $contentType = 'application/xml'; $xmlBuilder = new TeamCityXmlBuildBuilder(); $payload = $xmlBuilder->addBuildId($settings['buildId'])->addBranchName(implode(array("D", $variables['buildable.diff'])))->addDiffId(implode(array("D", $variables['buildable.diff'])))->addHarbormasterPHID($variables['target.phid'])->addRevisionId($variables['buildable.revision'])->build(); $future = id(new HTTPFuture($uri, $payload))->setMethod($method)->addHeader('Content-Type', $contentType)->setTimeout(60); $credential_phid = $this->getSetting('credential'); if ($credential_phid) { $key = PassphrasePasswordKey::loadFromPHID($credential_phid, $viewer); $future->setHTTPBasicAuthCredentials($key->getUsernameEnvelope()->openEnvelope(), $key->getPasswordEnvelope()); } $this->resolveFutures($build, $build_target, array($future)); list($status, $body, $headers) = $future->resolve(); $header_lines = array(); // TODO: We don't currently preserve the entire "HTTP" response header, but // should. Once we do, reproduce it here faithfully. $status_code = $status->getStatusCode(); $header_lines[] = "HTTP {$status_code}"; foreach ($headers as $header) { list($head, $tail) = $header; $header_lines[] = "{$head}: {$tail}"; } $header_lines = implode("\n", $header_lines); $build_target->newLog($uri, 'http.head')->append($header_lines); $build_target->newLog($uri, 'http.body')->append($body); if ($status->isError()) { throw new HarbormasterBuildFailureException(); } }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $uri = $this->mergeVariables('vurisprintf', $settings['uri'], $variables); $method = nonempty(idx($settings, 'method'), 'POST'); $future = id(new HTTPSFuture($uri))->setMethod($method)->setTimeout(60); $credential_phid = $this->getSetting('credential'); if ($credential_phid) { $key = PassphrasePasswordKey::loadFromPHID($credential_phid, $viewer); $future->setHTTPBasicAuthCredentials($key->getUsernameEnvelope()->openEnvelope(), $key->getPasswordEnvelope()); } $this->resolveFutures($build, $build_target, array($future)); list($status, $body, $headers) = $future->resolve(); $header_lines = array(); // TODO: We don't currently preserve the entire "HTTP" response header, but // should. Once we do, reproduce it here faithfully. $status_code = $status->getStatusCode(); $header_lines[] = "HTTP {$status_code}"; foreach ($headers as $header) { list($head, $tail) = $header; $header_lines[] = "{$head}: {$tail}"; } $header_lines = implode("\n", $header_lines); $build_target->newLog($uri, 'http.head')->append($header_lines); $build_target->newLog($uri, 'http.body')->append($body); if ($status->isError()) { throw new HarbormasterBuildFailureException(); } }
protected function loadPage() { $table = new HarbormasterBuildTarget(); $conn_r = $table->establishConnection('r'); $data = queryfx_all($conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); }
public static function getArtifactIndex(HarbormasterBuildTarget $target, $artifact_key) { $build = $target->getBuild(); $parts = array($build->getPHID(), $target->getBuildGeneration(), $artifact_key); $parts = implode("", $parts); return PhabricatorHash::digestForIndex($parts); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { // We can only wait when building against commits. $buildable = $build->getBuildable(); $object = $buildable->getBuildableObject(); if (!$object instanceof PhabricatorRepositoryCommit) { return; } // Block until all previous builds of the same build plan have // finished. $plan = $build->getBuildPlan(); $existing_logs = id(new HarbormasterBuildLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withBuildTargetPHIDs(array($build_target->getPHID()))->execute(); if ($existing_logs) { $log = head($existing_logs); } else { $log = $build->createLog($build_target, 'waiting', 'blockers'); } $blockers = $this->getBlockers($object, $plan, $build); if ($blockers) { $log->start(); $log->append(pht("Blocked by: %s\n", implode(',', $blockers))); $log->finalize(); } if ($blockers) { throw new PhabricatorWorkerYieldException(15); } }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $artifact = $build_target->loadArtifact($settings['hostartifact']); $impl = $artifact->getArtifactImplementation(); $lease = $impl->loadArtifactLease($viewer); $this->platform = $lease->getAttribute('platform'); $command = $this->mergeVariables(array($this, 'escapeCommand'), $settings['command'], $variables); $this->platform = null; $interface = $lease->getInterface('command'); $future = $interface->getExecFuture('%C', $command); $log_stdout = $build->createLog($build_target, 'remote', 'stdout'); $log_stderr = $build->createLog($build_target, 'remote', 'stderr'); $start_stdout = $log_stdout->start(); $start_stderr = $log_stderr->start(); $build_update = 5; // Read the next amount of available output every second. $futures = new FutureIterator(array($future)); foreach ($futures->setUpdateInterval(1) as $key => $future_iter) { if ($future_iter === null) { // Check to see if we should abort. if ($build_update <= 0) { $build->reload(); if ($this->shouldAbort($build, $build_target)) { $future->resolveKill(); throw new HarbormasterBuildAbortedException(); } else { $build_update = 5; } } else { $build_update -= 1; } // Command is still executing. // Read more data as it is available. list($stdout, $stderr) = $future->read(); $log_stdout->append($stdout); $log_stderr->append($stderr); $future->discardBuffers(); } else { // Command execution is complete. // Get the return value so we can log that as well. list($err) = $future->resolve(); // Retrieve the last few bits of information. list($stdout, $stderr) = $future->read(); $log_stdout->append($stdout); $log_stderr->append($stderr); $future->discardBuffers(); break; } } $log_stdout->finalize($start_stdout); $log_stderr->finalize($start_stderr); if ($err) { throw new HarbormasterBuildFailureException(); } }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $settings = $this->getSettings(); // Create the lease. $lease = id(new DrydockLease())->setResourceType('host')->setOwnerPHID($build_target->getPHID())->setAttributes(array('platform' => $settings['platform']))->queueForActivation(); // Wait until the lease is fulfilled. // TODO: This will throw an exception if the lease can't be fulfilled; // we should treat that as build failure not build error. $lease->waitUntilActive(); // Create the associated artifact. $artifact = $build_target->createArtifact(PhabricatorUser::getOmnipotentUser(), $settings['name'], HarbormasterHostArtifact::ARTIFACTCONST, array('drydockLeasePHID' => $lease->getPHID())); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $settings = $this->getSettings(); $variables = $build_target->getVariables(); $path = $this->mergeVariables('vsprintf', $settings['path'], $variables); $artifact = $build_target->loadArtifact($settings['hostartifact']); $lease = $artifact->loadDrydockLease(); $interface = $lease->getInterface('filesystem'); // TODO: Handle exceptions. $file = $interface->saveFile($path, $settings['name']); // Insert the artifact record. $artifact = $build_target->createArtifact(PhabricatorUser::getOmnipotentUser(), $settings['name'], HarbormasterFileArtifact::ARTIFACTCONST, array('filePHID' => $file->getPHID())); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $settings = $this->getSettings(); $variables = $build_target->getVariables(); $path = $this->mergeVariables('vsprintf', $settings['path'], $variables); $artifact = $build->loadArtifact($settings['hostartifact']); $lease = $artifact->loadDrydockLease(); $interface = $lease->getInterface('filesystem'); // TODO: Handle exceptions. $file = $interface->saveFile($path, $settings['name']); // Insert the artifact record. $artifact = $build->createArtifact($build_target, $settings['name'], HarbormasterBuildArtifact::TYPE_FILE); $artifact->setArtifactData(array('filePHID' => $file->getPHID())); $artifact->save(); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $settings = $this->getSettings(); $variables = $build_target->getVariables(); $path = $this->mergeVariables('vsprintf', $settings['path'], $variables); $artifact = $build->loadArtifact($settings['artifact']); $file = $artifact->loadPhabricatorFile(); $fragment = id(new PhragmentFragmentQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPaths(array($path))->executeOne(); if ($fragment === null) { PhragmentFragment::createFromFile(PhabricatorUser::getOmnipotentUser(), $file, $path, PhabricatorPolicies::getMostOpenPolicy(), PhabricatorPolicies::POLICY_USER); } else { if ($file->getMimeType() === 'application/zip') { $fragment->updateFromZIP(PhabricatorUser::getOmnipotentUser(), $file); } else { $fragment->updateFromFile(PhabricatorUser::getOmnipotentUser(), $file); } } }
private function buildRepositoryMap(HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $variables = $build_target->getVariables(); $repository_phid = idx($variables, 'repository.phid'); if (!$repository_phid) { throw new Exception(pht('Unable to determine how to clone the repository for this ' . 'buildable: it is not associated with a tracked repository.')); } $also_phids = $build_target->getFieldValue('repositoryPHIDs'); if (!is_array($also_phids)) { $also_phids = array(); } $all_phids = $also_phids; $all_phids[] = $repository_phid; $repositories = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs($all_phids)->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($all_phids as $phid) { if (empty($repositories[$phid])) { throw new PhabricatorWorkerPermanentFailureException(pht('Unable to load repository with PHID "%s".', $phid)); } } $map = array(); foreach ($also_phids as $also_phid) { $also_repo = $repositories[$also_phid]; $map[$also_repo->getCloneName()] = array('phid' => $also_repo->getPHID(), 'branch' => 'master'); } $repository = $repositories[$repository_phid]; $commit = idx($variables, 'buildable.commit'); $ref_uri = idx($variables, 'repository.staging.uri'); $ref_ref = idx($variables, 'repository.staging.ref'); if ($commit) { $spec = array('commit' => $commit); } else { if ($ref_uri && $ref_ref) { $spec = array('ref' => array('uri' => $ref_uri, 'ref' => $ref_ref)); } else { throw new Exception(pht('Unable to determine how to fetch changes: this buildable does not ' . 'identify a commit or a staging ref. You may need to configure a ' . 'repository staging area.')); } } $directory = $repository->getCloneName(); $map[$directory] = array('phid' => $repository->getPHID(), 'default' => true) + $spec; return $map; }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $uri = $this->mergeVariables('vurisprintf', $settings['uri'], $variables); $method = nonempty(idx($settings, 'method'), 'POST'); $future = id(new HTTPSFuture($uri))->setMethod($method)->setTimeout(60); $credential_phid = $this->getSetting('credential'); if ($credential_phid) { $key = PassphrasePasswordKey::loadFromPHID($credential_phid, $viewer); $future->setHTTPBasicAuthCredentials($key->getUsernameEnvelope()->openEnvelope(), $key->getPasswordEnvelope()); } $this->resolveFutures($build, $build_target, array($future)); $this->logHTTPResponse($build, $build_target, $future, $uri); list($status) = $future->resolve(); if ($status->isError()) { throw new HarbormasterBuildFailureException(); } }
private function updateTarget(HarbormasterBuildTarget $target, array $payload) { $step = $target->getBuildStep(); $impl = $step->getStepImplementation(); if (!$impl instanceof HarbormasterCircleCIBuildStepImplementation) { throw new Exception(pht('Build target ("%s") has the wrong type of build step. Only ' . 'CircleCI build steps may be updated via the CircleCI webhook.', $target->getPHID())); } switch (idx($payload, 'status')) { case 'success': case 'fixed': $message_type = HarbormasterMessageType::MESSAGE_PASS; break; default: $message_type = HarbormasterMessageType::MESSAGE_FAIL; break; } $viewer = PhabricatorUser::getOmnipotentUser(); $api_method = 'harbormaster.sendmessage'; $api_params = array('buildTargetPHID' => $target->getPHID(), 'type' => $message_type); id(new ConduitCall($api_method, $api_params))->setUser($viewer)->execute(); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); // TODO: We should probably have a separate temporary storage area for // execution stuff that doesn't step on configuration state? $lease_phid = $build_target->getDetail('exec.leasePHID'); if ($lease_phid) { $lease = id(new DrydockLeaseQuery())->setViewer($viewer)->withPHIDs(array($lease_phid))->executeOne(); if (!$lease) { throw new PhabricatorWorkerPermanentFailureException(pht('Lease "%s" could not be loaded.', $lease_phid)); } } else { $working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())->getType(); $lease = id(new DrydockLease())->setResourceType($working_copy_type)->setOwnerPHID($build_target->getPHID()); $variables = $build_target->getVariables(); $repository_phid = idx($variables, 'repository.phid'); $commit = idx($variables, 'repository.commit'); $lease->setAttribute('repositoryPHID', $repository_phid)->setAttribute('commit', $commit); $lease->queueForActivation(); $build_target->setDetail('exec.leasePHID', $lease->getPHID())->save(); } if ($lease->isActivating()) { // TODO: Smart backoff? throw new PhabricatorWorkerYieldException(15); } if (!$lease->isActive()) { // TODO: We could just forget about this lease and retry? throw new PhabricatorWorkerPermanentFailureException(pht('Lease "%s" never activated.', $lease->getPHID())); } $artifact = $build_target->createArtifact($viewer, $settings['name'], HarbormasterWorkingCopyArtifact::ARTIFACTCONST, array('drydockLeasePHID' => $lease->getPHID())); }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $uri = $this->mergeVariables('vurisprintf', $settings['uri'], $variables); $log_body = $build->createLog($build_target, $uri, 'http-body'); $start = $log_body->start(); $method = nonempty(idx($settings, 'method'), 'POST'); $future = id(new HTTPSFuture($uri))->setMethod($method)->setTimeout(60); $credential_phid = $this->getSetting('credential'); if ($credential_phid) { $key = PassphrasePasswordKey::loadFromPHID($credential_phid, $viewer); $future->setHTTPBasicAuthCredentials($key->getUsernameEnvelope()->openEnvelope(), $key->getPasswordEnvelope()); } list($status, $body, $headers) = $this->resolveFuture($build, $build_target, $future); $log_body->append($body); $log_body->finalize($start); if ($status->getStatusCode() != 200) { $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); } }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $settings = $this->getSettings(); $variables = $build_target->getVariables(); $artifact = $build_target->loadArtifact($settings['artifact']); $impl = $artifact->getArtifactImplementation(); $lease = $impl->loadArtifactLease($viewer); // TODO: Require active lease. $command = $this->mergeVariables('vcsprintf', $settings['command'], $variables); $interface = $lease->getInterface(DrydockCommandInterface::INTERFACE_TYPE); $exec_future = $interface->getExecFuture('%C', $command); $harbor_future = id(new HarbormasterExecFuture())->setFuture($exec_future)->setLogs($build_target->newLog('remote', 'stdout'), $build_target->newLog('remote', 'stderr')); $this->resolveFutures($build, $build_target, array($harbor_future)); list($err) = $harbor_future->resolve(); if ($err) { throw new HarbormasterBuildFailureException(); } }
private function updateBuildSteps(HarbormasterBuild $build) { $targets = id(new HarbormasterBuildTargetQuery())->setViewer($this->getViewer())->withBuildPHIDs(array($build->getPHID()))->withBuildGenerations(array($build->getBuildGeneration()))->execute(); $this->updateWaitingTargets($targets); $targets = mgroup($targets, 'getBuildStepPHID'); $steps = id(new HarbormasterBuildStepQuery())->setViewer($this->getViewer())->withBuildPlanPHIDs(array($build->getBuildPlan()->getPHID()))->execute(); // Identify steps which are in various states. $queued = array(); $underway = array(); $waiting = array(); $complete = array(); $failed = array(); foreach ($steps as $step) { $step_targets = idx($targets, $step->getPHID(), array()); if ($step_targets) { $is_queued = false; $is_underway = false; foreach ($step_targets as $target) { if ($target->isUnderway()) { $is_underway = true; break; } } $is_waiting = false; foreach ($step_targets as $target) { if ($target->isWaiting()) { $is_waiting = true; break; } } $is_complete = true; foreach ($step_targets as $target) { if (!$target->isComplete()) { $is_complete = false; break; } } $is_failed = false; foreach ($step_targets as $target) { if ($target->isFailed()) { $is_failed = true; break; } } } else { $is_queued = true; $is_underway = false; $is_waiting = false; $is_complete = false; $is_failed = false; } if ($is_queued) { $queued[$step->getPHID()] = true; } if ($is_underway) { $underway[$step->getPHID()] = true; } if ($is_waiting) { $waiting[$step->getPHID()] = true; } if ($is_complete) { $complete[$step->getPHID()] = true; } if ($is_failed) { $failed[$step->getPHID()] = true; } } // If any step failed, fail the whole build, then bail. if (count($failed)) { $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); $build->save(); return; } // If every step is complete, we're done with this build. Mark it passed // and bail. if (count($complete) == count($steps)) { $build->setBuildStatus(HarbormasterBuild::STATUS_PASSED); $build->save(); return; } // Identify all the steps which are ready to run (because all their // dependencies are complete). $runnable = array(); foreach ($steps as $step) { $dependencies = $step->getStepImplementation()->getDependencies($step); if (isset($queued[$step->getPHID()])) { $can_run = true; foreach ($dependencies as $dependency) { if (empty($complete[$dependency])) { $can_run = false; break; } } if ($can_run) { $runnable[] = $step; } } } if (!$runnable && !$waiting && !$underway) { // This means the build is deadlocked, and the user has configured // circular dependencies. $build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED); $build->save(); return; } foreach ($runnable as $runnable_step) { $target = HarbormasterBuildTarget::initializeNewBuildTarget($build, $runnable_step, $build->retrieveVariablesFromBuild()); $target->save(); $this->queueNewBuildTarget($target); } }
private function buildBuildList(HarbormasterBuildable $buildable) { $viewer = $this->getRequest()->getUser(); $build_list = id(new PHUIObjectItemListView())->setUser($viewer); foreach ($buildable->getBuilds() as $build) { $view_uri = $this->getApplicationURI('/build/' . $build->getID() . '/'); $item = id(new PHUIObjectItemView())->setObjectName(pht('Build %d', $build->getID()))->setHeader($build->getName())->setHref($view_uri); $status = $build->getBuildStatus(); $item->setStatusIcon('fa-dot-circle-o ' . HarbormasterBuild::getBuildStatusColor($status), HarbormasterBuild::getBuildStatusName($status)); $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); if ($build->isRestarting()) { $item->addIcon('fa-repeat', pht('Restarting')); } else { if ($build->isPausing()) { $item->addIcon('fa-pause', pht('Pausing')); } else { if ($build->isResuming()) { $item->addIcon('fa-play', pht('Resuming')); } } } $build_id = $build->getID(); $restart_uri = "build/restart/{$build_id}/buildable/"; $resume_uri = "build/resume/{$build_id}/buildable/"; $pause_uri = "build/pause/{$build_id}/buildable/"; $abort_uri = "build/abort/{$build_id}/buildable/"; $item->addAction(id(new PHUIListItemView())->setIcon('fa-repeat')->setName(pht('Restart'))->setHref($this->getApplicationURI($restart_uri))->setWorkflow(true)->setDisabled(!$build->canRestartBuild())); if ($build->canResumeBuild()) { $item->addAction(id(new PHUIListItemView())->setIcon('fa-play')->setName(pht('Resume'))->setHref($this->getApplicationURI($resume_uri))->setWorkflow(true)); } else { $item->addAction(id(new PHUIListItemView())->setIcon('fa-pause')->setName(pht('Pause'))->setHref($this->getApplicationURI($pause_uri))->setWorkflow(true)->setDisabled(!$build->canPauseBuild())); } $targets = $build->getBuildTargets(); if ($targets) { $target_list = id(new PHUIStatusListView()); foreach ($targets as $target) { $status = $target->getTargetStatus(); $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); $status_name = HarbormasterBuildTarget::getBuildTargetStatusName($status); $name = $target->getName(); $target_list->addItem(id(new PHUIStatusItemView())->setIcon($icon, $color, $status_name)->setTarget(pht('Target %d', $target->getID()))->setNote($name)); } $target_box = id(new PHUIBoxView())->addPadding(PHUI::PADDING_SMALL)->appendChild($target_list); $item->appendChild($target_box); } $build_list->addItem($item); } $build_list->setFlush(true); $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Builds'))->appendChild($build_list); return $box; }
public static function initializeNewLintMessage(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildLintMessage())->setBuildTargetPHID($build_target->getPHID()); }
protected function shouldAbort(HarbormasterBuild $build, HarbormasterBuildTarget $target) { return $build->getBuildGeneration() !== $target->getBuildGeneration(); }
private function buildLog(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $request = $this->getRequest(); $viewer = $request->getUser(); $limit = $request->getInt('l', 25); $logs = id(new HarbormasterBuildLogQuery())->setViewer($viewer)->withBuildTargetPHIDs(array($build_target->getPHID()))->execute(); $empty_logs = array(); $log_boxes = array(); foreach ($logs as $log) { $start = 1; $lines = preg_split("/\r\n|\r|\n/", $log->getLogText()); if ($limit !== 0) { $start = count($lines) - $limit; if ($start >= 1) { $lines = array_slice($lines, -$limit, $limit); } else { $start = 1; } } $id = null; $is_empty = false; if (count($lines) === 1 && trim($lines[0]) === '') { // Prevent Harbormaster from showing empty build logs. $id = celerity_generate_unique_node_id(); $empty_logs[] = $id; $is_empty = true; } $log_view = new ShellLogView(); $log_view->setLines($lines); $log_view->setStart($start); $header = id(new PHUIHeaderView())->setHeader(pht('Build Log %d (%s - %s)', $log->getID(), $log->getLogSource(), $log->getLogType()))->setSubheader($this->createLogHeader($build, $log))->setUser($viewer); $log_box = id(new PHUIObjectBoxView())->setHeader($header)->setForm($log_view); if ($is_empty) { $log_box = phutil_tag('div', array('style' => 'display: none', 'id' => $id), $log_box); } $log_boxes[] = $log_box; } if ($empty_logs) { $hide_id = celerity_generate_unique_node_id(); Javelin::initBehavior('phabricator-reveal-content'); $expand = phutil_tag('div', array('id' => $hide_id, 'class' => 'harbormaster-empty-logs-are-hidden mlr mlt mll'), array(pht('%s empty logs are hidden.', new PhutilNumber(count($empty_logs))), ' ', javelin_tag('a', array('href' => '#', 'sigil' => 'reveal-content', 'meta' => array('showIDs' => $empty_logs, 'hideIDs' => array($hide_id))), pht('Show all logs.')))); array_unshift($log_boxes, $expand); } return $log_boxes; }
public static function initializeNewBuildLog(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildLog())->setBuildTargetPHID($build_target->getPHID())->setDuration(null)->setLive(0); }
private function loadHistoryDiffStatus(array $diffs) { assert_instances_of($diffs, 'DifferentialDiff'); $diff_phids = mpull($diffs, 'getPHID'); $bad_unit_status = array(ArcanistUnitTestResult::RESULT_FAIL, ArcanistUnitTestResult::RESULT_BROKEN); $message = new HarbormasterBuildUnitMessage(); $target = new HarbormasterBuildTarget(); $build = new HarbormasterBuild(); $buildable = new HarbormasterBuildable(); $broken_diffs = queryfx_all($message->establishConnection('r'), 'SELECT distinct a.buildablePHID FROM %T m JOIN %T t ON m.buildTargetPHID = t.phid JOIN %T b ON t.buildPHID = b.phid JOIN %T a ON b.buildablePHID = a.phid WHERE a.buildablePHID IN (%Ls) AND m.result in (%Ls)', $message->getTableName(), $target->getTableName(), $build->getTableName(), $buildable->getTableName(), $diff_phids, $bad_unit_status); $unit_status = array(); foreach ($broken_diffs as $broken) { $phid = $broken['buildablePHID']; $unit_status[$phid] = DifferentialUnitStatus::UNIT_FAIL; } return $unit_status; }
public function execute(HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); $buildable = $build->getBuildable(); $object = $buildable->getBuildableObject(); $object_phid = $object->getPHID(); if (!$object instanceof HarbormasterCircleCIBuildableInterface) { throw new Exception(pht('Object ("%s") does not implement interface "%s". Only objects ' . 'which implement this interface can be built with CircleCI.', $object_phid, 'HarbormasterCircleCIBuildableInterface')); } $github_uri = $object->getCircleCIGitHubRepositoryURI(); $build_type = $object->getCircleCIBuildIdentifierType(); $build_identifier = $object->getCircleCIBuildIdentifier(); $path = self::getGitHubPath($github_uri); if ($path === null) { throw new Exception(pht('Object ("%s") claims "%s" is a GitHub repository URI, but the ' . 'domain does not appear to be GitHub.', $object_phid, $github_uri)); } $path_parts = trim($path, '/'); $path_parts = explode('/', $path_parts); if (count($path_parts) < 2) { throw new Exception(pht('Object ("%s") claims "%s" is a GitHub repository URI, but the ' . 'path ("%s") does not have enough components (expected at least ' . 'two).', $object_phid, $github_uri, $path)); } list($github_namespace, $github_name) = $path_parts; $github_name = preg_replace('(\\.git$)', '', $github_name); $credential_phid = $this->getSetting('token'); $api_token = id(new PassphraseCredentialQuery())->setViewer($viewer)->withPHIDs(array($credential_phid))->needSecrets(true)->executeOne(); if (!$api_token) { throw new Exception(pht('Unable to load API token ("%s")!', $credential_phid)); } // When we pass "revision", the branch is ignored (and does not even need // to exist), and only shows up in the UI. Use a cute string which will // certainly never break anything or cause any kind of problem. $ship = "🚢"; $branch = "{$ship}Harbormaster"; $token = $api_token->getSecret()->openEnvelope(); $parts = array('https://circleci.com/api/v1/project', phutil_escape_uri($github_namespace), phutil_escape_uri($github_name) . "?circle-token={$token}"); $uri = implode('/', $parts); $data_structure = array(); switch ($build_type) { case 'tag': $data_structure['tag'] = $build_identifier; break; case 'revision': $data_structure['revision'] = $build_identifier; break; default: throw new Exception(pht('Unknown CircleCI build type "%s". Expected "%s" or "%s".', $build_type, 'tag', 'revision')); } $data_structure['build_parameters'] = array('HARBORMASTER_BUILD_TARGET_PHID' => $build_target->getPHID()); $json_data = phutil_json_encode($data_structure); $future = id(new HTTPSFuture($uri, $json_data))->setMethod('POST')->addHeader('Content-Type', 'application/json')->addHeader('Accept', 'application/json')->setTimeout(60); $this->resolveFutures($build, $build_target, array($future)); $this->logHTTPResponse($build, $build_target, $future, pht('CircleCI')); list($status, $body) = $future->resolve(); if ($status->isError()) { throw new HarbormasterBuildFailureException(); } $response = phutil_json_decode($body); $build_uri = idx($response, 'build_url'); if (!$build_uri) { throw new Exception(pht('CircleCI did not return a "%s"!', 'build_url')); } $target_phid = $build_target->getPHID(); // Write an artifact to create a link to the external build in CircleCI. $api_method = 'harbormaster.createartifact'; $api_params = array('buildTargetPHID' => $target_phid, 'artifactType' => HarbormasterURIArtifact::ARTIFACTCONST, 'artifactKey' => 'circleci.uri', 'artifactData' => array('uri' => $build_uri, 'name' => pht('View in CircleCI'), 'ui.external' => true)); id(new ConduitCall($api_method, $api_params))->setUser($viewer)->execute(); }
protected function logHTTPResponse(HarbormasterBuild $build, HarbormasterBuildTarget $build_target, BaseHTTPFuture $future, $label) { list($status, $body, $headers) = $future->resolve(); $header_lines = array(); // TODO: We don't currently preserve the entire "HTTP" response header, but // should. Once we do, reproduce it here faithfully. $status_code = $status->getStatusCode(); $header_lines[] = "HTTP {$status_code}"; foreach ($headers as $header) { list($head, $tail) = $header; $header_lines[] = "{$head}: {$tail}"; } $header_lines = implode("\n", $header_lines); $build_target->newLog($label, 'http.head')->append($header_lines); $build_target->newLog($label, 'http.body')->append($body); }
public static function initializeNewBuildArtifact(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildArtifact())->setBuildTargetPHID($build_target->getPHID()); }