1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-09-20 00:49:11 +02:00

Bring "pro" browse queries from modern hardpoint code

Summary: Ref T11968. Merge the modern hardpoint queries and refs for the "browse" workflow as "pro" variations.

Test Plan: Ran `arc inspect browse(...)` for objects, paths, commits, and revisions.

Maniphest Tasks: T11968

Differential Revision: https://secure.phabricator.com/D21080
This commit is contained in:
epriestley 2020-04-10 06:36:47 -07:00
parent 8bb81217d5
commit 9e72e4ed1d
12 changed files with 596 additions and 3 deletions

View file

@ -57,13 +57,22 @@ phutil_register_library_map(array(
'ArcanistBranchRefPro' => 'ref/ArcanistBranchRefPro.php',
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
'ArcanistBrowseCommitHardpointLoader' => 'browse/loader/ArcanistBrowseCommitHardpointLoader.php',
'ArcanistBrowseCommitHardpointQuery' => 'browse/query/ArcanistBrowseCommitHardpointQuery.php',
'ArcanistBrowseCommitURIHardpointLoader' => 'browse/loader/ArcanistBrowseCommitURIHardpointLoader.php',
'ArcanistBrowseCommitURIHardpointQuery' => 'browse/query/ArcanistBrowseCommitURIHardpointQuery.php',
'ArcanistBrowseObjectNameURIHardpointLoader' => 'browse/loader/ArcanistBrowseObjectNameURIHardpointLoader.php',
'ArcanistBrowseObjectNameURIHardpointQuery' => 'browse/query/ArcanistBrowseObjectNameURIHardpointQuery.php',
'ArcanistBrowsePathURIHardpointLoader' => 'browse/loader/ArcanistBrowsePathURIHardpointLoader.php',
'ArcanistBrowsePathURIHardpointQuery' => 'browse/query/ArcanistBrowsePathURIHardpointQuery.php',
'ArcanistBrowseRef' => 'browse/ref/ArcanistBrowseRef.php',
'ArcanistBrowseRefInspector' => 'inspector/ArcanistBrowseRefInspector.php',
'ArcanistBrowseRefPro' => 'browse/ref/ArcanistBrowseRefPro.php',
'ArcanistBrowseRevisionURIHardpointLoader' => 'browse/loader/ArcanistBrowseRevisionURIHardpointLoader.php',
'ArcanistBrowseRevisionURIHardpointQuery' => 'browse/query/ArcanistBrowseRevisionURIHardpointQuery.php',
'ArcanistBrowseURIHardpointLoader' => 'browse/loader/ArcanistBrowseURIHardpointLoader.php',
'ArcanistBrowseURIHardpointQuery' => 'browse/query/ArcanistBrowseURIHardpointQuery.php',
'ArcanistBrowseURIRef' => 'browse/ref/ArcanistBrowseURIRef.php',
'ArcanistBrowseURIRefPro' => 'browse/ref/ArcanistBrowseURIRefPro.php',
'ArcanistBrowseWorkflow' => 'browse/workflow/ArcanistBrowseWorkflow.php',
'ArcanistBuildPlanRef' => 'ref/ArcanistBuildPlanRef.php',
'ArcanistBuildRef' => 'ref/ArcanistBuildRef.php',
@ -1031,13 +1040,22 @@ phutil_register_library_map(array(
'ArcanistBranchRefPro' => 'ArcanistRefPro',
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
'ArcanistBrowseCommitHardpointLoader' => 'ArcanistHardpointLoader',
'ArcanistBrowseCommitHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
'ArcanistBrowseCommitURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
'ArcanistBrowseCommitURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
'ArcanistBrowseObjectNameURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
'ArcanistBrowseObjectNameURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
'ArcanistBrowsePathURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
'ArcanistBrowsePathURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
'ArcanistBrowseRef' => 'ArcanistRef',
'ArcanistBrowseRefInspector' => 'ArcanistRefInspector',
'ArcanistBrowseRefPro' => 'ArcanistRefPro',
'ArcanistBrowseRevisionURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
'ArcanistBrowseRevisionURIHardpointQuery' => 'ArcanistBrowseURIHardpointQuery',
'ArcanistBrowseURIHardpointLoader' => 'ArcanistHardpointLoader',
'ArcanistBrowseURIHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
'ArcanistBrowseURIRef' => 'ArcanistRef',
'ArcanistBrowseURIRefPro' => 'ArcanistRefPro',
'ArcanistBrowseWorkflow' => 'ArcanistArcWorkflow',
'ArcanistBuildPlanRef' => 'Phobject',
'ArcanistBuildRef' => 'Phobject',

View file

@ -0,0 +1,65 @@
<?php
final class ArcanistBrowseCommitHardpointQuery
extends ArcanistWorkflowHardpointQuery {
public function getHardpoints() {
return array(
ArcanistBrowseRefPro::HARDPOINT_COMMITREFS,
);
}
protected function canLoadRef(ArcanistRefPro $ref) {
return ($ref instanceof ArcanistBrowseRefPro);
}
public function loadHardpoint(array $refs, $hardpoint) {
$api = $this->getRepositoryAPI();
$commit_map = array();
foreach ($refs as $key => $ref) {
$token = $ref->getToken();
if ($token === '.') {
// Git resolves "." like HEAD, but we want to treat it as "browse the
// current directory" instead in all cases.
continue;
}
// Always resolve the empty token; top-level loaders filter out
// irrelevant tokens before this stage.
if ($token === null) {
$token = $api->getHeadCommit();
}
// TODO: We should pull a full commit ref out of the API as soon as it
// is able to provide them. In particular, we currently miss Git tree
// hashes which reduces the accuracy of lookups.
try {
$commit = $api->getCanonicalRevisionName($token);
if ($commit) {
$commit_map[$commit][] = $key;
}
} catch (Exception $ex) {
// Ignore anything we can't resolve.
}
}
if (!$commit_map) {
yield $this->yieldMap(array());
}
$results = array();
foreach ($commit_map as $commit_identifier => $ref_keys) {
foreach ($ref_keys as $key) {
$commit_ref = id(new ArcanistCommitRefPro())
->setCommitHash($commit_identifier);
$results[$key][] = $commit_ref;
}
}
yield $this->yieldMap($results);
}
}

View file

@ -0,0 +1,76 @@
<?php
final class ArcanistBrowseCommitURIHardpointQuery
extends ArcanistBrowseURIHardpointQuery {
const BROWSETYPE = 'commit';
public function willLoadBrowseURIRefs(array $refs) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
return;
}
$query = $this->getQuery();
$working_ref = $query->getWorkingCopyRef();
if (!$working_ref) {
// If we aren't in a working copy, don't warn about this.
return;
}
$repository_ref = $this->getQuery()->getRepositoryRef();
if (!$repository_ref) {
echo pht(
'NO REPOSITORY: Unable to determine which repository this working '.
'copy belongs to, so arguments can not be resolved as commits. Use '.
'"%s" to understand how repositories are resolved.',
'arc which');
echo "\n";
return;
}
}
public function loadHardpoint(array $refs, $hardpoint) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
yield $this->yieldMap(array());
}
yield $this->yieldRequests(
$refs,
array(
ArcanistBrowseRefPro::HARDPOINT_COMMITREFS,
));
$commit_refs = array();
foreach ($refs as $key => $ref) {
foreach ($ref->getCommitRefs() as $commit_ref) {
$commit_refs[] = $commit_ref;
}
}
yield $this->yieldRequests(
$commit_refs,
array(
ArcanistCommitRefPro::HARDPOINT_UPSTREAM,
));
$results = array();
foreach ($refs as $key => $ref) {
$commit_refs = $ref->getCommitRefs();
foreach ($commit_refs as $commit_ref) {
$uri = $commit_ref->getURI();
if ($uri !== null) {
$results[$key][] = $this->newBrowseURIRef()
->setURI($uri);
}
}
}
yield $this->yieldMap($results);
}
}

