1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-29 18:22:41 +01:00

[Wilds] Make "arc call-conduit ..." call Conduit methods

Summary:
Ref T13098. This repairs Conduit integration. Conduit was meaningfully updated in the `experimental` branch so a lot of this is just deleting code I don't plan to support going forward.

This removes "conduit.timeout", "http.basicauth.user" and "http.basicauth.pass". I believe these were all crazy niche calls with essentially no legitimate use. We could provide extension support if anyone actually uses this stuff, now.

Fixes an old "phutil" reference in HTTPSFuture.

Builds Conduit engine support into `ArcanistWorkflow`. There's perhaps some argument for trying to not make this core, but every upstream thing we'll ever write probably wants it (`arc`, `phage`) and there's not much of a cost to making it core. Even non-core stuff may include extensions which expect Conduit support (for example, for reporting workflow metrics to Phabricator).

There's no authentication support yet, I'm planning to update "hosts" config handling next.

Test Plan:
```
$ echo '{}' | arc call-conduit conduit.ping
 WARNING  Ignoring unrecognized configuration option ("hosts") from source: User Config File (/Users/epriestley/.arcrc).
 WARNING  Ignoring unrecognized configuration option ("load") from source: Project Config File (/Users/epriestley/dev/core/.arcconfig).
{
  "error": null,
  "errorMessage": null,
  "response": "secure001.phacility.net"
}
```

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13098

Differential Revision: https://secure.phabricator.com/D19698
This commit is contained in:
epriestley 2018-09-20 14:35:19 -07:00
parent 5e70719306
commit e6c37bd4b3
10 changed files with 92 additions and 110 deletions

View file

