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:
parent
5e70719306
commit
e6c37bd4b3
10 changed files with 92 additions and 110 deletions
|
@ -139,6 +139,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConcatenationOperatorXHPASTLinterRuleTestCase.php',
|
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConcatenationOperatorXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistConduitCall' => 'conduit/ArcanistConduitCall.php',
|
'ArcanistConduitCall' => 'conduit/ArcanistConduitCall.php',
|
||||||
'ArcanistConduitEngine' => 'conduit/ArcanistConduitEngine.php',
|
'ArcanistConduitEngine' => 'conduit/ArcanistConduitEngine.php',
|
||||||
|
'ArcanistConduitException' => 'conduit/ArcanistConduitException.php',
|
||||||
'ArcanistConfigOption' => 'config/option/ArcanistConfigOption.php',
|
'ArcanistConfigOption' => 'config/option/ArcanistConfigOption.php',
|
||||||
'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php',
|
'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php',
|
||||||
'ArcanistConfigurationDrivenUnitTestEngine' => 'unit/engine/ArcanistConfigurationDrivenUnitTestEngine.php',
|
'ArcanistConfigurationDrivenUnitTestEngine' => 'unit/engine/ArcanistConfigurationDrivenUnitTestEngine.php',
|
||||||
|
@ -341,6 +342,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistNoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistNoLintLinterTestCase.php',
|
'ArcanistNoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistNoLintLinterTestCase.php',
|
||||||
'ArcanistNoParentScopeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNoParentScopeXHPASTLinterRule.php',
|
'ArcanistNoParentScopeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNoParentScopeXHPASTLinterRule.php',
|
||||||
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNoParentScopeXHPASTLinterRuleTestCase.php',
|
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNoParentScopeXHPASTLinterRuleTestCase.php',
|
||||||
|
'ArcanistNoURIConduitException' => 'conduit/ArcanistNoURIConduitException.php',
|
||||||
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
|
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
|
||||||
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php',
|
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php',
|
||||||
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase.php',
|
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase.php',
|
||||||
|
@ -1163,7 +1165,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
|
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistArcConfigurationEngineExtension' => 'ArcanistConfigurationEngineExtension',
|
'ArcanistArcConfigurationEngineExtension' => 'ArcanistConfigurationEngineExtension',
|
||||||
'ArcanistArcToolset' => 'ArcanistToolset',
|
'ArcanistArcToolset' => 'ArcanistToolset',
|
||||||
'ArcanistArcWorkflow' => 'ArcanistWorkflow',
|
'ArcanistArcWorkflow' => 'Phobject',
|
||||||
'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistArrayCombineXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistArrayCombineXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
@ -1242,6 +1244,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistConcatenationOperatorXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistConduitCall' => 'Phobject',
|
'ArcanistConduitCall' => 'Phobject',
|
||||||
'ArcanistConduitEngine' => 'Phobject',
|
'ArcanistConduitEngine' => 'Phobject',
|
||||||
|
'ArcanistConduitException' => 'Exception',
|
||||||
'ArcanistConfigOption' => 'Phobject',
|
'ArcanistConfigOption' => 'Phobject',
|
||||||
'ArcanistConfigurationDrivenLintEngine' => 'ArcanistLintEngine',
|
'ArcanistConfigurationDrivenLintEngine' => 'ArcanistLintEngine',
|
||||||
'ArcanistConfigurationDrivenUnitTestEngine' => 'ArcanistUnitTestEngine',
|
'ArcanistConfigurationDrivenUnitTestEngine' => 'ArcanistUnitTestEngine',
|
||||||
|
@ -1444,6 +1447,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistNoLintLinterTestCase' => 'ArcanistLinterTestCase',
|
'ArcanistNoLintLinterTestCase' => 'ArcanistLinterTestCase',
|
||||||
'ArcanistNoParentScopeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistNoParentScopeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
|
'ArcanistNoURIConduitException' => 'ArcanistConduitException',
|
||||||
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
|
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
|
||||||
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistObjectOperatorSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
|
|
|
@ -6,10 +6,6 @@ final class ArcanistConduitEngine
|
||||||
private $conduitURI;
|
private $conduitURI;
|
||||||
private $conduitToken;
|
private $conduitToken;
|
||||||
|
|
||||||
private $conduitTimeout;
|
|
||||||
private $basicAuthUser;
|
|
||||||
private $basicAuthPass;
|
|
||||||
|
|
||||||
private $client;
|
private $client;
|
||||||
private $callKey = 0;
|
private $callKey = 0;
|
||||||
private $activeFutures = array();
|
private $activeFutures = array();
|
||||||
|
@ -37,33 +33,6 @@ final class ArcanistConduitEngine
|
||||||
return $this->conduitToken;
|
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) {
|
public function newCall($method, array $parameters) {
|
||||||
if ($this->conduitURI == null) {
|
if ($this->conduitURI == null) {
|
||||||
$this->raiseURIException();
|
$this->raiseURIException();
|
||||||
|
@ -97,17 +66,6 @@ final class ArcanistConduitEngine
|
||||||
|
|
||||||
$client = new ConduitClient($conduit_uri);
|
$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();
|
$token = $this->getConduitToken();
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$client->setConduitToken($this->getConduitToken());
|
$client->setConduitToken($this->getConduitToken());
|
||||||
|
|
3
src/conduit/ArcanistConduitException.php
Normal file
3
src/conduit/ArcanistConduitException.php
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class ArcanistConduitException extends Exception {}
|
4
src/conduit/ArcanistNoURIConduitException.php
Normal file
4
src/conduit/ArcanistNoURIConduitException.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistNoURIConduitException
|
||||||
|
extends ArcanistConduitException {}
|
|
@ -92,17 +92,6 @@ final class ArcanistArcConfigurationEngineExtension
|
||||||
'help' => pht('Command to use to invoke a web browser.'),
|
'help' => pht('Command to use to invoke a web browser.'),
|
||||||
'example' => '"gnome-www-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"',
|
|
||||||
),
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,8 @@ final class HTTPSFuture extends BaseHTTPFuture {
|
||||||
// work, give up and yell at the user.
|
// work, give up and yell at the user.
|
||||||
|
|
||||||
if (!$this->getCABundle()) {
|
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');
|
$ini_val = ini_get('curl.cainfo');
|
||||||
if (self::getGlobalCABundle()) {
|
if (self::getGlobalCABundle()) {
|
||||||
$this->setCABundleFromPath(self::getGlobalCABundle());
|
$this->setCABundleFromPath(self::getGlobalCABundle());
|
||||||
|
|
|
@ -7,6 +7,7 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
private $arguments;
|
private $arguments;
|
||||||
private $configurationEngine;
|
private $configurationEngine;
|
||||||
private $configurationSourceList;
|
private $configurationSourceList;
|
||||||
|
private $conduitEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the command used to invoke this workflow from the command like,
|
* Return the command used to invoke this workflow from the command like,
|
||||||
|
@ -48,7 +49,6 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function newPhutilWorkflow() {
|
public function newPhutilWorkflow() {
|
||||||
$arguments = $this->getWorkflowArguments();
|
$arguments = $this->getWorkflowArguments();
|
||||||
assert_instances_of($arguments, 'ArcanistWorkflowArgument');
|
assert_instances_of($arguments, 'ArcanistWorkflowArgument');
|
||||||
|
@ -164,4 +164,25 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
return new ArcanistWorkflowInformation();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
* @task scratch Scratch Files
|
* @task scratch Scratch Files
|
||||||
* @task phabrep Phabricator Repositories
|
* @task phabrep Phabricator Repositories
|
||||||
*/
|
*/
|
||||||
abstract class ArcanistArcWorkflow extends ArcanistWorkflow {
|
abstract class ArcanistArcWorkflow extends Phobject {
|
||||||
|
|
||||||
const COMMIT_DISABLE = 0;
|
const COMMIT_DISABLE = 0;
|
||||||
const COMMIT_ALLOW = 1;
|
const COMMIT_ALLOW = 1;
|
||||||
|
|
|
@ -9,63 +9,54 @@ final class ArcanistCallConduitWorkflow extends ArcanistWorkflow {
|
||||||
return 'call-conduit';
|
return 'call-conduit';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCommandSynopses() {
|
public function getWorkflowInformation() {
|
||||||
return phutil_console_format(<<<EOTEXT
|
$help = pht(<<<EOTEXT
|
||||||
**call-conduit** __method__
|
Make a call to Conduit, the Phabricator API.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
$ echo '{}' | arc call-conduit conduit.ping
|
||||||
EOTEXT
|
EOTEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return $this->newWorkflowInformation()
|
||||||
|
->addExample(pht('**call-conduit** __method__'))
|
||||||
|
->setHelp($help);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCommandHelp() {
|
public function getWorkflowArguments() {
|
||||||
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() {
|
|
||||||
return array(
|
return array(
|
||||||
'*' => 'method',
|
$this->newWorkflowArgument('argv')
|
||||||
|
->setWildcard(true),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldShellComplete() {
|
public function runWorkflow() {
|
||||||
return false;
|
$argv = $this->getArgument('argv');
|
||||||
}
|
|
||||||
|
|
||||||
public function requiresConduit() {
|
if (!$argv) {
|
||||||
return true;
|
// TOOLSETS: We should call "conduit.query" and list available methods
|
||||||
}
|
// here.
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
public function requiresAuthentication() {
|
pht(
|
||||||
return true;
|
'Provide the name of the Conduit method you want to call on the '.
|
||||||
}
|
'command line.'));
|
||||||
|
} else if (count($argv) > 1) {
|
||||||
public function run() {
|
throw new PhutilArgumentUsageException(
|
||||||
$method = $this->getArgument('method', array());
|
pht(
|
||||||
if (count($method) !== 1) {
|
'Provide the name of only one method to call.'));
|
||||||
throw new ArcanistUsageException(
|
|
||||||
pht('Provide exactly one Conduit method name.'));
|
|
||||||
}
|
}
|
||||||
$method = reset($method);
|
|
||||||
|
|
||||||
$console = PhutilConsole::getConsole();
|
$method = head($argv);
|
||||||
|
|
||||||
if (!function_exists('posix_isatty') || posix_isatty(STDIN)) {
|
if (!function_exists('posix_isatty') || posix_isatty(STDIN)) {
|
||||||
$console->writeErr(
|
fprintf(
|
||||||
"%s\n",
|
STDERR,
|
||||||
pht('Waiting for JSON parameters on stdin...'));
|
tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Waiting for JSON parameters on stdin...')));
|
||||||
}
|
}
|
||||||
|
|
||||||
$params = @file_get_contents('php://stdin');
|
$params = @file_get_contents('php://stdin');
|
||||||
try {
|
try {
|
||||||
$params = phutil_json_decode($params);
|
$params = phutil_json_decode($params);
|
||||||
|
@ -77,20 +68,24 @@ EOTEXT
|
||||||
$error = null;
|
$error = null;
|
||||||
$error_message = null;
|
$error_message = null;
|
||||||
try {
|
try {
|
||||||
$result = $this->getConduit()->callMethodSynchronous(
|
$result = $this->getConduitEngine()->resolveCall($method, $params);
|
||||||
$method,
|
|
||||||
$params);
|
|
||||||
} catch (ConduitClientException $ex) {
|
} 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 = $ex->getErrorCode();
|
||||||
$error_message = $ex->getMessage();
|
$error_message = $ex->getMessage();
|
||||||
$result = null;
|
$result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo json_encode(array(
|
echo id(new PhutilJSON())
|
||||||
'error' => $error,
|
->encodeFormatted(
|
||||||
'errorMessage' => $error_message,
|
array(
|
||||||
'response' => $result,
|
'error' => $error,
|
||||||
))."\n";
|
'errorMessage' => $error_message,
|
||||||
|
'response' => $result,
|
||||||
|
));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,13 @@ final class ArcanistRuntime {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $this->executeCore($argv);
|
return $this->executeCore($argv);
|
||||||
|
} catch (ArcanistConduitException $ex) {
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
tsprintf(
|
||||||
|
"**%s:** %s\n",
|
||||||
|
pht('Conduit Exception'),
|
||||||
|
$ex->getMessage()));
|
||||||
} catch (PhutilArgumentUsageException $ex) {
|
} catch (PhutilArgumentUsageException $ex) {
|
||||||
fwrite(
|
fwrite(
|
||||||
STDERR,
|
STDERR,
|
||||||
|
|
Loading…
Reference in a new issue