View file

@ -0,0 +1,56 @@
<?php
final class ArcanistBrowseObjectNameURIHardpointQuery
extends ArcanistBrowseURIHardpointQuery {
const BROWSETYPE = 'object';
public function loadHardpoint(array $refs, $hardpoint) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
yield $this->yieldMap(array());
}
$name_map = array();
$token_set = array();
foreach ($refs as $key => $ref) {
$token = $ref->getToken();
if (!strlen($token)) {
continue;
}
$name_map[$key] = $token;
$token_set[$token] = $token;
}
if (!$token_set) {
yield $this->yieldMap(array());
}
$objects = (yield $this->yieldConduit(
'phid.lookup',
array(
'names' => $token_set,
)));
$result = array();
foreach ($name_map as $ref_key => $token) {
$object = idx($objects, $token);
if (!$object) {
continue;
}
$uri = idx($object, 'uri');
if (!strlen($uri)) {
continue;
}
$result[$ref_key][] = $this->newBrowseURIRef()
->setURI($object['uri']);
}
yield $this->yieldMap($result);
}
}

View file

@ -0,0 +1,128 @@
<?php
final class ArcanistBrowsePathURIHardpointQuery
extends ArcanistBrowseURIHardpointQuery {
const BROWSETYPE = 'path';
public function willLoadBrowseURIRefs(array $refs) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
return;
}
$query = $this->getQuery();
$working_ref = $query->getWorkingCopyRef();
if (!$working_ref) {
echo pht(
'NO WORKING COPY: The current directory is not a repository '.
'working copy, so arguments can not be resolved as paths. Run '.
'this command inside a working copy to resolve paths.');
echo "\n";
return;
}
$repository_ref = $query->getRepositoryRef();
if (!$repository_ref) {
echo pht(
'NO REPOSITORY: Unable to determine which repository this working '.
'copy belongs to, so arguments can not be resolved as paths. Use '.
'"%s" to understand how repositories are resolved.',
'arc which');
echo "\n";
return;
}
}
public function didFailToLoadBrowseURIRefs(array $refs) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
return;
}
$query = $this->getQuery();
$working_ref = $query->getWorkingCopyRef();
if (!$working_ref) {
return;
}
$repository_ref = $query->getRepositoryRef();
if (!$repository_ref) {
return;
}
echo pht(
'Use "--types path" to force arguments to be interpreted as paths.');
echo "\n";
}
public function loadHardpoint(array $refs, $hardpoint) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
yield $this->yieldMap(array());
}
$repository_ref = (yield $this->yieldRepositoryRef());
if (!$repository_ref) {
yield $this->yieldMap(array());
}
$working_copy = $this->getWorkingCopy();
$working_root = $working_copy->getPath();
$results = array();
foreach ($refs as $key => $ref) {
$is_path = $ref->hasType(self::BROWSETYPE);
$path = $ref->getToken();
if ($path === null) {
// If we're explicitly resolving no arguments as a path, treat it
// as the current working directory.
if ($is_path) {
$path = '.';
} else {
continue;
}
}
$lines = null;
$parts = explode(':', $path);
if (count($parts) > 1) {
$lines = array_pop($parts);
}
$path = implode(':', $parts);
$full_path = Filesystem::resolvePath($path);
if (!Filesystem::pathExists($full_path)) {
if (!$is_path) {
continue;
}
}
if ($full_path == $working_root) {
$path = '';
} else {
$path = Filesystem::readablePath($full_path, $working_root);
}
$params = array(
'path' => $path,
'lines' => $lines,
'branch' => $ref->getBranch(),
);
$uri = $repository_ref->newBrowseURI($params);
$results[$key][] = $this->newBrowseURIRef()
->setURI($uri);
}
yield $this->yieldMap($results);
}
}