@ -139,6 +139,7 @@ phutil_register_library_map(array(
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConcatenationOperatorXHPASTLinterRuleTestCase.php',
'ArcanistConduitCall' => 'conduit/ArcanistConduitCall.php',
'ArcanistConduitEngine' => 'conduit/ArcanistConduitEngine.php',
'ArcanistConduitException' => 'conduit/ArcanistConduitException.php',
'ArcanistConfigOption' => 'config/option/ArcanistConfigOption.php',
'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php',
'ArcanistConfigurationDrivenUnitTestEngine' => 'unit/engine/ArcanistConfigurationDrivenUnitTestEngine.php',
@ -341,6 +342,7 @@ phutil_register_library_map(array(
'ArcanistNoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistNoLintLinterTestCase.php',
'ArcanistNoParentScopeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNoParentScopeXHPASTLinterRule.php',
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNoParentScopeXHPASTLinterRuleTestCase.php',
'ArcanistNoURIConduitException' => 'conduit/ArcanistNoURIConduitException.php',
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php',
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase.php',
@ -1163,7 +1165,7 @@ phutil_register_library_map(array(
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
'ArcanistArcConfigurationEngineExtension' => 'ArcanistConfigurationEngineExtension',
'ArcanistArcToolset' => 'ArcanistToolset',
'ArcanistArcWorkflow' => 'ArcanistWorkflow',
'ArcanistArcWorkflow' => 'Phobject',
'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArrayCombineXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@ -1242,6 +1244,7 @@ phutil_register_library_map(array(
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistConduitCall' => 'Phobject',
'ArcanistConduitEngine' => 'Phobject',
'ArcanistConduitException' => 'Exception',
'ArcanistConfigOption' => 'Phobject',
'ArcanistConfigurationDrivenLintEngine' => 'ArcanistLintEngine',
'ArcanistConfigurationDrivenUnitTestEngine' => 'ArcanistUnitTestEngine',
@ -1444,6 +1447,7 @@ phutil_register_library_map(array(
'ArcanistNoLintLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistNoParentScopeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNoURIConduitException' => 'ArcanistConduitException',
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',

View file

@ -6,10 +6,6 @@ final class ArcanistConduitEngine
private $conduitURI;
private $conduitToken;
private $conduitTimeout;
private $basicAuthUser;
private $basicAuthPass;
private $client;
private $callKey = 0;
private $activeFutures = array();
@ -37,33 +33,6 @@ final class ArcanistConduitEngine
return $this->conduitToken;
}
public function setConduitTimeout($conduit_timeout) {
$this->conduitTimeout = $conduit_timeout;
return $this;
}
public function getConduitTimeout() {
return $this->conduitTimeout;
}
public function setBasicAuthUser($basic_auth_user) {
$this->basicAuthUser = $basic_auth_user;
return $this;
}
public function getBasicAuthUser() {
return $this->basicAuthUser;
}
public function setBasicAuthPass($basic_auth_pass) {
$this->basicAuthPass = $basic_auth_pass;
return $this;
}
public function getBasicAuthPass() {
return $this->basicAuthPass;
}
public function newCall($method, array $parameters) {
if ($this->conduitURI == null) {
$this->raiseURIException();
@ -97,17 +66,6 @@ final class ArcanistConduitEngine
$client = new ConduitClient($conduit_uri);
$timeout = $this->getConduitTimeout();
if ($timeout) {
$client->setTimeout($timeout);
}
$basic_user = $this->getBasicAuthUser();
$basic_pass = $this->getBasicAuthPass();
if ($basic_user !== null || $basic_pass !== null) {
$client->setBasicAuthCredentials($basic_user, $basic_pass);
}
$token = $this->getConduitToken();
if ($token) {
$client->setConduitToken($this->getConduitToken());

View file

@ -0,0 +1,3 @@
<?php
abstract class ArcanistConduitException extends Exception {}

View file

@ -0,0 +1,4 @@
<?php
final class ArcanistNoURIConduitException
extends ArcanistConduitException {}

View file

@ -92,17 +92,6 @@ final class ArcanistArcConfigurationEngineExtension
'help' => pht('Command to use to invoke a web browser.'),
'example' => '"gnome-www-browser"',
),
'http.basicauth.user' => array(
'type' => 'string',
'help' => pht('Username to use for basic auth over HTTP transports.'),
'example' => '"bob"',
),
'http.basicauth.pass' => array(
'type' => 'string',
'help' => pht('Password to use for basic auth over HTTP transports.'),
'example' => '"bobhasasecret"',
),
*/

View file

@ -321,7 +321,8 @@ final class HTTPSFuture extends BaseHTTPFuture {
// work, give up and yell at the user.
if (!$this->getCABundle()) {
$caroot = dirname(phutil_get_library_root('phutil')).'/resources/ssl/';
$arcanist_root = dirname(phutil_get_library_root('arcanist'));
$caroot = $arcanist_root.'/resources/ssl/';
$ini_val = ini_get('curl.cainfo');
if (self::getGlobalCABundle()) {
$this->setCABundleFromPath(self::getGlobalCABundle());

View file

@ -7,6 +7,7 @@ abstract class ArcanistWorkflow extends Phobject {
private $arguments;
private $configurationEngine;
private $configurationSourceList;
private $conduitEngine;
/**
* Return the command used to invoke this workflow from the command like,
@ -48,7 +49,6 @@ abstract class ArcanistWorkflow extends Phobject {
return null;
}
public function newPhutilWorkflow() {
$arguments = $this->getWorkflowArguments();
assert_instances_of($arguments, 'ArcanistWorkflowArgument');
@ -164,4 +164,25 @@ abstract class ArcanistWorkflow extends Phobject {
return new ArcanistWorkflowInformation();
}
final protected function getConduitEngine() {
if (!$this->conduitEngine) {
$conduit_uri = $this->getConfig('phabricator.uri');
if (!strlen($conduit_uri)) {
throw new ArcanistNoURIConduitException(
pht(
'This workflow is trying to connect to the Phabricator API, but '.
'no Phabricator URI is configured. Run "arc help connect" for '.
'guidance.'));
}
$conduit_engine = id(new ArcanistConduitEngine())
->setConduitURI($conduit_uri);
$this->conduitEngine = $conduit_engine;
}
return $this->conduitEngine;
}
}

View file

@ -33,7 +33,7 @@
* @task scratch Scratch Files
* @task phabrep Phabricator Repositories
*/
abstract class ArcanistArcWorkflow extends ArcanistWorkflow {
abstract class ArcanistArcWorkflow extends Phobject {
const COMMIT_DISABLE = 0;
const COMMIT_ALLOW = 1;

View file

@ -9,63 +9,54 @@ final class ArcanistCallConduitWorkflow extends ArcanistWorkflow {
return 'call-conduit';
}
public function getCommandSynopses() {
return phutil_console_format(<<<EOTEXT
**call-conduit** __method__
public function getWorkflowInformation() {
$help = pht(<<<EOTEXT
Make a call to Conduit, the Phabricator API.
For example:
$ echo '{}' | arc call-conduit conduit.ping
EOTEXT
);
);
return $this->newWorkflowInformation()
->addExample(pht('**call-conduit** __method__'))
->setHelp($help);
}
public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT
Supports: http, https
Allows you to make a raw Conduit method call:
- Run this command from a working directory.
- Call parameters are REQUIRED and read as a JSON blob from stdin.
- Results are written to stdout as a JSON blob.
This workflow is primarily useful for writing scripts which integrate
with Phabricator. Examples:
$ echo '{}' | arc call-conduit conduit.ping
$ echo '{"phid":"PHID-FILE-xxxx"}' | arc call-conduit file.download
EOTEXT
);
}
public function getArguments() {
public function getWorkflowArguments() {
return array(
'*' => 'method',
$this->newWorkflowArgument('argv')
->setWildcard(true),
);
}
protected function shouldShellComplete() {
return false;
}
public function runWorkflow() {
$argv = $this->getArgument('argv');
public function requiresConduit() {
return true;
}
public function requiresAuthentication() {
return true;
}
public function run() {
$method = $this->getArgument('method', array());
if (count($method) !== 1) {
throw new ArcanistUsageException(
pht('Provide exactly one Conduit method name.'));
if (!$argv) {
// TOOLSETS: We should call "conduit.query" and list available methods
// here.
throw new PhutilArgumentUsageException(
pht(
'Provide the name of the Conduit method you want to call on the '.
'command line.'));
} else if (count($argv) > 1) {
throw new PhutilArgumentUsageException(
pht(
'Provide the name of only one method to call.'));
}
$method = reset($method);
$console = PhutilConsole::getConsole();
$method = head($argv);
if (!function_exists('posix_isatty') || posix_isatty(STDIN)) {
$console->writeErr(
"%s\n",
pht('Waiting for JSON parameters on stdin...'));
fprintf(
STDERR,
tsprintf(
"%s\n",
pht('Waiting for JSON parameters on stdin...')));
}
$params = @file_get_contents('php://stdin');
try {
$params = phutil_json_decode($params);
@ -77,20 +68,24 @@ EOTEXT
$error = null;
$error_message = null;
try {
$result = $this->getConduit()->callMethodSynchronous(
$method,
$params);
$result = $this->getConduitEngine()->resolveCall($method, $params);
} catch (ConduitClientException $ex) {
// TOOLSETS: We should use "conduit.query" to suggest similar calls if
// it looks like the user called a method which does not exist.
$error = $ex->getErrorCode();
$error_message = $ex->getMessage();
$result = null;
}
echo json_encode(array(
'error' => $error,
'errorMessage' => $error_message,
'response' => $result,
))."\n";
echo id(new PhutilJSON())
->encodeFormatted(
array(
'error' => $error,
'errorMessage' => $error_message,
'response' => $result,
));
return 0;
}

View file

@ -21,6 +21,13 @@ final class ArcanistRuntime {
try {
return $this->executeCore($argv);
} catch (ArcanistConduitException $ex) {
fwrite(
STDERR,
tsprintf(
"**%s:** %s\n",
pht('Conduit Exception'),
$ex->getMessage()));
} catch (PhutilArgumentUsageException $ex) {
fwrite(
STDERR,