protected function loadPage() { $table = new HarbormasterBuildable(); $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); }
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->setBarColor(HarbormasterBuild::getBuildStatusColor($status)); $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); if ($build->isRestarting()) { $item->addIcon('fa-repeat', pht('Restarting')); } else { if ($build->isStopping()) { $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/"; $stop_uri = "build/stop/{$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($stop_uri))->setWorkflow(true)->setDisabled(!$build->canStopBuild())); } $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); } return $build_list; }
public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $names = $args->getArg('buildable'); if (count($names) != 1) { throw new PhutilArgumentUsageException(pht('Specify exactly one buildable object, by object name.')); } $name = head($names); $buildable = id(new PhabricatorObjectQuery())->setViewer($viewer)->withNames($names)->executeOne(); if (!$buildable) { throw new PhutilArgumentUsageException(pht('No such buildable "%s"!', $name)); } if (!$buildable instanceof HarbormasterBuildableInterface) { throw new PhutilArgumentUsageException(pht('Object "%s" is not a buildable!', $name)); } $plan_id = $args->getArg('plan'); if (!$plan_id) { throw new PhutilArgumentUsageException(pht('Use --plan to specify a build plan to run.')); } $plan = id(new HarbormasterBuildPlanQuery())->setViewer($viewer)->withIDs(array($plan_id))->executeOne(); if (!$plan) { throw new PhutilArgumentUsageException(pht('Build plan "%s" does not exist.', $plan_id)); } $console = PhutilConsole::getConsole(); $buildable = HarbormasterBuildable::initializeNewBuildable($viewer)->setIsManualBuildable(true)->setBuildablePHID($buildable->getHarbormasterBuildablePHID())->setContainerPHID($buildable->getHarbormasterContainerPHID())->save(); $console->writeOut("%s\n", pht('Applying plan %s to new buildable %s...', $plan->getID(), 'B' . $buildable->getID())); $console->writeOut("\n %s\n\n", PhabricatorEnv::getProductionURI('/B' . $buildable->getID())); PhabricatorWorker::setRunAllTasksInProcess(true); $buildable->applyPlan($plan); $console->writeOut("%s\n", pht('Done.')); return 0; }
private function handlePropertyEvent($ui_event) { $user = $ui_event->getUser(); $object = $ui_event->getValue('object'); if (!$object || !$object->getPHID()) { // No object, or the object has no PHID yet.. return; } if ($object instanceof HarbormasterBuildable) { // Although HarbormasterBuildable implements the correct interface, it // does not make sense to show a build's build status. In the best case // it is meaningless, and in the worst case it's confusing. return; } if ($object instanceof DifferentialRevision) { // TODO: This is a bit hacky and we could probably find a cleaner fix // eventually, but we show build status on each diff, immediately below // this property list, so it's redundant to show it on the revision view. return; } if (!$object instanceof HarbormasterBuildableInterface) { return; } $buildable_phid = $object->getHarbormasterBuildablePHID(); if (!$buildable_phid) { return; } if (!$this->canUseApplication($ui_event->getUser())) { return; } $buildable = id(new HarbormasterBuildableQuery())->setViewer($user)->withManualBuildables(false)->withBuildablePHIDs(array($buildable_phid))->needBuilds(true)->executeOne(); if (!$buildable) { return; } $builds = $buildable->getBuilds(); $build_handles = id(new PhabricatorHandleQuery())->setViewer($user)->withPHIDs(mpull($builds, 'getPHID'))->execute(); $status_view = new PHUIStatusListView(); $buildable_status = $buildable->getBuildableStatus(); $buildable_icon = HarbormasterBuildable::getBuildableStatusIcon($buildable_status); $buildable_color = HarbormasterBuildable::getBuildableStatusColor($buildable_status); $buildable_name = HarbormasterBuildable::getBuildableStatusName($buildable_status); $target = phutil_tag('a', array('href' => '/' . $buildable->getMonogram()), pht('Buildable %d', $buildable->getID())); $target = phutil_tag('strong', array(), $target); $status_view->addItem(id(new PHUIStatusItemView())->setIcon($buildable_icon, $buildable_color, $buildable_name)->setTarget($target)); foreach ($builds as $build) { $item = new PHUIStatusItemView(); $item->setTarget($build_handles[$build->getPHID()]->renderLink()); $status = $build->getBuildStatus(); $status_name = HarbormasterBuild::getBuildStatusName($status); $icon = HarbormasterBuild::getBuildStatusIcon($status); $color = HarbormasterBuild::getBuildStatusColor($status); $item->setIcon($icon, $color, $status_name); $status_view->addItem($item); } $view = $ui_event->getValue('view'); $view->addProperty(pht('Build Status'), $status_view); }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $this->requireApplicationCapability(HarbormasterManagePlansCapability::CAPABILITY); $plan_id = $request->getURIData('id'); // NOTE: At least for now, this only requires the "Can Manage Plans" // capability, not the "Can Edit" capability. Possibly it should have // a more stringent requirement, though. $plan = id(new HarbormasterBuildPlanQuery())->setViewer($viewer)->withIDs(array($plan_id))->executeOne(); if (!$plan) { return new Aphront404Response(); } $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); if (!$plan->canRunManually()) { return $this->newDialog()->setTitle(pht('Can Not Run Plan'))->appendParagraph(pht('This plan can not be run manually.'))->addCancelButton($cancel_uri); } $e_name = true; $v_name = null; $errors = array(); if ($request->isFormPost()) { $buildable = HarbormasterBuildable::initializeNewBuildable($viewer)->setIsManualBuildable(true); $v_name = $request->getStr('buildablePHID'); if ($v_name) { $object = id(new PhabricatorObjectQuery())->setViewer($viewer)->withNames(array($v_name))->executeOne(); if ($object instanceof HarbormasterBuildableInterface) { $buildable->setBuildablePHID($object->getHarbormasterBuildablePHID())->setContainerPHID($object->getHarbormasterContainerPHID()); } else { $e_name = pht('Invalid'); $errors[] = pht('Enter the name of a revision or commit.'); } } else { $e_name = pht('Required'); $errors[] = pht('You must choose a revision or commit to build.'); } if (!$errors) { $buildable->save(); $buildable->applyPlan($plan); $buildable_uri = '/B' . $buildable->getID(); return id(new AphrontRedirectResponse())->setURI($buildable_uri); } } if ($errors) { $errors = id(new PHUIInfoView())->setErrors($errors); } $title = pht('Run Build Plan Manually'); $save_button = pht('Run Plan Manually'); $form = id(new PHUIFormLayoutView())->setUser($viewer)->appendRemarkupInstructions(pht("Enter the name of a commit or revision to run this plan on (for " . "example, `rX123456` or `D123`).\n\n" . "For more detailed output, you can also run manual builds from " . "the command line:\n\n" . " phabricator/ \$ ./bin/harbormaster build <object> --plan %s", $plan->getID()))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Buildable Name'))->setName('buildablePHID')->setError($e_name)->setValue($v_name)); return $this->newDialog()->setWidth(AphrontDialogView::WIDTH_FULL)->setTitle($title)->appendChild($form)->addCancelButton($cancel_uri)->addSubmitButton($save_button); }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new HarbormasterBuildableQuery())->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $buildable_phids = $request->getValue('buildablePHIDs'); if ($buildable_phids !== null) { $query->withBuildablePHIDs($buildable_phids); } $container_phids = $request->getValue('containerPHIDs'); if ($container_phids !== null) { $query->withContainerPHIDs($container_phids); } $manual = $request->getValue('manualBuildables'); if ($manual !== null) { $query->withManualBuildables($manual); } $pager = $this->newPager($request); $buildables = $query->executeWithCursorPager($pager); $data = array(); foreach ($buildables as $buildable) { $monogram = $buildable->getMonogram(); $status = $buildable->getBuildableStatus(); $status_name = HarbormasterBuildable::getBuildableStatusName($status); $data[] = array('id' => $buildable->getID(), 'phid' => $buildable->getPHID(), 'monogram' => $monogram, 'uri' => PhabricatorEnv::getProductionURI('/' . $monogram), 'buildableStatus' => $status, 'buildableStatusName' => $status_name, 'buildablePHID' => $buildable->getBuildablePHID(), 'containerPHID' => $buildable->getContainerPHID(), 'isManualBuildable' => (bool) $buildable->getIsManualBuildable()); } $results = array('data' => $data); $results = $this->addPagerResults($results, $pager); return $results; }
private function applyHeraldRules(PhabricatorLiskDAO $object, array $xactions) { $adapter = $this->buildHeraldAdapter($object, $xactions)->setContentSource($this->getContentSource())->setIsNewObject($this->getIsNewObject())->setAppliedTransactions($xactions); if ($this->getApplicationEmail()) { $adapter->setApplicationEmail($this->getApplicationEmail()); } $xscript = HeraldEngine::loadAndApplyRules($adapter); $this->setHeraldAdapter($adapter); $this->setHeraldTranscript($xscript); if ($adapter instanceof HarbormasterBuildableAdapterInterface) { HarbormasterBuildable::applyBuildPlans($adapter->getHarbormasterBuildablePHID(), $adapter->getHarbormasterContainerPHID(), $adapter->getQueuedHarbormasterBuildRequests()); } return array_merge($this->didApplyHeraldRules($object, $adapter, $xscript), $adapter->getQueuedTransactions()); }
protected final function renderBuildable(HarbormasterBuildable $buildable) { $status = $buildable->getBuildableStatus(); $icon = HarbormasterBuildable::getBuildableStatusIcon($status); $color = HarbormasterBuildable::getBuildableStatusColor($status); $name = HarbormasterBuildable::getBuildableStatusName($status); $icon_view = id(new PHUIIconView())->setIcon($icon . ' ' . $color); $tooltip_view = javelin_tag('span', array('sigil' => 'has-tooltip', 'meta' => array('tip' => $name)), $icon_view); Javelin::initBehavior('phabricator-tooltips'); return phutil_tag('a', array('href' => '/' . $buildable->getMonogram()), $tooltip_view); }
private function applyHeraldRules(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $commit->attachRepository($repository); // Don't take any actions on an importing repository. Principally, this // avoids generating thousands of audits or emails when you import an // established repository on an existing install. if ($repository->isImporting()) { return; } if ($repository->getDetail('herald-disabled')) { return; } $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID()); if (!$data) { throw new PhabricatorWorkerPermanentFailureException(pht('Unable to load commit data. The data for this task is invalid ' . 'or no longer exists.')); } $adapter = id(new HeraldCommitAdapter())->setCommit($commit); $rules = id(new HeraldRuleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withContentTypes(array($adapter->getAdapterContentType()))->withDisabled(false)->needConditionsAndActions(true)->needAppliedToPHIDs(array($adapter->getPHID()))->needValidateAuthors(true)->execute(); $engine = new HeraldEngine(); $effects = $engine->applyRules($rules, $adapter); $engine->applyEffects($effects, $adapter, $rules); $xscript = $engine->getTranscript(); $audit_phids = $adapter->getAuditMap(); $cc_phids = $adapter->getAddCCMap(); if ($audit_phids || $cc_phids) { $this->createAudits($commit, $audit_phids, $cc_phids, $rules); } HarbormasterBuildable::applyBuildPlans($commit->getPHID(), $repository->getPHID(), $adapter->getBuildPlans()); $explicit_auditors = $this->createAuditsFromCommitMessage($commit, $data); $this->publishFeedStory($repository, $commit, $data); $herald_targets = $adapter->getEmailPHIDs(); $email_phids = array_unique(array_merge($explicit_auditors, array_keys($cc_phids), $herald_targets)); if (!$email_phids) { return; } $revision = $adapter->loadDifferentialRevision(); if ($revision) { $name = $revision->getTitle(); } else { $name = $data->getSummary(); } $author_phid = $data->getCommitDetail('authorPHID'); $reviewer_phid = $data->getCommitDetail('reviewerPHID'); $phids = array_filter(array($author_phid, $reviewer_phid, $commit->getPHID())); $handles = id(new PhabricatorHandleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($phids)->execute(); $commit_handle = $handles[$commit->getPHID()]; $commit_name = $commit_handle->getName(); if ($author_phid) { $author_name = $handles[$author_phid]->getName(); } else { $author_name = $data->getAuthorName(); } if ($reviewer_phid) { $reviewer_name = $handles[$reviewer_phid]->getName(); } else { $reviewer_name = null; } $who = implode(', ', array_filter(array($author_name, $reviewer_name))); $description = $data->getCommitMessage(); $commit_uri = PhabricatorEnv::getProductionURI($commit_handle->getURI()); $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.'; $limit = self::MAX_FILES_SHOWN_IN_EMAIL; $files = $adapter->loadAffectedPaths(); sort($files); if (count($files) > $limit) { array_splice($files, $limit); $files[] = '(This commit affected more than ' . $limit . ' files. ' . 'Only ' . $limit . ' are shown here and additional ones are truncated.)'; } $files = implode("\n", $files); $xscript_id = $xscript->getID(); $why_uri = '/herald/transcript/' . $xscript_id . '/'; $reply_handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($commit); $template = new PhabricatorMetaMTAMail(); $inline_patch_text = $this->buildPatch($template, $repository, $commit); $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection($description); $body->addTextSection(pht('DETAILS'), $commit_uri); // TODO: This should be integrated properly once we move to // ApplicationTransactions. $field_list = PhabricatorCustomField::getObjectFields($commit, PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS); $field_list->setViewer(PhabricatorUser::getOmnipotentUser())->readFieldsFromStorage($commit); foreach ($field_list->getFields() as $field) { try { $field->buildApplicationTransactionMailBody(new DifferentialTransaction(), $body); } catch (Exception $ex) { // Log the exception and continue. phlog($ex); } } $body->addTextSection(pht('DIFFERENTIAL REVISION'), $differential); $body->addTextSection(pht('AFFECTED FILES'), $files); $body->addReplySection($reply_handler->getReplyHandlerInstructions()); $body->addHeraldSection($why_uri); $body->addRawSection($inline_patch_text); $body = $body->render(); $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix'); $threading = PhabricatorAuditCommentEditor::getMailThreading($repository, $commit); list($thread_id, $thread_topic) = $threading; $template->setRelatedPHID($commit->getPHID()); $template->setSubject("{$commit_name}: {$name}"); $template->setSubjectPrefix($prefix); $template->setVarySubjectPrefix('[Commit]'); $template->setBody($body); $template->setThreadID($thread_id, $is_new = true); $template->addHeader('Thread-Topic', $thread_topic); $template->setIsBulk(true); $template->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader()); if ($author_phid) { $template->setFrom($author_phid); } // TODO: We should verify that each recipient can actually see the // commit before sending them email (T603). $mails = $reply_handler->multiplexMail($template, id(new PhabricatorHandleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($email_phids)->execute(), array()); foreach ($mails as $mail) { $mail->saveAndSend(); } }
protected function didApplyHeraldRules(PhabricatorLiskDAO $object, HeraldAdapter $adapter, HeraldTranscript $transcript) { $xactions = array(); // Build a transaction to adjust reviewers. $reviewers = array(DifferentialReviewerStatus::STATUS_ADDED => array_keys($adapter->getReviewersAddedByHerald()), DifferentialReviewerStatus::STATUS_BLOCKING => array_keys($adapter->getBlockingReviewersAddedByHerald())); $old_reviewers = $object->getReviewerStatus(); $old_reviewers = mpull($old_reviewers, null, 'getReviewerPHID'); $value = array(); foreach ($reviewers as $status => $phids) { foreach ($phids as $phid) { if ($phid == $object->getAuthorPHID()) { // Don't try to add the revision's author as a reviewer, since this // isn't valid and doesn't make sense. continue; } // If the target is already a reviewer, don't try to change anything // if their current status is at least as strong as the new status. // For example, don't downgrade an "Accepted" to a "Blocking Reviewer". $old_reviewer = idx($old_reviewers, $phid); if ($old_reviewer) { $old_status = $old_reviewer->getStatus(); $old_strength = DifferentialReviewerStatus::getStatusStrength($old_status); $new_strength = DifferentialReviewerStatus::getStatusStrength($status); if ($new_strength <= $old_strength) { continue; } } $value['+'][$phid] = array('data' => array('status' => $status)); } } if ($value) { $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST; $xactions[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $edge_reviewer)->setNewValue($value); } // Require legalpad document signatures. $legal_phids = $adapter->getRequiredSignatureDocumentPHIDs(); if ($legal_phids) { // We only require signatures of documents which have not already // been signed. In general, this reduces the amount of churn that // signature rules cause. $signatures = id(new LegalpadDocumentSignatureQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withDocumentPHIDs($legal_phids)->withSignerPHIDs(array($object->getAuthorPHID()))->execute(); $signed_phids = mpull($signatures, 'getDocumentPHID'); $legal_phids = array_diff($legal_phids, $signed_phids); // If we still have something to trigger, add the edges. if ($legal_phids) { $edge_legal = LegalpadObjectNeedsSignatureEdgeType::EDGECONST; $xactions[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $edge_legal)->setNewValue(array('+' => array_fuse($legal_phids))); } } // Apply build plans. HarbormasterBuildable::applyBuildPlans($adapter->getDiff()->getPHID(), $adapter->getPHID(), $adapter->getBuildPlans()); return $xactions; }
protected function addBuildableCrumb(PHUICrumbsView $crumbs, HarbormasterBuildable $buildable) { $monogram = $buildable->getMonogram(); $uri = '/' . $monogram; $crumbs->addTextCrumb($monogram, $uri); }
/** * Update the overall status of the buildable this build is attached to. * * After a build changes state (for example, passes or fails) it may affect * the overall state of the associated buildable. Compute the new aggregate * state and save it on the buildable. * * @param HarbormasterBuild The buildable to update. * @return void */ private function updateBuildable(HarbormasterBuildable $buildable) { $viewer = $this->getViewer(); $lock_key = 'harbormaster.buildable:' . $buildable->getID(); $lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15); $buildable = id(new HarbormasterBuildableQuery())->setViewer($viewer)->withIDs(array($buildable->getID()))->needBuilds(true)->executeOne(); $all_pass = true; $any_fail = false; foreach ($buildable->getBuilds() as $build) { if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) { $all_pass = false; } if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED || $build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR || $build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) { $any_fail = true; } } if ($any_fail) { $new_status = HarbormasterBuildable::STATUS_FAILED; } else { if ($all_pass) { $new_status = HarbormasterBuildable::STATUS_PASSED; } else { $new_status = HarbormasterBuildable::STATUS_BUILDING; } } $old_status = $buildable->getBuildableStatus(); $did_update = $old_status != $new_status; if ($did_update) { $buildable->setBuildableStatus($new_status); $buildable->save(); } $lock->unlock(); // If we changed the buildable status, try to post a transaction to the // object about it. We can safely do this outside of the locked region. // NOTE: We only post transactions for automatic buildables, not for // manual ones: manual builds are test builds, whoever is doing tests // can look at the results themselves, and other users generally don't // care about the outcome. $should_publish = $did_update && $new_status != HarbormasterBuildable::STATUS_BUILDING && !$buildable->getIsManualBuildable(); if (!$should_publish) { return; } $object = id(new PhabricatorObjectQuery())->setViewer($viewer)->withPHIDs(array($buildable->getBuildablePHID()))->executeOne(); if (!$object) { return; } if (!$object instanceof PhabricatorApplicationTransactionInterface) { return; } // TODO: Publishing these transactions is causing a race. See T8650. // We shouldn't be publishing to diffs anyway. if ($object instanceof DifferentialDiff) { return; } $template = $object->getApplicationTransactionTemplate(); if (!$template) { return; } $template->setTransactionType(PhabricatorTransactions::TYPE_BUILDABLE)->setMetadataValue('harbormaster:buildablePHID', $buildable->getPHID())->setOldValue($old_status)->setNewValue($new_status); $harbormaster_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); $daemon_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_DAEMON, array()); $editor = $object->getApplicationTransactionEditor()->setActor($viewer)->setActingAsPHID($harbormaster_phid)->setContentSource($daemon_source)->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); $editor->applyTransactions($object->getApplicationTransactionObject(), array($template)); }
protected function renderResultList(array $buildables, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($buildables, 'HarbormasterBuildable'); $viewer = $this->requireViewer(); $list = new PHUIObjectItemListView(); foreach ($buildables as $buildable) { $id = $buildable->getID(); $item = id(new PHUIObjectItemView())->setHeader(pht('Buildable %d', $buildable->getID())); if ($buildable->getContainerHandle() !== null) { $item->addAttribute($buildable->getContainerHandle()->getName()); } if ($buildable->getBuildableHandle() !== null) { $item->addAttribute($buildable->getBuildableHandle()->getFullName()); } if ($id) { $item->setHref("/B{$id}"); } if ($buildable->getIsManualBuildable()) { $item->addIcon('fa-wrench grey', pht('Manual')); } $item->setBarColor(HarbormasterBuildable::getBuildableStatusColor($buildable->getBuildableStatus())); $item->addByline(HarbormasterBuildable::getBuildableStatusName($buildable->getBuildableStatus())); $list->addItem($item); } return $list; }
/** * Looks up the plan PHIDs and applies the plans to the specified * object identified by it's PHID. */ public static function applyBuildPlans($phid, $container_phid, array $plan_phids) { if (count($plan_phids) === 0) { return; } // Skip all of this logic if the Harbormaster application // isn't currently installed. $harbormaster_app = 'PhabricatorHarbormasterApplication'; if (!PhabricatorApplication::isClassInstalled($harbormaster_app)) { return; } $buildable = HarbormasterBuildable::createOrLoadExisting(PhabricatorUser::getOmnipotentUser(), $phid, $container_phid); $plans = id(new HarbormasterBuildPlanQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($plan_phids)->execute(); foreach ($plans as $plan) { if ($plan->isDisabled()) { // TODO: This should be communicated more clearly -- maybe we should // create the build but set the status to "disabled" or "derelict". continue; } $buildable->applyPlan($plan); } }
protected function didApplyHeraldRules(PhabricatorLiskDAO $object, HeraldAdapter $adapter, HeraldTranscript $transcript) { $xactions = array(); $audit_phids = $adapter->getAuditMap(); foreach ($audit_phids as $phid => $rule_ids) { foreach ($rule_ids as $rule_id) { $this->addAuditReason($phid, pht('%s Triggered Audit', "H{$rule_id}")); } } if ($audit_phids) { $xactions[] = id(new PhabricatorAuditTransaction())->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS)->setNewValue(array_fuse(array_keys($audit_phids)))->setMetadataValue('auditStatus', PhabricatorAuditStatusConstants::AUDIT_REQUIRED)->setMetadataValue('auditReasonMap', $this->auditReasonMap); } HarbormasterBuildable::applyBuildPlans($object->getPHID(), $object->getRepository()->getPHID(), $adapter->getBuildPlans()); $limit = self::MAX_FILES_SHOWN_IN_EMAIL; $files = $adapter->loadAffectedPaths(); sort($files); if (count($files) > $limit) { array_splice($files, $limit); $files[] = pht('(This commit affected more than %d files. Only %d are shown here ' . 'and additional ones are truncated.)', $limit, $limit); } $this->affectedFiles = implode("\n", $files); return $xactions; }
protected function applyCustomExternalTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case DifferentialTransaction::TYPE_ACTION: return; case DifferentialTransaction::TYPE_INLINE: $reply = $xaction->getComment()->getReplyToComment(); if ($reply && !$reply->getHasReplies()) { $reply->setHasReplies(1)->save(); } return; case DifferentialTransaction::TYPE_UPDATE: // Now that we're inside the transaction, do a final check. $diff = $this->requireDiff($xaction->getNewValue()); // TODO: It would be slightly cleaner to just revalidate this // transaction somehow using the same validation code, but that's // not easy to do at the moment. $revision_id = $diff->getRevisionID(); if ($revision_id && $revision_id != $object->getID()) { throw new Exception(pht('Diff is already attached to another revision. You lost ' . 'a race?')); } // TODO: This can race with diff updates, particularly those from // Harbormaster. See discussion in T8650. $diff->setRevisionID($object->getID()); $diff->save(); // Update Harbormaster to set the containerPHID correctly for any // existing buildables. We may otherwise have buildables stuck with // the old (`null`) container. // TODO: This is a bit iffy, maybe we can find a cleaner approach? // In particular, this could (rarely) be overwritten by Harbormaster // workers. $table = new HarbormasterBuildable(); $conn_w = $table->establishConnection('w'); queryfx($conn_w, 'UPDATE %T SET containerPHID = %s WHERE buildablePHID = %s', $table->getTableName(), $object->getPHID(), $diff->getPHID()); return; } return parent::applyCustomExternalTransaction($object, $xaction); }
/** * Get a list of @{class:HarbormasterBuildTarget} objects for a list of * autotarget keys. * * If some targets or builds do not exist, they are created. * * @param HarbormasterBuildable A buildable. * @param map<string, object> Map of keys to steps. * @return map<string, object> Map of keys to targets. */ private function generateBuildTargetMap(HarbormasterBuildable $buildable, array $step_map) { $viewer = $this->getViewer(); $plan_map = mgroup($step_map, 'getBuildPlanPHID'); $builds = id(new HarbormasterBuildQuery())->setViewer($viewer)->withBuildablePHIDs(array($buildable->getPHID()))->withBuildPlanPHIDs(array_keys($plan_map))->needBuildTargets(true)->execute(); $autobuilds = array(); foreach ($builds as $build) { $plan_key = $build->getBuildPlan()->getPlanAutoKey(); $autobuilds[$plan_key] = $build; } $new_builds = array(); foreach ($plan_map as $plan_phid => $steps) { $plan = head($steps)->getBuildPlan(); $plan_key = $plan->getPlanAutoKey(); $build = idx($autobuilds, $plan_key); if ($build) { // We already have a build for this set of targets, so we don't need // to do any work. (It's possible the build is an older build that // doesn't have all of the right targets if new autotargets were // recently introduced, but we don't currently try to construct them.) continue; } // NOTE: Normally, `applyPlan()` does not actually generate targets. // We need to apply the plan in-process to perform target generation. // This is fine as long as autotargets are empty containers that don't // do any work, which they always should be. PhabricatorWorker::setRunAllTasksInProcess(true); try { // NOTE: We might race another process here to create the same build // with the same `planAutoKey`. The database will prevent this and // using autotargets only currently makes sense if you just created the // resource and "own" it, so we don't try to handle this, but may need // to be more careful here if use of autotargets expands. $build = $buildable->applyPlan($plan, array()); PhabricatorWorker::setRunAllTasksInProcess(false); } catch (Exception $ex) { PhabricatorWorker::setRunAllTasksInProcess(false); throw $ex; } $new_builds[] = $build; } if ($new_builds) { $all_targets = id(new HarbormasterBuildTargetQuery())->setViewer($viewer)->withBuildPHIDs(mpull($new_builds, 'getPHID'))->execute(); } else { $all_targets = array(); } foreach ($builds as $build) { foreach ($build->getBuildTargets() as $target) { $all_targets[] = $target; } } $target_map = array(); foreach ($all_targets as $target) { $target_key = $target->getImplementation()->getBuildStepAutotargetStepKey(); if (!$target_key) { continue; } $target_map[$target_key] = $target; } $target_map = array_select_keys($target_map, array_keys($step_map)); return $target_map; }
public function render() { $drequest = $this->getDiffusionRequest(); $handles = $this->handles; $graph = null; if ($this->parents) { $graph = $this->renderGraph(); } $show_builds = PhabricatorApplication::isClassInstalledForViewer('PhabricatorHarbormasterApplication', $this->getUser()); $rows = array(); $ii = 0; foreach ($this->history as $history) { $epoch = $history->getEpoch(); if ($epoch) { $date = phabricator_date($epoch, $this->user); $time = phabricator_time($epoch, $this->user); } else { $date = null; $time = null; } $data = $history->getCommitData(); $author_phid = $committer = $committer_phid = null; if ($data) { $author_phid = $data->getCommitDetail('authorPHID'); $committer_phid = $data->getCommitDetail('committerPHID'); $committer = $data->getCommitDetail('committer'); } if ($author_phid && isset($handles[$author_phid])) { $author = $handles[$author_phid]->renderLink(); } else { $author = self::renderName($history->getAuthorName()); } $different_committer = false; if ($committer_phid) { $different_committer = $committer_phid != $author_phid; } else { if ($committer != '') { $different_committer = $committer != $history->getAuthorName(); } } if ($different_committer) { if ($committer_phid && isset($handles[$committer_phid])) { $committer = $handles[$committer_phid]->renderLink(); } else { $committer = self::renderName($committer); } $author = hsprintf('%s/%s', $author, $committer); } // We can show details once the message and change have been imported. $partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE | PhabricatorRepositoryCommit::IMPORTED_CHANGE; $commit = $history->getCommit(); if ($commit && $commit->isPartiallyImported($partial_import) && $data) { $summary = AphrontTableView::renderSingleDisplayLine($history->getSummary()); } else { $summary = phutil_tag('em', array(), "Importing…"); } $build = null; if ($show_builds) { $buildable_lookup = $this->loadBuildablesOnDemand(); $buildable = idx($buildable_lookup, $commit->getPHID()); if ($buildable !== null) { $icon = HarbormasterBuildable::getBuildableStatusIcon($buildable->getBuildableStatus()); $color = HarbormasterBuildable::getBuildableStatusColor($buildable->getBuildableStatus()); $name = HarbormasterBuildable::getBuildableStatusName($buildable->getBuildableStatus()); $icon_view = id(new PHUIIconView())->setIconFont($icon . ' ' . $color); $tooltip_view = javelin_tag('span', array('sigil' => 'has-tooltip', 'meta' => array('tip' => $name)), $icon_view); Javelin::initBehavior('phabricator-tooltips'); $href_view = phutil_tag('a', array('href' => '/' . $buildable->getMonogram()), $tooltip_view); $build = $href_view; $has_any_build = true; } } $rows[] = array($graph ? $graph[$ii++] : null, self::linkCommit($drequest->getRepository(), $history->getCommitIdentifier()), $build, $commit ? self::linkRevision(idx($this->revisions, $commit->getPHID())) : null, $author, $summary, $date, $time); } $view = new AphrontTableView($rows); $view->setHeaders(array('', pht('Commit'), '', pht('Revision'), pht('Author/Committer'), pht('Details'), pht('Date'), pht('Time'))); $view->setColumnClasses(array('threads', 'n', 'icon', 'n', '', 'wide', '', 'right')); $view->setColumnVisibility(array($graph ? true : false)); $view->setDeviceVisibility(array($graph ? true : false, true, true, true, false, true, false, false)); return $view->render(); }
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; }
private function renderLintAndUnit(HarbormasterBuildable $buildable, array $builds) { $viewer = $this->getViewer(); $targets = array(); foreach ($builds as $build) { foreach ($build->getBuildTargets() as $target) { $targets[] = $target; } } if (!$targets) { return; } $target_phids = mpull($targets, 'getPHID'); $lint_data = id(new HarbormasterBuildLintMessage())->loadAllWhere('buildTargetPHID IN (%Ls)', $target_phids); $unit_data = id(new HarbormasterBuildUnitMessage())->loadAllWhere('buildTargetPHID IN (%Ls)', $target_phids); if ($lint_data) { $lint_table = id(new HarbormasterLintPropertyView())->setUser($viewer)->setLimit(10)->setLintMessages($lint_data); $lint_href = $this->getApplicationURI('lint/' . $buildable->getID() . '/'); $lint_header = id(new PHUIHeaderView())->setHeader(pht('Lint Messages'))->addActionLink(id(new PHUIButtonView())->setTag('a')->setHref($lint_href)->setIconFont('fa-list-ul')->setText('View All')); $lint = id(new PHUIObjectBoxView())->setHeader($lint_header)->setTable($lint_table); } else { $lint = null; } if ($unit_data) { $unit_table = id(new HarbormasterUnitPropertyView())->setUser($viewer)->setLimit(25)->setUnitMessages($unit_data); $unit_href = $this->getApplicationURI('unit/' . $buildable->getID() . '/'); $unit_header = id(new PHUIHeaderView())->setHeader(pht('Unit Tests'))->addActionLink(id(new PHUIButtonView())->setTag('a')->setHref($unit_href)->setIconFont('fa-list-ul')->setText('View All')); $unit = id(new PHUIObjectBoxView())->setHeader($unit_header)->setTable($unit_table); } else { $unit = null; } return array($lint, $unit); }
protected function renderResultList(array $buildables, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($buildables, 'HarbormasterBuildable'); $viewer = $this->requireViewer(); $phids = array(); foreach ($buildables as $buildable) { $phids[] = $buildable->getBuildableObject()->getHarbormasterBuildableDisplayPHID(); $phids[] = $buildable->getContainerPHID(); $phids[] = $buildable->getBuildablePHID(); } $handles = $viewer->loadHandles($phids); $list = new PHUIObjectItemListView(); foreach ($buildables as $buildable) { $id = $buildable->getID(); $display_phid = $buildable->getBuildableObject()->getHarbormasterBuildableDisplayPHID(); $container_phid = $buildable->getContainerPHID(); $buildable_phid = $buildable->getBuildablePHID(); $item = id(new PHUIObjectItemView())->setObjectName(pht('Buildable %d', $buildable->getID())); if ($display_phid) { $handle = $handles[$display_phid]; $item->setHeader($handle->getFullName()); } if ($container_phid && $container_phid != $display_phid) { $handle = $handles[$container_phid]; $item->addAttribute($handle->getName()); } if ($buildable_phid && $buildable_phid != $display_phid) { $handle = $handles[$buildable_phid]; $item->addAttribute($handle->getFullName()); } $item->setHref($buildable->getURI()); if ($buildable->getIsManualBuildable()) { $item->addIcon('fa-wrench grey', pht('Manual')); } $status = $buildable->getBuildableStatus(); $status_icon = HarbormasterBuildable::getBuildableStatusIcon($status); $status_color = HarbormasterBuildable::getBuildableStatusColor($status); $status_label = HarbormasterBuildable::getBuildableStatusName($status); $item->setStatusIcon("{$status_icon} {$status_color}", $status_label); $list->addItem($item); } $result = new PhabricatorApplicationSearchResultView(); $result->setObjectList($list); $result->setNoDataString(pht('No buildables found.')); return $result; }