View file

@ -0,0 +1,62 @@
<?php
final class ArcanistBrowseRevisionURIHardpointQuery
extends ArcanistBrowseURIHardpointQuery {
const BROWSETYPE = 'revision';
public function loadHardpoint(array $refs, $hardpoint) {
$refs = $this->getRefsWithSupportedTypes($refs);
if (!$refs) {
yield $this->yieldMap(array());
}
yield $this->yieldRequests(
$refs,
array(
ArcanistBrowseRefPro::HARDPOINT_COMMITREFS,
));
$states = array();
$map = array();
foreach ($refs as $key => $ref) {
foreach ($ref->getCommitRefs() as $commit_ref) {
$hash = $commit_ref->getCommitHash();
$states[$hash] = id(new ArcanistWorkingCopyStateRefPro())
->setCommitRef($commit_ref);
$map[$hash][] = $key;
}
}
if (!$states) {
yield $this->yieldMap(array());
}
yield $this->yieldRequests(
$states,
array(
'revisionRefs',
));
$results = array();
foreach ($states as $hash => $state) {
foreach ($state->getRevisionRefs() as $revision) {
if ($revision->isClosed()) {
// Don't resolve closed revisions.
continue;
}
$uri = $revision->getURI();
foreach ($map[$hash] as $key) {
$results[$key][] = $this->newBrowseURIRef()
->setURI($uri);
}
}
}
yield $this->yieldMap($results);
}
}

View file

