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(); // 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()); $map = $this->buildRepositoryMap($build_target); $lease->setAttribute('repositories.map', $map); $task_id = $this->getCurrentWorkerTaskID(); if ($task_id) { $lease->setAwakenTaskIDs(array($task_id)); } $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) { $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())); }
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 static function initializeNewBuildLog(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildLog())->setBuildTargetPHID($build_target->getPHID())->setDuration(null)->setLive(0); }
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(); }
public static function initializeNewBuildArtifact(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildArtifact())->setBuildTargetPHID($build_target->getPHID()); }
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 initializeNewLintMessage(HarbormasterBuildTarget $build_target) { return id(new HarbormasterBuildLintMessage())->setBuildTargetPHID($build_target->getPHID()); }