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());
 }
Пример #8
0
 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;
 }
Пример #11
0
 protected function addBuildableCrumb(PHUICrumbsView $crumbs, HarbormasterBuildable $buildable)
 {
     $monogram = $buildable->getMonogram();
     $uri = '/' . $monogram;
     $crumbs->addTextCrumb($monogram, $uri);
 }
Пример #12
0
 /**
  * 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;
 }
Пример #14
0
 /**
  * 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);
     }
 }
Пример #15
0
 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;
 }