@ -0,0 +1,49 @@
<?php
abstract class ArcanistBrowseURIHardpointQuery
extends ArcanistWorkflowHardpointQuery {
public function getSupportedBrowseType() {
return $this->getPhobjectClassConstant('BROWSETYPE', 32);
}
public function getHardpoints() {
return array(
ArcanistBrowseRefPro::HARDPOINT_URIS,
);
}
protected function canLoadRef(ArcanistRefPro $ref) {
return ($ref instanceof ArcanistBrowseRefPro);
}
public function getRefsWithSupportedTypes(array $refs) {
$type = $this->getSupportedBrowseType();
foreach ($refs as $key => $ref) {
if ($ref->isUntyped()) {
continue;
}
if ($ref->hasType($type)) {
continue;
}
unset($refs[$key]);
}
return $refs;
}
public static function getAllBrowseQueries() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
}
final protected function newBrowseURIRef() {
return id(new ArcanistBrowseURIRefPro())
->setType($this->getSupportedBrowseType());
}
}

View file

@ -0,0 +1,69 @@
<?php
final class ArcanistBrowseRefPro
extends ArcanistRefPro {
const HARDPOINT_URIS = 'uris';
const HARDPOINT_COMMITREFS = 'commitRefs';
private $token;
private $types = array();
private $branch;
public function getRefDisplayName() {
return pht('Browse Query "%s"', $this->getToken());
}
protected function newHardpoints() {
return array(
$this->newVectorHardpoint(self::HARDPOINT_COMMITREFS),
$this->newVectorHardpoint(self::HARDPOINT_URIS),
);
}
public function setToken($token) {
$this->token = $token;
return $this;
}
public function getToken() {
return $this->token;
}
public function setTypes(array $types) {
$this->types = $types;
return $this;
}
public function getTypes() {
return $this->types;
}
public function hasType($type) {
$map = $this->getTypes();
$map = array_fuse($map);
return isset($map[$type]);
}
public function isUntyped() {
return !$this->types;
}
public function setBranch($branch) {
$this->branch = $branch;
return $this;
}
public function getBranch() {
return $this->branch;
}
public function getURIs() {
return $this->getHardpoint(self::HARDPOINT_URIS);
}
public function getCommitRefs() {
return $this->getHardpoint(self::HARDPOINT_COMMITREFS);
}
}

View file

@ -0,0 +1,35 @@
<?php
final class ArcanistBrowseURIRefPro
extends ArcanistRefPro {
private $uri;
private $type;
public function getRefDisplayName() {
return pht('Browse URI "%s"', $this->getURI());
}
public function defineHardpoints() {
return array();
}
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
}

View file

@ -171,6 +171,19 @@ final class ArcanistHardpointTask
$result = $generator_result->getValue();
} else {
$result = $generator->getReturn();
if ($result instanceof ArcanistHardpointTaskResult) {
throw new Exception(
pht(
'Generator (for query "%s") returned an '.
'"ArcanistHardpointTaskResult" object, which is not a valid '.
'thing to return from a generator.'.
"\n\n".
'This almost always means the generator implementation has a '.
'"return $this->yield..." statement which should be '.
'a "yield $this->yield..." instead.',
get_class($query)));
}
}
$this->attachResult($result);

View file

@ -0,0 +1,22 @@
<?php
final class ArcanistBrowseRefInspector
extends ArcanistRefInspector {
public function getInspectFunctionName() {
return 'browse';
}
public function newInspectRef(array $argv) {
if (count($argv) !== 1) {
throw new PhutilArgumentUsageException(
pht(
'Expected exactly one argument to "browse(...)" with a '.
'token.'));
}
return id(new ArcanistBrowseRefPro())
->setToken($argv[0]);
}
}

View file

@ -21,7 +21,7 @@ EOTEXT
public function getWorkflowArguments() {
return array(
$this->newWorkflowArgument('all')
$this->newWorkflowArgument('explore')
->setHelp(pht('Load all object hardpoints.')),
$this->newWorkflowArgument('objects')
->setWildcard(true),
@ -29,7 +29,7 @@ EOTEXT
}
public function runWorkflow() {
$is_all = $this->getArgument('all');
$is_explore = $this->getArgument('explore');
$objects = $this->getArgument('objects');
$inspectors = ArcanistRefInspector::getAllInspectors();
@ -88,7 +88,7 @@ EOTEXT
$all_refs[] = $ref;
}
if ($is_all) {
if ($is_explore) {
foreach ($ref_lists as $ref_class => $refs) {
$ref = head($refs);