diff --git a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php index a0c8608935..ad8f673fd7 100644 --- a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php +++ b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php @@ -1,218 +1,218 @@ getViewer(); $id = $request->getURIData('id'); $task = id(new PhabricatorWorkerActiveTask())->load($id); if (!$task) { $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) ->withIDs(array($id)) ->execute(); $task = reset($tasks); } if (!$task) { $title = pht('Task Does Not Exist'); $error_view = new PHUIInfoView(); $error_view->setTitle(pht('No Such Task')); $error_view->appendChild(phutil_tag( 'p', array(), pht('This task may have recently been garbage collected.'))); $error_view->setSeverity(PHUIInfoView::SEVERITY_NODATA); $content = $error_view; } else { $title = pht('Task %d', $task->getID()); $header = id(new PHUIHeaderView()) ->setHeader(pht('Task %d (%s)', $task->getID(), $task->getTaskClass())); $properties = $this->buildPropertyListView($task); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); $retry_head = id(new PHUIHeaderView()) ->setHeader(pht('Retries')); $retry_info = $this->buildRetryListView($task); $retry_box = id(new PHUIObjectBoxView()) ->setHeader($retry_head) ->addPropertyList($retry_info); $content = array( $object_box, $retry_box, ); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); return $this->buildApplicationPage( array( $crumbs, $content, ), array( 'title' => $title, )); } private function buildPropertyListView( PhabricatorWorkerTask $task) { $viewer = $this->getRequest()->getUser(); $view = new PHUIPropertyListView(); if ($task->isArchived()) { switch ($task->getResult()) { case PhabricatorWorkerArchiveTask::RESULT_SUCCESS: $status = pht('Complete'); break; case PhabricatorWorkerArchiveTask::RESULT_FAILURE: $status = pht('Failed'); break; case PhabricatorWorkerArchiveTask::RESULT_CANCELLED: $status = pht('Cancelled'); break; default: throw new Exception(pht('Unknown task status!')); } } else { $status = pht('Queued'); } $view->addProperty( pht('Task Status'), $status); $view->addProperty( pht('Task Class'), $task->getTaskClass()); if ($task->getLeaseExpires()) { if ($task->getLeaseExpires() > time()) { $lease_status = pht('Leased'); } else { $lease_status = pht('Lease Expired'); } } else { $lease_status = phutil_tag('em', array(), pht('Not Leased')); } $view->addProperty( pht('Lease Status'), $lease_status); $view->addProperty( pht('Lease Owner'), $task->getLeaseOwner() ? $task->getLeaseOwner() : phutil_tag('em', array(), pht('None'))); if ($task->getLeaseExpires() && $task->getLeaseOwner()) { $expires = ($task->getLeaseExpires() - time()); $expires = phutil_format_relative_time_detailed($expires); } else { $expires = phutil_tag('em', array(), pht('None')); } $view->addProperty( pht('Lease Expires'), $expires); if ($task->isArchived()) { $duration = pht('%s us', new PhutilNumber($task->getDuration())); } else { $duration = phutil_tag('em', array(), pht('Not Completed')); } $view->addProperty( pht('Duration'), $duration); $data = id(new PhabricatorWorkerTaskData())->load($task->getDataID()); $task->setData($data->getData()); $worker = $task->getWorkerInstance(); $data = $worker->renderForDisplay($viewer); - $view->addProperty( - pht('Data'), - $data); + if ($data !== null) { + $view->addProperty(pht('Data'), $data); + } return $view; } private function buildRetryListView(PhabricatorWorkerTask $task) { $view = new PHUIPropertyListView(); $data = id(new PhabricatorWorkerTaskData())->load($task->getDataID()); $task->setData($data->getData()); $worker = $task->getWorkerInstance(); $view->addProperty( pht('Failure Count'), $task->getFailureCount()); $retry_count = $worker->getMaximumRetryCount(); if ($retry_count === null) { $max_retries = phutil_tag('em', array(), pht('Retries Forever')); $retry_count = INF; } else { $max_retries = $retry_count; } $view->addProperty( pht('Maximum Retries'), $max_retries); $projection = clone $task; $projection->makeEphemeral(); $next = array(); for ($ii = $task->getFailureCount(); $ii < $retry_count; $ii++) { $projection->setFailureCount($ii); $next[] = $worker->getWaitBeforeRetry($projection); if (count($next) > 10) { break; } } if ($next) { $cumulative = 0; foreach ($next as $key => $duration) { if ($duration === null) { $duration = 60; } $cumulative += $duration; $next[$key] = phutil_format_relative_time($cumulative); } if ($ii != $retry_count) { $next[] = '...'; } $retries_in = implode(', ', $next); } else { $retries_in = pht('No More Retries'); } $view->addProperty( pht('Retries After'), $retries_in); return $view; } } diff --git a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php index 5ca684a098..5669df4ee9 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php @@ -1,39 +1,38 @@ withPHIDs($phids); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $build = $objects[$phid]; - $handles[$phid]->setName(pht( - 'Build %d: %s', - $build->getID(), - $build->getName())); - $handles[$phid]->setURI( - '/harbormaster/build/'.$build->getID()); + $build_id = $build->getID(); + $name = $build->getName(); + + $handle->setName(pht('Build %d: %s', $build_id, $name)); + $handle->setURI("/harbormaster/build/{$build_id}/"); } } } diff --git a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php index 8aa69abad6..e16db13ccb 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php @@ -1,33 +1,43 @@ withPHIDs($phids); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { - $build_target = $objects[$phid]; + $target = $objects[$phid]; + $target_id = $target->getID(); + + // Build target don't currently have their own page, so just point + // the user at the build until we have one. + $build = $target->getBuild(); + $build_id = $build->getID(); + $uri = "/harbormaster/build/{$build_id}/"; + + $handle->setName(pht('Build Target %d', $target_id)); + $handle->setURI($uri); } } } diff --git a/src/applications/harbormaster/worker/HarbormasterBuildWorker.php b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php index c71563a8a5..67eb6b3e78 100644 --- a/src/applications/harbormaster/worker/HarbormasterBuildWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php @@ -1,28 +1,45 @@ loadBuild(); + } catch (Exception $ex) { + return null; + } + + return $viewer->renderHandle($build->getPHID()); + } + protected function doWork() { + $viewer = $this->getViewer(); + $build = $this->loadBuild(); + + id(new HarbormasterBuildEngine()) + ->setViewer($viewer) + ->setBuild($build) + ->continueBuild(); + } + + private function loadBuild() { $data = $this->getTaskData(); $id = idx($data, 'buildID'); - $viewer = $this->getViewer(); + $viewer = $this->getViewer(); $build = id(new HarbormasterBuildQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); if (!$build) { throw new PhabricatorWorkerPermanentFailureException( pht('Invalid build ID "%s".', $id)); } - id(new HarbormasterBuildEngine()) - ->setViewer($viewer) - ->setBuild($build) - ->continueBuild(); + return $build; } } diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php index a13a9de163..db355b145c 100644 --- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php @@ -1,103 +1,113 @@ loadBuildTarget(); + } catch (Exception $ex) { + return null; + } + + return $viewer->renderHandle($target->getPHID()); + } + private function loadBuildTarget() { $data = $this->getTaskData(); $id = idx($data, 'targetID'); $target = id(new HarbormasterBuildTargetQuery()) ->withIDs(array($id)) ->setViewer($this->getViewer()) ->executeOne(); if (!$target) { throw new PhabricatorWorkerPermanentFailureException( pht( 'Bad build target ID "%d".', $id)); } return $target; } protected function doWork() { $target = $this->loadBuildTarget(); $build = $target->getBuild(); $viewer = $this->getViewer(); $target->setDateStarted(time()); try { if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { throw new HarbormasterBuildAbortedException(); } $status_pending = HarbormasterBuildTarget::STATUS_PENDING; if ($target->getTargetStatus() == $status_pending) { $target->setTargetStatus(HarbormasterBuildTarget::STATUS_BUILDING); $target->save(); } $implementation = $target->getImplementation(); $implementation->execute($build, $target); $next_status = HarbormasterBuildTarget::STATUS_PASSED; if ($implementation->shouldWaitForMessage($target)) { $next_status = HarbormasterBuildTarget::STATUS_WAITING; } $target->setTargetStatus($next_status); if ($target->isComplete()) { $target->setDateCompleted(PhabricatorTime::getNow()); } $target->save(); } catch (PhabricatorWorkerYieldException $ex) { // If the target wants to yield, let that escape without further // processing. We'll resume after the task retries. throw $ex; } catch (HarbormasterBuildFailureException $ex) { // A build step wants to fail explicitly. $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); $target->setDateCompleted(PhabricatorTime::getNow()); $target->save(); } catch (HarbormasterBuildAbortedException $ex) { // A build step is aborting because the build has been restarted. $target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED); $target->setDateCompleted(PhabricatorTime::getNow()); $target->save(); } catch (Exception $ex) { phlog($ex); try { $log = $build->createLog($target, 'core', 'exception'); $start = $log->start(); $log->append((string)$ex); $log->finalize($start); } catch (Exception $log_ex) { phlog($log_ex); } $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); $target->setDateCompleted(time()); $target->save(); } id(new HarbormasterBuildEngine()) ->setViewer($viewer) ->setBuild($build) ->continueBuild(); } } diff --git a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php index 49f5fbe293..17226a1377 100644 --- a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php +++ b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php @@ -1,231 +1,226 @@ getTaskData(); $email_phids = idx($task_data, 'emailPHIDs'); if (!$email_phids) { // If we don't have any email targets, don't send any email. return; } $event_phid = idx($task_data, 'eventPHID'); $event = id(new PhabricatorRepositoryPushEventQuery()) ->setViewer($viewer) ->withPHIDs(array($event_phid)) ->needLogs(true) ->executeOne(); $repository = $event->getRepository(); if (!$repository->shouldPublish()) { // If the repository is still importing, don't send email. return; } $targets = id(new PhabricatorRepositoryPushReplyHandler()) ->setMailReceiver($repository) ->getMailTargets($email_phids, array()); $messages = array(); foreach ($targets as $target) { $messages[] = $this->sendMail($target, $repository, $event); } foreach ($messages as $message) { $message->save(); } } private function sendMail( PhabricatorMailTarget $target, PhabricatorRepository $repository, PhabricatorRepositoryPushEvent $event) { $task_data = $this->getTaskData(); $viewer = $target->getViewer(); $locale = PhabricatorEnv::beginScopedLocale($viewer->getTranslation()); $logs = $event->getLogs(); list($ref_lines, $ref_list) = $this->renderRefs($logs); list($commit_lines, $subject_line) = $this->renderCommits( $repository, $logs, idx($task_data, 'info', array())); $ref_count = count($ref_lines); $commit_count = count($commit_lines); $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs(array($event->getPusherPHID())) ->execute(); $pusher_name = $handles[$event->getPusherPHID()]->getName(); $repo_name = $repository->getMonogram(); if ($commit_count) { $overview = pht( '%s pushed %d commit(s) to %s.', $pusher_name, $commit_count, $repo_name); } else { $overview = pht( '%s pushed to %s.', $pusher_name, $repo_name); } $details_uri = PhabricatorEnv::getProductionURI( '/diffusion/pushlog/view/'.$event->getID().'/'); $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection($overview); $body->addLinkSection(pht('DETAILS'), $details_uri); if ($commit_lines) { $body->addTextSection(pht('COMMITS'), implode("\n", $commit_lines)); } if ($ref_lines) { $body->addTextSection(pht('REFERENCES'), implode("\n", $ref_lines)); } $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix'); $parts = array(); if ($commit_count) { $parts[] = pht('%s commit(s)', $commit_count); } if ($ref_count) { $parts[] = implode(', ', $ref_list); } $parts = implode(', ', $parts); if ($subject_line) { $subject = pht('(%s) %s', $parts, $subject_line); } else { $subject = pht('(%s)', $parts); } $mail = id(new PhabricatorMetaMTAMail()) ->setRelatedPHID($event->getPHID()) ->setSubjectPrefix($prefix) ->setVarySubjectPrefix(pht('[Push]')) ->setSubject($subject) ->setFrom($event->getPusherPHID()) ->setBody($body->render()) ->setThreadID($event->getPHID(), $is_new = true) ->addHeader('Thread-Topic', $subject) ->setIsBulk(true); return $target->willSendMail($mail); } - public function renderForDisplay(PhabricatorUser $viewer) { - // This data has some sensitive stuff, so don't show it. - return null; - } - private function renderRefs(array $logs) { $ref_lines = array(); $ref_list = array(); foreach ($logs as $log) { $type_name = null; $type_prefix = null; switch ($log->getRefType()) { case PhabricatorRepositoryPushLog::REFTYPE_BRANCH: $type_name = pht('branch'); break; case PhabricatorRepositoryPushLog::REFTYPE_TAG: $type_name = pht('tag'); $type_prefix = pht('tag:'); break; case PhabricatorRepositoryPushLog::REFTYPE_BOOKMARK: $type_name = pht('bookmark'); $type_prefix = pht('bookmark:'); break; case PhabricatorRepositoryPushLog::REFTYPE_COMMIT: default: break; } if ($type_name === null) { continue; } $flags = $log->getChangeFlags(); if ($flags & PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS) { $action = '!'; } else if ($flags & PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE) { $action = '-'; } else if ($flags & PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE) { $action = '~'; } else if ($flags & PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND) { $action = ' '; } else if ($flags & PhabricatorRepositoryPushLog::CHANGEFLAG_ADD) { $action = '+'; } else { $action = '?'; } $old = nonempty($log->getRefOldShort(), pht('')); $new = nonempty($log->getRefNewShort(), pht('')); $name = $log->getRefName(); $ref_lines[] = "{$action} {$type_name} {$name} {$old} > {$new}"; $ref_list[] = $type_prefix.$name; } return array( $ref_lines, array_unique($ref_list), ); } private function renderCommits( PhabricatorRepository $repository, array $logs, array $info) { $commit_lines = array(); $subject_line = null; foreach ($logs as $log) { if ($log->getRefType() != PhabricatorRepositoryPushLog::REFTYPE_COMMIT) { continue; } $commit_info = idx($info, $log->getRefNew(), array()); $name = $repository->formatCommitName($log->getRefNew()); $branches = null; if (idx($commit_info, 'branches')) { $branches = ' ('.implode(', ', $commit_info['branches']).')'; } $summary = null; if (strlen(idx($commit_info, 'summary'))) { $summary = ' '.$commit_info['summary']; } $commit_lines[] = "{$name}{$branches}{$summary}"; if ($subject_line === null) { $subject_line = "{$name}{$summary}"; } } return array($commit_lines, $subject_line); } } diff --git a/src/infrastructure/daemon/workers/PhabricatorWorker.php b/src/infrastructure/daemon/workers/PhabricatorWorker.php index 29f3e0ae39..629e688b90 100644 --- a/src/infrastructure/daemon/workers/PhabricatorWorker.php +++ b/src/infrastructure/daemon/workers/PhabricatorWorker.php @@ -1,259 +1,258 @@ data = $data; } final protected function getTaskData() { return $this->data; } final public function executeTask() { $this->doWork(); } final public static function scheduleTask( $task_class, $data, $options = array()) { $priority = idx($options, 'priority'); if ($priority === null) { $priority = self::PRIORITY_DEFAULT; } $object_phid = idx($options, 'objectPHID'); $task = id(new PhabricatorWorkerActiveTask()) ->setTaskClass($task_class) ->setData($data) ->setPriority($priority) ->setObjectPHID($object_phid); if (self::$runAllTasksInProcess) { // Do the work in-process. $worker = newv($task_class, array($data)); while (true) { try { $worker->doWork(); foreach ($worker->getQueuedTasks() as $queued_task) { list($queued_class, $queued_data, $queued_priority) = $queued_task; $queued_options = array('priority' => $queued_priority); self::scheduleTask($queued_class, $queued_data, $queued_options); } break; } catch (PhabricatorWorkerYieldException $ex) { phlog( pht( 'In-process task "%s" yielded for %s seconds, sleeping...', $task_class, $ex->getDuration())); sleep($ex->getDuration()); } } // Now, save a task row and immediately archive it so we can return an // object with a valid ID. $task->openTransaction(); $task->save(); $archived = $task->archiveTask( PhabricatorWorkerArchiveTask::RESULT_SUCCESS, 0); $task->saveTransaction(); return $archived; } else { $task->save(); return $task; } } /** * Wait for tasks to complete. If tasks are not leased by other workers, they * will be executed in this process while waiting. * * @param list List of queued task IDs to wait for. * @return void */ final public static function waitForTasks(array $task_ids) { if (!$task_ids) { return; } $task_table = new PhabricatorWorkerActiveTask(); $waiting = array_fuse($task_ids); while ($waiting) { $conn_w = $task_table->establishConnection('w'); // Check if any of the tasks we're waiting on are still queued. If they // are not, we're done waiting. $row = queryfx_one( $conn_w, 'SELECT COUNT(*) N FROM %T WHERE id IN (%Ld)', $task_table->getTableName(), $waiting); if (!$row['N']) { // Nothing is queued anymore. Stop waiting. break; } $tasks = id(new PhabricatorWorkerLeaseQuery()) ->withIDs($waiting) ->setLimit(1) ->execute(); if (!$tasks) { // We were not successful in leasing anything. Sleep for a bit and // see if we have better luck later. sleep(1); continue; } $task = head($tasks)->executeTask(); $ex = $task->getExecutionException(); if ($ex) { throw $ex; } } $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) ->withIDs($task_ids) ->execute(); foreach ($tasks as $task) { if ($task->getResult() != PhabricatorWorkerArchiveTask::RESULT_SUCCESS) { throw new Exception(pht('Task %d failed!', $task->getID())); } } } public function renderForDisplay(PhabricatorUser $viewer) { - $data = PhutilReadableSerializer::printableValue($this->data); - return phutil_tag('pre', array(), $data); + return null; } /** * Set this flag to execute scheduled tasks synchronously, in the same * process. This is useful for debugging, and otherwise dramatically worse * in every way imaginable. */ public static function setRunAllTasksInProcess($all) { self::$runAllTasksInProcess = $all; } final protected function log($pattern /* , ... */) { $console = PhutilConsole::getConsole(); $argv = func_get_args(); call_user_func_array(array($console, 'writeLog'), $argv); return $this; } /** * Queue a task to be executed after this one succeeds. * * The followup task will be queued only if this task completes cleanly. * * @param string Task class to queue. * @param array Data for the followup task. * @param int|null Priority for the followup task. * @return this */ final protected function queueTask($class, array $data, $priority = null) { $this->queuedTasks[] = array($class, $data, $priority); return $this; } /** * Get tasks queued as followups by @{method:queueTask}. * * @return list> Queued task specifications. */ final public function getQueuedTasks() { return $this->queuedTasks; } } diff --git a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php index a999fe5e82..145a371e7d 100644 --- a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php +++ b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php @@ -1,11 +1,6 @@