diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a650e33fcf..93efcc65e1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -444,6 +444,7 @@ phutil_register_library_map(array( 'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php', 'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php', + 'DifferentialDraftField' => 'applications/differential/customfield/DifferentialDraftField.php', 'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php', 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', @@ -5451,6 +5452,7 @@ phutil_register_library_map(array( 'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher', + 'DifferentialDraftField' => 'DifferentialCoreCustomField', 'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception', diff --git a/src/applications/differential/customfield/DifferentialDraftField.php b/src/applications/differential/customfield/DifferentialDraftField.php new file mode 100644 index 0000000000..e37d622a33 --- /dev/null +++ b/src/applications/differential/customfield/DifferentialDraftField.php @@ -0,0 +1,90 @@ +getViewer(); + $revision = $this->getObject(); + + if (!$revision->isDraft()) { + return array(); + } + + $warnings = array(); + + $blocking_map = array( + HarbormasterBuildStatus::STATUS_FAILED, + HarbormasterBuildStatus::STATUS_ABORTED, + HarbormasterBuildStatus::STATUS_ERROR, + HarbormasterBuildStatus::STATUS_PAUSED, + HarbormasterBuildStatus::STATUS_DEADLOCKED, + ); + $blocking_map = array_fuse($blocking_map); + + $builds = $revision->loadActiveBuilds($viewer); + + $waiting = array(); + $blocking = array(); + foreach ($builds as $build) { + if (isset($blocking_map[$build->getBuildStatus()])) { + $blocking[] = $build; + } else { + $waiting[] = $build; + } + } + + $blocking_list = $viewer->renderHandleList(mpull($blocking, 'getPHID')) + ->setAsInline(true); + $waiting_list = $viewer->renderHandleList(mpull($waiting, 'getPHID')) + ->setAsInline(true); + + if ($blocking) { + $warnings[] = pht( + 'This draft revision will not be submitted for review because %s '. + 'build(s) failed: %s.', + phutil_count($blocking), + $blocking_list); + $warnings[] = pht( + 'Fix build failures and update the revision.'); + } else if ($waiting) { + $warnings[] = pht( + 'This draft revision will be sent for review once %s '. + 'build(s) pass: %s.', + phutil_count($waiting), + $waiting_list); + } else { + $warnings[] = pht( + 'This is a draft revision that has not yet been submitted for '. + 'review.'); + } + + return $warnings; + } + +} diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 253d49c1ec..7c9bb01b07 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -1570,58 +1570,12 @@ final class DifferentialTransactionEditor private function hasActiveBuilds($object) { $viewer = $this->requireActor(); - $diff = $object->getActiveDiff(); - $buildables = id(new HarbormasterBuildableQuery()) - ->setViewer($viewer) - ->withContainerPHIDs(array($object->getPHID())) - ->withBuildablePHIDs(array($diff->getPHID())) - ->withManualBuildables(false) - ->execute(); - if (!$buildables) { - return false; - } - - $builds = id(new HarbormasterBuildQuery()) - ->setViewer($viewer) - ->withBuildablePHIDs(mpull($buildables, 'getPHID')) - ->withBuildStatuses( - array( - HarbormasterBuildStatus::STATUS_INACTIVE, - HarbormasterBuildStatus::STATUS_PENDING, - HarbormasterBuildStatus::STATUS_BUILDING, - HarbormasterBuildStatus::STATUS_FAILED, - HarbormasterBuildStatus::STATUS_ABORTED, - HarbormasterBuildStatus::STATUS_ERROR, - HarbormasterBuildStatus::STATUS_PAUSED, - HarbormasterBuildStatus::STATUS_DEADLOCKED, - )) - ->needBuildTargets(true) - ->execute(); + $builds = $object->loadActiveBuilds($viewer); if (!$builds) { return false; } - $active = array(); - foreach ($builds as $key => $build) { - foreach ($build->getBuildTargets() as $target) { - if ($target->isAutotarget()) { - // Ignore autotargets when looking for active of failed builds. If - // local tests fail and you continue anyway, you don't need to - // double-confirm them. - continue; - } - - // This build has at least one real target that's doing something. - $active[$key] = $build; - break; - } - } - - if (!$active) { - return false; - } - return true; } diff --git a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php index 7a694cfd98..586680d1b8 100644 --- a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php @@ -41,6 +41,20 @@ final class DifferentialChangesetOneUpRenderer $column_width = 4; + $aural_minus = javelin_tag( + 'span', + array( + 'aural' => true, + ), + '- '); + + $aural_plus = javelin_tag( + 'span', + array( + 'aural' => true, + ), + '+ '); + $out = array(); foreach ($primitives as $k => $p) { $type = $p['type']; @@ -55,8 +69,10 @@ final class DifferentialChangesetOneUpRenderer if ($is_old) { if ($p['htype']) { $class = 'left old'; + $aural = $aural_minus; } else { $class = 'left'; + $aural = null; } if ($type == 'old-file') { @@ -79,14 +95,20 @@ final class DifferentialChangesetOneUpRenderer ), $line); + $render = $p['render']; + if ($aural !== null) { + $render = array($aural, $render); + } + $cells[] = phutil_tag('th', array('class' => $class)); $cells[] = $no_copy; - $cells[] = phutil_tag('td', array('class' => $class), $p['render']); + $cells[] = phutil_tag('td', array('class' => $class), $render); $cells[] = $no_coverage; } else { if ($p['htype']) { $class = 'right new'; $cells[] = phutil_tag('th', array('class' => $class)); + $aural = $aural_plus; } else { $class = 'right'; if ($left_prefix) { @@ -98,6 +120,7 @@ final class DifferentialChangesetOneUpRenderer $oline = $p['oline']; $cells[] = phutil_tag('th', array('id' => $left_id), $oline); + $aural = null; } if ($type == 'new-file') { @@ -120,8 +143,13 @@ final class DifferentialChangesetOneUpRenderer ), $line); + $render = $p['render']; + if ($aural !== null) { + $render = array($aural, $render); + } + $cells[] = $no_copy; - $cells[] = phutil_tag('td', array('class' => $class), $p['render']); + $cells[] = phutil_tag('td', array('class' => $class), $render); $cells[] = $no_coverage; } diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index e29db36b14..76c7c4da95 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -67,13 +67,19 @@ final class DifferentialRevision extends DifferentialDAO $view_policy = $app->getPolicy( DifferentialDefaultViewCapability::CAPABILITY); + if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) { + $initial_state = DifferentialRevisionStatus::DRAFT; + } else { + $initial_state = DifferentialRevisionStatus::NEEDS_REVIEW; + } + return id(new DifferentialRevision()) ->setViewPolicy($view_policy) ->setAuthorPHID($actor->getPHID()) ->attachRepository(null) ->attachActiveDiff(null) ->attachReviewers(array()) - ->setModernRevisionStatus(DifferentialRevisionStatus::NEEDS_REVIEW); + ->setModernRevisionStatus($initial_state); } protected function getConfiguration() { @@ -702,6 +708,58 @@ final class DifferentialRevision extends DifferentialDAO return false; } + public function loadActiveBuilds(PhabricatorUser $viewer) { + $diff = $this->getActiveDiff(); + + $buildables = id(new HarbormasterBuildableQuery()) + ->setViewer($viewer) + ->withContainerPHIDs(array($this->getPHID())) + ->withBuildablePHIDs(array($diff->getPHID())) + ->withManualBuildables(false) + ->execute(); + if (!$buildables) { + return array(); + } + + $builds = id(new HarbormasterBuildQuery()) + ->setViewer($viewer) + ->withBuildablePHIDs(mpull($buildables, 'getPHID')) + ->withBuildStatuses( + array( + HarbormasterBuildStatus::STATUS_INACTIVE, + HarbormasterBuildStatus::STATUS_PENDING, + HarbormasterBuildStatus::STATUS_BUILDING, + HarbormasterBuildStatus::STATUS_FAILED, + HarbormasterBuildStatus::STATUS_ABORTED, + HarbormasterBuildStatus::STATUS_ERROR, + HarbormasterBuildStatus::STATUS_PAUSED, + HarbormasterBuildStatus::STATUS_DEADLOCKED, + )) + ->needBuildTargets(true) + ->execute(); + if (!$builds) { + return array(); + } + + $active = array(); + foreach ($builds as $key => $build) { + foreach ($build->getBuildTargets() as $target) { + if ($target->isAutotarget()) { + // Ignore autotargets when looking for active of failed builds. If + // local tests fail and you continue anyway, you don't need to + // double-confirm them. + continue; + } + + // This build has at least one real target that's doing something. + $active[$key] = $build; + break; + } + } + + return $active; + } + /* -( HarbormasterBuildableInterface )------------------------------------- */ diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php index b649ca65a8..f9e9f74774 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php @@ -256,6 +256,66 @@ final class DiffusionLowLevelResolveRefsQuery return $results; } + // If some of the refs look like hashes, try to bulk resolve them. This + // workflow happens via RefEngine and bulk resolution is dramatically + // faster than individual resolution. See PHI158. + + $hashlike = array(); + foreach ($unresolved as $key => $ref) { + if (preg_match('/^[a-f0-9]{40}\z/', $ref)) { + $hashlike[$key] = $ref; + } + } + + if (count($hashlike) > 1) { + $hashlike_map = array(); + + $hashlike_groups = array_chunk($hashlike, 64, true); + foreach ($hashlike_groups as $hashlike_group) { + $hashlike_arg = array(); + foreach ($hashlike_group as $hashlike_ref) { + $hashlike_arg[] = hgsprintf('%s', $hashlike_ref); + } + $hashlike_arg = '('.implode(' or ', $hashlike_arg).')'; + + list($err, $refs) = $repository->execLocalCommand( + 'log --template=%s --rev %s', + '{node}\n', + $hashlike_arg); + if ($err) { + // NOTE: If any ref fails to resolve, Mercurial will exit with an + // error. We just give up on the whole group and resolve it + // individually below. In theory, we could split it into subgroups + // but the pathway where this bulk resolution matters rarely tries + // to resolve missing refs (see PHI158). + continue; + } + + $refs = phutil_split_lines($refs, false); + + foreach ($refs as $ref) { + $hashlike_map[$ref] = true; + } + } + + foreach ($unresolved as $key => $ref) { + if (!isset($hashlike_map[$ref])) { + continue; + } + + $results[$ref][] = array( + 'type' => 'commit', + 'identifier' => $ref, + ); + + unset($unresolved[$key]); + } + } + + if (!$unresolved) { + return $results; + } + // If we still have unresolved refs (which might be things like "tip"), // try to resolve them individually. diff --git a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php index 66e15b634a..1dfc944f63 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php @@ -15,4 +15,8 @@ final class PhabricatorOwnersPackageTransaction return 'PhabricatorOwnersPackageTransactionType'; } + public function getApplicationTransactionCommentObject() { + return null; + } + } diff --git a/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php index 4b983fe0f9..1a1124b4d8 100644 --- a/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php @@ -16,19 +16,49 @@ final class PhabricatorSearchManagementNgramsWorkflow 'name' => 'reset', 'help' => pht('Reset all common ngram records.'), ), + array( + 'name' => 'threshold', + 'param' => 'threshold', + 'help' => pht( + 'Prune ngrams present in more than this fraction of '. + 'documents. Provide a value between 0.0 and 1.0.'), + ), )); } public function execute(PhutilArgumentParser $args) { + $min_documents = 4096; + $is_reset = $args->getArg('reset'); + $threshold = $args->getArg('threshold'); + + if ($is_reset && $threshold !== null) { + throw new PhutilArgumentUsageException( + pht('Specify either --reset or --threshold, not both.')); + } + + if (!$is_reset && $threshold === null) { + throw new PhutilArgumentUsageException( + pht('Specify either --reset or --threshold.')); + } + + if (!$is_reset) { + if (!is_numeric($threshold)) { + throw new PhutilArgumentUsageException( + pht('Specify a numeric threshold between 0 and 1.')); + } + + $threshold = (double)$threshold; + if ($threshold <= 0 || $threshold >= 1) { + throw new PhutilArgumentUsageException( + pht('Threshold must be greater than 0.0 and less than 1.0.')); + } + } $all_objects = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFerretInterface') ->execute(); - $min_documents = 4096; - $threshold = 0.15; - foreach ($all_objects as $object) { $engine = $object->newFerretEngine(); $conn = $object->establishConnection('w'); diff --git a/src/docs/user/userguide/prototypes.diviner b/src/docs/user/userguide/prototypes.diviner index 7352502f99..c84bad1178 100644 --- a/src/docs/user/userguide/prototypes.diviner +++ b/src/docs/user/userguide/prototypes.diviner @@ -9,8 +9,6 @@ Overview Phabricator includes //prototype applications//, which are applications in an early stage of development. -IMPORTANT: The upstream does not offer support for these applications. - When we begin working on a new application, we usually implement it as a prototype first. This allows us to get a better sense of how the application might work and integrate with other applications, and what technical and product @@ -32,34 +30,3 @@ on hold indefinitely if we're less excited about it after we begin building it. If you're interested in previewing upcoming applications, you can use the `phabricator.show-prototypes` configuration setting to enable prototypes. - -Feedback on Prototypes -====================== - -We're usually interested in this sort of feedback on prototypes: - - - {icon check, color=green} **Use Cases**: If we're building something that - you think you'd use, we'd love to hear about your use cases for it. This can - help us figure out what features to add and how users may think about, use, - and integrate the application. - - {icon check, color=green} **General Interest**: Is an application something - you're looking forward to? Knowing which applications users are interested - in can help us set priorities. - -We're usually **not** interested in this sort of feedback on prototypes: - - - {icon times, color=red} **Support Requests**: We do not support these - applications. Use them at your own risk, or wait for them to leave the - prototype phase. - - {icon times, color=red} **Bug Reports**: We know these applications don't - work well yet, and usually know about most of the open bugs. Even if we - don't, whatever isn't working yet may change completely before the - application leaves the prototype phase. - - {icon times, color=red} **Contributions / Pull Requests**: These - applications are usually in too early a state to accept contributions. Let - us know about your use case, but wait for release to send code. - -Overall, using prototypes makes it easier for us to explore and develop -application ideas, and to share a preview of what's coming in the future with -users, but prototypes are not yet full applications and we do not provide -support until applications leave the prototype phase. diff --git a/support/startup/PhabricatorClientRateLimit.php b/support/startup/PhabricatorClientRateLimit.php index 85a6def878..89a273e3bf 100644 --- a/support/startup/PhabricatorClientRateLimit.php +++ b/support/startup/PhabricatorClientRateLimit.php @@ -35,7 +35,15 @@ final class PhabricatorClientRateLimit // If the user was logged in, let them make more requests. if (isset($request_state['viewer'])) { $viewer = $request_state['viewer']; - if ($viewer->isLoggedIn()) { + if ($viewer->isOmnipotent()) { + // If the viewer was omnipotent, this was an intracluster request or + // some other kind of special request, so don't give it any points + // toward rate limiting. + $score = 0; + } else if ($viewer->isLoggedIn()) { + // If the viewer was logged in, give them fewer points than if they + // were logged out, since this traffic is much more likely to be + // legitimate. $score = 0.25; } }