From adea2550f5335fe87a0de55c6d364caf89895162 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 8 Apr 2020 12:52:41 -0700 Subject: [PATCH] Introduce "arc inspect" and some of the new ref/hardpoint classes Summary: Ref T11968. Inches toward the new ref/hardpoint code by introducing the modern refs as "RefPro" objects and supporting an "arc inspect " to load objects and hardpoints. This doesn't impact any existing runtime behavior. Test Plan: Ran "arc inspect [--all] commit(...)", got hardpoint queries and yield-based data fetching. Maniphest Tasks: T11968 Differential Revision: https://secure.phabricator.com/D21078 --- src/__phutil_library_map__.php | 24 ++ src/hardpoint/ArcanistHardpointList.php | 4 + src/hardpoint/ArcanistHardpointTask.php | 25 ++- src/hardpoint/ArcanistHardpointTaskResult.php | 16 ++ src/inspector/ArcanistCommitRefInspector.php | 22 ++ src/inspector/ArcanistRefInspector.php | 16 ++ .../ArcanistCommitUpstreamHardpointQuery.php | 53 +++++ ...ArcanistGitCommitMessageHardpointQuery.php | 53 +++++ ...rcanistWorkingCopyCommitHardpointQuery.php | 39 ++++ src/ref/ArcanistBranchRefPro.php | 57 +++++ src/ref/ArcanistCommitRefPro.php | 93 ++++++++ src/ref/ArcanistRefPro.php | 8 + src/ref/ArcanistWorkingCopyStateRefPro.php | 62 ++++++ src/repository/api/ArcanistFilesystemAPI.php | 6 +- .../query/ArcanistWorkflowHardpointQuery.php | 88 ++++++++ src/workflow/ArcanistInspectWorkflow.php | 207 ++++++++++++++++++ src/workflow/ArcanistWorkflow.php | 38 ++++ .../ArcanistFilesystemWorkingCopy.php | 2 +- src/workingcopy/ArcanistWorkingCopy.php | 2 +- 19 files changed, 803 insertions(+), 12 deletions(-) create mode 100644 src/hardpoint/ArcanistHardpointTaskResult.php create mode 100644 src/inspector/ArcanistCommitRefInspector.php create mode 100644 src/inspector/ArcanistRefInspector.php create mode 100644 src/query/ArcanistCommitUpstreamHardpointQuery.php create mode 100644 src/query/ArcanistGitCommitMessageHardpointQuery.php create mode 100644 src/query/ArcanistWorkingCopyCommitHardpointQuery.php create mode 100644 src/ref/ArcanistBranchRefPro.php create mode 100644 src/ref/ArcanistCommitRefPro.php create mode 100644 src/ref/ArcanistRefPro.php create mode 100644 src/ref/ArcanistWorkingCopyStateRefPro.php create mode 100644 src/toolset/query/ArcanistWorkflowHardpointQuery.php create mode 100644 src/workflow/ArcanistInspectWorkflow.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c39e766e..53c43926 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -54,6 +54,7 @@ phutil_register_library_map(array( 'ArcanistBraceFormattingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBraceFormattingXHPASTLinterRule.php', 'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php', 'ArcanistBranchRef' => 'ref/ArcanistBranchRef.php', + 'ArcanistBranchRefPro' => 'ref/ArcanistBranchRefPro.php', 'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php', 'ArcanistBrowseCommitHardpointLoader' => 'browse/loader/ArcanistBrowseCommitHardpointLoader.php', 'ArcanistBrowseCommitURIHardpointLoader' => 'browse/loader/ArcanistBrowseCommitURIHardpointLoader.php', @@ -102,7 +103,10 @@ phutil_register_library_map(array( 'ArcanistCommentStyleXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistCommentStyleXHPASTLinterRule.php', 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistCommentStyleXHPASTLinterRuleTestCase.php', 'ArcanistCommitRef' => 'ref/ArcanistCommitRef.php', + 'ArcanistCommitRefInspector' => 'inspector/ArcanistCommitRefInspector.php', + 'ArcanistCommitRefPro' => 'ref/ArcanistCommitRefPro.php', 'ArcanistCommitUpstreamHardpointLoader' => 'loader/ArcanistCommitUpstreamHardpointLoader.php', + 'ArcanistCommitUpstreamHardpointQuery' => 'query/ArcanistCommitUpstreamHardpointQuery.php', 'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php', 'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php', 'ArcanistComposerLinter' => 'lint/linter/ArcanistComposerLinter.php', @@ -203,6 +207,7 @@ phutil_register_library_map(array( 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php', 'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php', 'ArcanistGitCommitMessageHardpointLoader' => 'loader/ArcanistGitCommitMessageHardpointLoader.php', + 'ArcanistGitCommitMessageHardpointQuery' => 'query/ArcanistGitCommitMessageHardpointQuery.php', 'ArcanistGitHardpointLoader' => 'loader/ArcanistGitHardpointLoader.php', 'ArcanistGitLandEngine' => 'land/ArcanistGitLandEngine.php', 'ArcanistGitRevisionHardpointLoader' => 'loader/ArcanistGitRevisionHardpointLoader.php', @@ -226,6 +231,7 @@ phutil_register_library_map(array( 'ArcanistHardpointRequest' => 'hardpoint/ArcanistHardpointRequest.php', 'ArcanistHardpointRequestList' => 'hardpoint/ArcanistHardpointRequestList.php', 'ArcanistHardpointTask' => 'hardpoint/ArcanistHardpointTask.php', + 'ArcanistHardpointTaskResult' => 'hardpoint/ArcanistHardpointTaskResult.php', 'ArcanistHelpWorkflow' => 'toolset/workflow/ArcanistHelpWorkflow.php', 'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule.php', 'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase.php', @@ -245,6 +251,7 @@ phutil_register_library_map(array( 'ArcanistInlineHTMLXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInlineHTMLXHPASTLinterRuleTestCase.php', 'ArcanistInnerFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistInnerFunctionXHPASTLinterRule.php', 'ArcanistInnerFunctionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInnerFunctionXHPASTLinterRuleTestCase.php', + 'ArcanistInspectWorkflow' => 'workflow/ArcanistInspectWorkflow.php', 'ArcanistInstallCertificateWorkflow' => 'workflow/ArcanistInstallCertificateWorkflow.php', 'ArcanistInstanceOfOperatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistInstanceOfOperatorXHPASTLinterRule.php', 'ArcanistInstanceofOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistInstanceofOperatorXHPASTLinterRuleTestCase.php', @@ -381,6 +388,8 @@ phutil_register_library_map(array( 'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php', 'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase.php', 'ArcanistRef' => 'ref/ArcanistRef.php', + 'ArcanistRefInspector' => 'inspector/ArcanistRefInspector.php', + 'ArcanistRefPro' => 'ref/ArcanistRefPro.php', 'ArcanistRefQuery' => 'ref/ArcanistRefQuery.php', 'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php', 'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php', @@ -489,12 +498,15 @@ phutil_register_library_map(array( 'ArcanistWildConfigOption' => 'config/option/ArcanistWildConfigOption.php', 'ArcanistWorkflow' => 'workflow/ArcanistWorkflow.php', 'ArcanistWorkflowArgument' => 'toolset/ArcanistWorkflowArgument.php', + 'ArcanistWorkflowHardpointQuery' => 'toolset/query/ArcanistWorkflowHardpointQuery.php', 'ArcanistWorkflowInformation' => 'toolset/ArcanistWorkflowInformation.php', 'ArcanistWorkingCopy' => 'workingcopy/ArcanistWorkingCopy.php', + 'ArcanistWorkingCopyCommitHardpointQuery' => 'query/ArcanistWorkingCopyCommitHardpointQuery.php', 'ArcanistWorkingCopyConfigurationSource' => 'config/source/ArcanistWorkingCopyConfigurationSource.php', 'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php', 'ArcanistWorkingCopyPath' => 'workingcopy/ArcanistWorkingCopyPath.php', 'ArcanistWorkingCopyStateRef' => 'ref/ArcanistWorkingCopyStateRef.php', + 'ArcanistWorkingCopyStateRefPro' => 'ref/ArcanistWorkingCopyStateRefPro.php', 'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/ArcanistXHPASTLintNamingHook.php', 'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php', 'ArcanistXHPASTLintSwitchHook' => 'lint/linter/xhpast/ArcanistXHPASTLintSwitchHook.php', @@ -1010,6 +1022,7 @@ phutil_register_library_map(array( 'ArcanistBraceFormattingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistBranchRef' => 'ArcanistRef', + 'ArcanistBranchRefPro' => 'ArcanistRefPro', 'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow', 'ArcanistBrowseCommitHardpointLoader' => 'ArcanistHardpointLoader', 'ArcanistBrowseCommitURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader', @@ -1058,7 +1071,10 @@ phutil_register_library_map(array( 'ArcanistCommentStyleXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistCommentStyleXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistCommitRef' => 'ArcanistRef', + 'ArcanistCommitRefInspector' => 'ArcanistRefInspector', + 'ArcanistCommitRefPro' => 'ArcanistRefPro', 'ArcanistCommitUpstreamHardpointLoader' => 'ArcanistHardpointLoader', + 'ArcanistCommitUpstreamHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistCommitWorkflow' => 'ArcanistWorkflow', 'ArcanistCompilerLintRenderer' => 'ArcanistLintRenderer', 'ArcanistComposerLinter' => 'ArcanistLinter', @@ -1159,6 +1175,7 @@ phutil_register_library_map(array( 'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistGitAPI' => 'ArcanistRepositoryAPI', 'ArcanistGitCommitMessageHardpointLoader' => 'ArcanistGitHardpointLoader', + 'ArcanistGitCommitMessageHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistGitHardpointLoader' => 'ArcanistHardpointLoader', 'ArcanistGitLandEngine' => 'ArcanistLandEngine', 'ArcanistGitRevisionHardpointLoader' => 'ArcanistGitHardpointLoader', @@ -1182,6 +1199,7 @@ phutil_register_library_map(array( 'ArcanistHardpointRequest' => 'Phobject', 'ArcanistHardpointRequestList' => 'Phobject', 'ArcanistHardpointTask' => 'Phobject', + 'ArcanistHardpointTaskResult' => 'Phobject', 'ArcanistHelpWorkflow' => 'ArcanistWorkflow', 'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistHexadecimalNumericScalarCasingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', @@ -1201,6 +1219,7 @@ phutil_register_library_map(array( 'ArcanistInlineHTMLXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistInnerFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistInnerFunctionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', + 'ArcanistInspectWorkflow' => 'ArcanistArcWorkflow', 'ArcanistInstallCertificateWorkflow' => 'ArcanistWorkflow', 'ArcanistInstanceOfOperatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistInstanceofOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', @@ -1337,6 +1356,8 @@ phutil_register_library_map(array( 'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistRaggedClassTreeEdgeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistRef' => 'Phobject', + 'ArcanistRefInspector' => 'Phobject', + 'ArcanistRefPro' => 'ArcanistHardpointObject', 'ArcanistRefQuery' => 'Phobject', 'ArcanistRepositoryAPI' => 'Phobject', 'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase', @@ -1444,12 +1465,15 @@ phutil_register_library_map(array( 'ArcanistWildConfigOption' => 'ArcanistConfigOption', 'ArcanistWorkflow' => 'Phobject', 'ArcanistWorkflowArgument' => 'Phobject', + 'ArcanistWorkflowHardpointQuery' => 'ArcanistHardpointQuery', 'ArcanistWorkflowInformation' => 'Phobject', 'ArcanistWorkingCopy' => 'Phobject', + 'ArcanistWorkingCopyCommitHardpointQuery' => 'ArcanistWorkflowHardpointQuery', 'ArcanistWorkingCopyConfigurationSource' => 'ArcanistFilesystemConfigurationSource', 'ArcanistWorkingCopyIdentity' => 'Phobject', 'ArcanistWorkingCopyPath' => 'Phobject', 'ArcanistWorkingCopyStateRef' => 'ArcanistRef', + 'ArcanistWorkingCopyStateRefPro' => 'ArcanistRefPro', 'ArcanistXHPASTLintNamingHook' => 'Phobject', 'ArcanistXHPASTLintNamingHookTestCase' => 'PhutilTestCase', 'ArcanistXHPASTLintSwitchHook' => 'Phobject', diff --git a/src/hardpoint/ArcanistHardpointList.php b/src/hardpoint/ArcanistHardpointList.php index e02f6d68..bc44bdde 100644 --- a/src/hardpoint/ArcanistHardpointList.php +++ b/src/hardpoint/ArcanistHardpointList.php @@ -46,6 +46,10 @@ final class ArcanistHardpointList return isset($this->attached[$hardpoint]); } + public function getHardpoints() { + return $this->hardpoints; + } + public function getHardpointDefinition($object, $hardpoint) { if (!$this->hasHardpoint($object, $hardpoint)) { throw new Exception( diff --git a/src/hardpoint/ArcanistHardpointTask.php b/src/hardpoint/ArcanistHardpointTask.php index bd06f374..f1f42f5d 100644 --- a/src/hardpoint/ArcanistHardpointTask.php +++ b/src/hardpoint/ArcanistHardpointTask.php @@ -100,6 +100,7 @@ final class ArcanistHardpointTask $generator->next(); } + $generator_result = null; if ($generator->valid()) { $result = $generator->current(); @@ -152,17 +153,25 @@ final class ArcanistHardpointTask return true; } - throw new Exception( - pht( - 'Hardpoint generator (for query "%s") yielded an unexpected '. - 'value. Generators may only yield "Future" or '. - '"ArcanistHardpointRequest" objects, got "%s".', - get_class($query), - phutil_describe_type($result))); + if ($result instanceof ArcanistHardpointTaskResult) { + $generator_result = $result; + } else { + throw new Exception( + pht( + 'Hardpoint generator (for query "%s") yielded an unexpected '. + 'value (of type "%s").', + get_class($query), + phutil_describe_type($result))); + } } $this->generator = null; - $result = $generator->getReturn(); + + if ($generator_result !== null) { + $result = $generator_result->getValue(); + } else { + $result = $generator->getReturn(); + } $this->attachResult($result); diff --git a/src/hardpoint/ArcanistHardpointTaskResult.php b/src/hardpoint/ArcanistHardpointTaskResult.php new file mode 100644 index 00000000..ca124ce9 --- /dev/null +++ b/src/hardpoint/ArcanistHardpointTaskResult.php @@ -0,0 +1,16 @@ +value = $value; + } + + public function getValue() { + return $this->value; + } + +} diff --git a/src/inspector/ArcanistCommitRefInspector.php b/src/inspector/ArcanistCommitRefInspector.php new file mode 100644 index 00000000..5190e155 --- /dev/null +++ b/src/inspector/ArcanistCommitRefInspector.php @@ -0,0 +1,22 @@ +setCommitHash($argv[0]); + } + +} diff --git a/src/inspector/ArcanistRefInspector.php b/src/inspector/ArcanistRefInspector.php new file mode 100644 index 00000000..ef10f2f3 --- /dev/null +++ b/src/inspector/ArcanistRefInspector.php @@ -0,0 +1,16 @@ +setAncestorClass(__CLASS__) + ->setUniqueMethod('getInspectFunctionName') + ->execute(); + } + +} diff --git a/src/query/ArcanistCommitUpstreamHardpointQuery.php b/src/query/ArcanistCommitUpstreamHardpointQuery.php new file mode 100644 index 00000000..a2b92926 --- /dev/null +++ b/src/query/ArcanistCommitUpstreamHardpointQuery.php @@ -0,0 +1,53 @@ +yieldRepositoryRef()); + if (!$repository_ref) { + yield $this->yieldValue($refs, null); + } + $repository_phid = $repository_ref->getPHID(); + + $commit_map = array(); + foreach ($refs as $key => $ref) { + $hash = $ref->getCommitHash(); + $commit_map[$hash][] = $key; + } + + $commit_info = (yield $this->yieldConduit( + 'diffusion.querycommits', + array( + 'repositoryPHID' => $repository_phid, + 'names' => array_keys($commit_map), + ))); + + $results = array(); + foreach ($commit_map as $hash => $keys) { + $commit_phid = idx($commit_info['identifierMap'], $hash); + if ($commit_phid) { + $commit_data = idx($commit_info['data'], $commit_phid); + } else { + $commit_data = null; + } + + foreach ($keys as $key) { + $results[$key] = $commit_data; + } + } + + yield $this->yieldMap($results); + } + +} diff --git a/src/query/ArcanistGitCommitMessageHardpointQuery.php b/src/query/ArcanistGitCommitMessageHardpointQuery.php new file mode 100644 index 00000000..7d513f41 --- /dev/null +++ b/src/query/ArcanistGitCommitMessageHardpointQuery.php @@ -0,0 +1,53 @@ +getRepositoryAPI(); + return ($api instanceof ArcanistGitAPI); + } + + public function loadHardpoint(array $refs, $hardpoint) { + $api = $this->getRepositoryAPI(); + + $hashes = mpull($refs, 'getCommitHash'); + $unique_hashes = array_fuse($hashes); + + // TODO: Update this to use "%B", see T5028. We can also bulk-resolve + // these with "git show --quiet --format=... hash hash hash ... --". + + $futures = array(); + foreach ($unique_hashes as $hash) { + $futures[$hash] = $api->execFutureLocal( + 'log -n1 --format=%s %s --', + '%s%n%n%b', + $hash); + } + + yield $this->yieldFutures($futures); + + $messages = array(); + foreach ($futures as $hash => $future) { + list($stdout) = $future->resolvex(); + $messages[$hash] = $stdout; + } + + foreach ($hashes as $ref_key => $hash) { + $hashes[$ref_key] = $messages[$hash]; + } + + yield $this->yieldMap($hashes); + } + +} diff --git a/src/query/ArcanistWorkingCopyCommitHardpointQuery.php b/src/query/ArcanistWorkingCopyCommitHardpointQuery.php new file mode 100644 index 00000000..b20baf25 --- /dev/null +++ b/src/query/ArcanistWorkingCopyCommitHardpointQuery.php @@ -0,0 +1,39 @@ +yieldRequests( + $refs, + array( + ArcanistWorkingCopyStateRefPro::HARDPOINT_BRANCHREF, + )); + + $branch_refs = mpull($refs, 'getBranchRef'); + + yield $this->yieldRequests( + $branch_refs, + array( + ArcanistBranchRefPro::HARDPOINT_COMMITREF, + )); + + $results = array(); + foreach ($refs as $key => $ref) { + $results[$key] = $ref->getBranchRef()->getCommitRef(); + } + + yield $this->yieldMap($results); + } + +} diff --git a/src/ref/ArcanistBranchRefPro.php b/src/ref/ArcanistBranchRefPro.php new file mode 100644 index 00000000..6998c2b1 --- /dev/null +++ b/src/ref/ArcanistBranchRefPro.php @@ -0,0 +1,57 @@ +getBranchName()); + } + + protected function newHardpoints() { + return array( + $this->newHardpoint(self::HARDPOINT_COMMITREF), + ); + } + + public function setBranchName($branch_name) { + $this->branchName = $branch_name; + return $this; + } + + public function getBranchName() { + return $this->branchName; + } + + public function setRefName($ref_name) { + $this->refName = $ref_name; + return $this; + } + + public function getRefName() { + return $this->refName; + } + + public function setIsCurrentBranch($is_current_branch) { + $this->isCurrentBranch = $is_current_branch; + return $this; + } + + public function getIsCurrentBranch() { + return $this->isCurrentBranch; + } + + public function attachCommitRef(ArcanistCommitRef $ref) { + return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $ref); + } + + public function getCommitRef() { + return $this->getHardpoint(self::HARDPOINT_COMMITREF); + } + +} diff --git a/src/ref/ArcanistCommitRefPro.php b/src/ref/ArcanistCommitRefPro.php new file mode 100644 index 00000000..c2b03be2 --- /dev/null +++ b/src/ref/ArcanistCommitRefPro.php @@ -0,0 +1,93 @@ +getCommitHash()); + } + + protected function newHardpoints() { + return array( + $this->newHardpoint(self::HARDPOINT_MESSAGE), + $this->newHardpoint(self::HARDPOINT_UPSTREAM), + ); + } + + public function setCommitHash($commit_hash) { + $this->commitHash = $commit_hash; + return $this; + } + + public function getCommitHash() { + return $this->commitHash; + } + + public function setTreeHash($tree_hash) { + $this->treeHash = $tree_hash; + return $this; + } + + public function getTreeHash() { + return $this->treeHash; + } + + public function setCommitEpoch($commit_epoch) { + $this->commitEpoch = $commit_epoch; + return $this; + } + + public function getCommitEpoch() { + return $this->commitEpoch; + } + + public function setAuthorEpoch($author_epoch) { + $this->authorEpoch = $author_epoch; + return $this; + } + + public function getAuthorEpoch() { + return $this->authorEpoch; + } + + public function getSummary() { + $message = $this->getMessage(); + + $message = trim($message); + $lines = phutil_split_lines($message, false); + + return head($lines); + } + + public function attachMessage($message) { + return $this->attachHardpoint(self::HARDPOINT_MESSAGE, $message); + } + + public function getMessage() { + return $this->getHardpoint(self::HARDPOINT_MESSAGE); + } + + public function getURI() { + return $this->getUpstreamProperty('uri'); + } + + private function getUpstreamProperty($key, $default = null) { + $upstream = $this->getHardpoint(self::HARDPOINT_UPSTREAM); + + if (!$upstream) { + return $default; + } + + return idx($upstream, $key, $default); + } + +} diff --git a/src/ref/ArcanistRefPro.php b/src/ref/ArcanistRefPro.php new file mode 100644 index 00000000..8807d871 --- /dev/null +++ b/src/ref/ArcanistRefPro.php @@ -0,0 +1,8 @@ +newHardpoint(self::HARDPOINT_COMMITREF), + $this->newHardpoint(self::HARDPOINT_BRANCHREF), + $this->newVectorHardpoint(self::HARDPOINT_REVISIONREFS), + ); + } + + public function attachBranchRef(ArcanistBranchRef $branch_ref) { + return $this->attachHardpoint(self::HARDPOINT_BRANCHREF, $branch_ref); + } + + public function getBranchRef() { + return $this->getHardpoint(self::HARDPOINT_BRANCHREF); + } + + public function setCommitRef(ArcanistCommitRef $commit_ref) { + return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $commit_ref); + } + + public function getCommitRef() { + return $this->getHardpoint(self::HARDPOINT_COMMITREF); + } + + public function getRevisionRefs() { + return $this->getHardpoint(self::HARDPOINT_REVISIONREFS); + } + + public function getRevisionRef() { + if ($this->hasAmbiguousRevisionRefs()) { + throw new Exception( + pht('State has multiple ambiguous revisions refs.')); + } + + $refs = $this->getRevisionRefs(); + if ($refs) { + return head($refs); + } + + return null; + } + + public function hasAmbiguousRevisionRefs() { + return (count($this->getRevisionRefs()) > 1); + } + +} diff --git a/src/repository/api/ArcanistFilesystemAPI.php b/src/repository/api/ArcanistFilesystemAPI.php index 5626d29a..198e0470 100644 --- a/src/repository/api/ArcanistFilesystemAPI.php +++ b/src/repository/api/ArcanistFilesystemAPI.php @@ -82,7 +82,7 @@ final class ArcanistFilesystemAPI } public function getRemoteURI() { - throw new PhutilMethodNotImplementedException(); + return null; } public function supportsLocalCommits() { @@ -90,7 +90,9 @@ final class ArcanistFilesystemAPI } protected function buildLocalFuture(array $argv) { - throw new PhutilMethodNotImplementedException(); + $future = newv('ExecFuture', $argv); + $future->setCWD($this->getPath()); + return $future; } public function supportsCommitRanges() { diff --git a/src/toolset/query/ArcanistWorkflowHardpointQuery.php b/src/toolset/query/ArcanistWorkflowHardpointQuery.php new file mode 100644 index 00000000..fceafbdd --- /dev/null +++ b/src/toolset/query/ArcanistWorkflowHardpointQuery.php @@ -0,0 +1,88 @@ +workflow = $workflow; + return $this; + } + + final public function getWorkflow() { + return $this->workflow; + } + + final public function getWorkingCopy() { + return $this->getWorkflow()->getWorkingCopy(); + } + + final public function getRepositoryAPI() { + return $this->getWorkingCopy()->getRepositoryAPI(); + } + + public static function getAllQueries() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->execute(); + } + + final public function canLoadObject(ArcanistHardpointObject $object) { + if ($this->canLoadHardpoint === null) { + $this->canLoadHardpoint = $this->canLoadHardpoint(); + } + + if (!$this->canLoadHardpoint) { + return false; + } + + if (!$object instanceof ArcanistRefPro) { + return false; + } + + return $this->canLoadRef($object); + } + + protected function canLoadHardpoint() { + return true; + } + + abstract protected function canLoadRef(ArcanistRefPro $ref); + + final public function yieldConduit($method, array $parameters) { + $conduit_engine = $this->getWorkflow() + ->getConduitEngine(); + + $call_object = $conduit_engine->newCall($method, $parameters); + $call_future = $conduit_engine->newFuture($call_object); + + return $this->yieldFuture($call_future); + } + + final public function yieldRepositoryRef() { + $workflow = $this->getWorkflow(); + + // TODO: This is currently a blocking request, but should yield to the + // hardpoint engine in the future. + + $repository_ref = $workflow->getRepositoryRef(); + $ref_future = new ImmediateFuture($repository_ref); + + return $this->yieldFuture($ref_future); + } + + final public function yieldValue(array $refs, $value) { + assert_instances_of($refs, 'ArcanistRefPro'); + + $keys = array_keys($refs); + $map = array_fill_keys($keys, $value); + return $this->yieldMap($map); + } + + final public function yieldMap(array $map) { + return new ArcanistHardpointTaskResult($map); + } + +} diff --git a/src/workflow/ArcanistInspectWorkflow.php b/src/workflow/ArcanistInspectWorkflow.php new file mode 100644 index 00000000..33add3d7 --- /dev/null +++ b/src/workflow/ArcanistInspectWorkflow.php @@ -0,0 +1,207 @@ +newWorkflowInformation() + ->setSynopsis(pht('Show internal object information.')) + ->addExample(pht('**inspect** [__options__] -- __object__')) + ->setHelp($help); + } + + public function getWorkflowArguments() { + return array( + $this->newWorkflowArgument('all') + ->setHelp(pht('Load all object hardpoints.')), + $this->newWorkflowArgument('objects') + ->setWildcard(true), + ); + } + + public function runWorkflow() { + $is_all = $this->getArgument('all'); + $objects = $this->getArgument('objects'); + + $inspectors = ArcanistRefInspector::getAllInspectors(); + + if (!$objects) { + echo tsprintf( + "%s\n\n", + pht('Choose an object to inspect:')); + + foreach ($inspectors as $inspector) { + echo tsprintf( + " - %s\n", + $inspector->getInspectFunctionName()); + } + + echo tsprintf("\n"); + + return 0; + } + + $all_refs = array(); + $ref_lists = array(); + foreach ($objects as $description) { + $matches = null; + if (!preg_match('/^(\w+)(?:\(([^)]+)\))?\z/', $description, $matches)) { + throw new PhutilArgumentUsageException( + pht( + 'Object specification "%s" is unknown, expected a specification '. + 'like "commit(HEAD)".')); + } + + $function = $matches[1]; + + if (!isset($inspectors[$function])) { + ksort($inspectors); + throw new PhutilArgumentUsageException( + pht( + 'Unknown object type "%s", supported types are: %s.', + $function, + implode(', ', array_keys($inspectors)))); + } + + $inspector = $inspectors[$function]; + + if (isset($matches[2])) { + $arguments = array($matches[2]); + } else { + $arguments = array(); + } + + $ref = $inspector->newInspectRef($arguments); + + $ref_lists[get_class($ref)][] = $ref; + $all_refs[] = $ref; + } + + if ($is_all) { + foreach ($ref_lists as $ref_class => $refs) { + $ref = head($refs); + + $hardpoint_list = $ref->getHardpointList(); + $hardpoints = $hardpoint_list->getHardpoints(); + + if ($hardpoints) { + $hardpoint_keys = mpull($hardpoints, 'getHardpointKey'); + + $this->loadHardpoints( + $refs, + $hardpoint_keys); + } + } + } + + $list = array(); + foreach ($all_refs as $ref) { + $out = $this->describeRef($ref, 0); + $list[] = implode('', $out); + } + $list = implode("\n", $list); + + echo tsprintf('%B', $list); + + return 0; + } + + private function describeRef(ArcanistRefPro $ref, $depth) { + $indent = str_repeat(' ', $depth); + + $out = array(); + $out[] = tsprintf( + "%s+ [%s] %s\n", + $indent, + get_class($ref), + $ref->getRefDisplayName()); + + $hardpoint_list = $ref->getHardpointList(); + foreach ($hardpoint_list->getHardpoints() as $hardpoint) { + $lines = $this->describeHardpoint($ref, $hardpoint, $depth + 1); + foreach ($lines as $line) { + $out[] = $line; + } + } + + return $out; + } + + private function describeHardpoint( + ArcanistRefPro $ref, + ArcanistHardpoint $hardpoint, + $depth) { + $indent = str_repeat(' ', $depth); + + $children = array(); + $values = array(); + + $hardpoint_key = $hardpoint->getHardpointKey(); + if ($ref->hasAttachedHardpoint($hardpoint_key)) { + $mode = '*'; + $value = $ref->getHardpoint($hardpoint_key); + if ($value instanceof ArcanistRefPro) { + $children[] = $value; + } else { + $values[] = $value; + } + } else { + $mode = 'o'; + } + + $out = array(); + $out[] = tsprintf( + "%s%s [%s] %s\n", + $indent, + $mode, + get_class($hardpoint), + $hardpoint->getHardpointKey()); + + foreach ($children as $child) { + $lines = $this->describeRef($child, $depth + 1); + foreach ($lines as $line) { + $out[] = $line; + } + } + + foreach ($values as $value) { + $lines = $this->describeValue($value, $depth + 1); + foreach ($lines as $line) { + $out[] = $line; + } + } + + return $out; + } + + private function describeValue($value, $depth) { + $indent = str_repeat(' ', $depth); + + if (is_string($value)) { + $display_value = '"'.addcslashes(substr($value, 0, 64), "\n\r\t\\\"").'"'; + } else if (is_scalar($value)) { + $display_value = phutil_string_cast($value); + } else if ($value === null) { + $display_value = 'null'; + } else { + $display_value = phutil_describe_type($value); + } + + $out = array(); + $out[] = tsprintf( + "%s> %s\n", + $indent, + $display_value); + return $out; + } + +} diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php index 85d60605..fd6fa20f 100644 --- a/src/workflow/ArcanistWorkflow.php +++ b/src/workflow/ArcanistWorkflow.php @@ -76,6 +76,8 @@ abstract class ArcanistWorkflow extends Phobject { private $configurationEngine; private $configurationSourceList; + private $hardpointEngine; + final public function setToolset(ArcanistToolset $toolset) { $this->toolset = $toolset; return $this; @@ -2329,4 +2331,40 @@ abstract class ArcanistWorkflow extends Phobject { ->setExecutableFuture($future); } + final protected function loadHardpoints( + array $objects, + array $requests) { + + $engine = $this->getHardpointEngine(); + + $requests = $engine->requestHardpoints( + $objects, + $requests); + + // TODO: Wait for only the required requests. + $engine->waitForRequests(array()); + } + + private function getHardpointEngine() { + if ($this->hardpointEngine === null) { + $this->hardpointEngine = $this->newHardpointEngine(); + } + return $this->hardpointEngine; + } + + private function newHardpointEngine() { + $engine = new ArcanistHardpointEngine(); + + $queries = ArcanistWorkflowHardpointQuery::getAllQueries(); + + foreach ($queries as $key => $query) { + $queries[$key] = id(clone $query) + ->setWorkflow($this); + } + + $engine->setQueries($queries); + + return $engine; + } + } diff --git a/src/workingcopy/ArcanistFilesystemWorkingCopy.php b/src/workingcopy/ArcanistFilesystemWorkingCopy.php index 364c9cf5..9aff0376 100644 --- a/src/workingcopy/ArcanistFilesystemWorkingCopy.php +++ b/src/workingcopy/ArcanistFilesystemWorkingCopy.php @@ -14,7 +14,7 @@ final class ArcanistFilesystemWorkingCopy } protected function newRepositoryAPI() { - return new ArcanistFilesystemAPI(); + return new ArcanistFilesystemAPI($this->getPath()); } public function getProjectConfigurationFilePath() { diff --git a/src/workingcopy/ArcanistWorkingCopy.php b/src/workingcopy/ArcanistWorkingCopy.php index f0687f6e..9473d8e6 100644 --- a/src/workingcopy/ArcanistWorkingCopy.php +++ b/src/workingcopy/ArcanistWorkingCopy.php @@ -57,7 +57,7 @@ abstract class ArcanistWorkingCopy $working_copy = new ArcanistFilesystemWorkingCopy(); - self::configureWorkingCopy($working_copy, $ancestor_path, $path); + self::configureWorkingCopy($working_copy, $path, $path); return $working_copy; }