diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1401,7 +1401,7 @@ 'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistUnsafeDynamicStringXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistUpgradeWorkflow' => 'ArcanistArcWorkflow', - 'ArcanistUploadWorkflow' => 'ArcanistWorkflow', + 'ArcanistUploadWorkflow' => 'ArcanistArcWorkflow', 'ArcanistUsageException' => 'Exception', 'ArcanistUseStatementNamespacePrefixXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistUseStatementNamespacePrefixXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', diff --git a/src/conduit/ArcanistConduitEngine.php b/src/conduit/ArcanistConduitEngine.php --- a/src/conduit/ArcanistConduitEngine.php +++ b/src/conduit/ArcanistConduitEngine.php @@ -7,8 +7,6 @@ private $conduitToken; private $conduitTimeout; - private $basicAuthUser; - private $basicAuthPass; private $client; private $callKey = 0; @@ -46,24 +44,6 @@ 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(); @@ -102,12 +82,6 @@ $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()); @@ -148,10 +122,10 @@ 'Run in a working copy with "phabricator.uri" set in ".arcconfig".')) ->addItem( pht( - 'Set a default URI with `arc set-config default `.')) + 'Set a default URI with `arc set-config phabricator.uri `.')) ->addItem( pht( - 'Specify a URI explicitly with `--conduit-uri=`.')); + 'Specify a URI explicitly with `--config phabricator.uri=`.')); $block = id(new PhutilConsoleBlock()) ->addParagraph( diff --git a/src/runtime/ArcanistRuntime.php b/src/runtime/ArcanistRuntime.php --- a/src/runtime/ArcanistRuntime.php +++ b/src/runtime/ArcanistRuntime.php @@ -107,6 +107,8 @@ $workflows = $this->newWorkflows($toolset); $this->workflows = $workflows; + $conduit_engine = $this->newConduitEngine($config); + $phutil_workflows = array(); foreach ($workflows as $key => $workflow) { $phutil_workflows[$key] = $workflow->newPhutilWorkflow(); @@ -114,7 +116,8 @@ $workflow ->setRuntime($this) ->setConfigurationEngine($config_engine) - ->setConfigurationSourceList($config); + ->setConfigurationSourceList($config) + ->setConduitEngine($conduit_engine); } @@ -673,4 +676,49 @@ return $this->stack; } + private function newConduitEngine(ArcanistConfigurationSourceList $config) { + + $conduit_uri = $config->getConfig('phabricator.uri'); + if ($conduit_uri === null) { + $conduit_uri = $config->getConfig('default'); + } + + if ($conduit_uri) { + // Set the URI path to '/api/'. TODO: Originally, I contemplated letting + // you deploy Phabricator somewhere other than the domain root, but ended + // up never pursuing that. We should get rid of all "/api/" silliness + // in things users are expected to configure. This is already happening + // to some degree, e.g. "arc install-certificate" does it for you. + $conduit_uri = new PhutilURI($conduit_uri); + $conduit_uri->setPath('/api/'); + $conduit_uri = phutil_string_cast($conduit_uri); + } + + $engine = new ArcanistConduitEngine(); + + if ($conduit_uri !== null) { + $engine->setConduitURI($conduit_uri); + } + + // TODO: This isn't using "getConfig()" because we aren't defining a + // real config entry for the moment. + + $hosts = array(); + + $hosts_list = $config->getStorageValueList('hosts'); + foreach ($hosts_list as $hosts_config) { + $hosts += $hosts_config->getValue(); + } + + $host_config = idx($hosts, $conduit_uri, array()); + $user_name = idx($host_config, 'user'); + $conduit_token = idx($host_config, 'token'); + + if ($conduit_token !== null) { + $engine->setConduitToken($conduit_token); + } + + return $engine; + } + } diff --git a/src/upload/ArcanistFileUploader.php b/src/upload/ArcanistFileUploader.php --- a/src/upload/ArcanistFileUploader.php +++ b/src/upload/ArcanistFileUploader.php @@ -25,22 +25,14 @@ */ final class ArcanistFileUploader extends Phobject { - private $conduit; + private $conduitEngine; private $files = array(); /* -( Configuring the Uploader )------------------------------------------- */ - - /** - * Provide a Conduit client to choose which server to upload files to. - * - * @param ConduitClient Configured client. - * @return this - * @task config - */ - public function setConduitClient(ConduitClient $conduit) { - $this->conduit = $conduit; + public function setConduitEngine(ArcanistConduitEngine $engine) { + $this->conduitEngine = $engine; return $this; } @@ -99,8 +91,8 @@ * @task upload */ public function uploadFiles() { - if (!$this->conduit) { - throw new PhutilInvalidStateException('setConduitClient'); + if (!$this->conduitEngine) { + throw new PhutilInvalidStateException('setConduitEngine'); } $files = $this->files; @@ -113,7 +105,7 @@ } } - $conduit = $this->conduit; + $conduit = $this->conduitEngine; $futures = array(); foreach ($files as $key => $file) { $params = $this->getUploadParameters($file) + array( @@ -126,7 +118,11 @@ $params['deleteAfterEpoch'] = $delete_after; } - $futures[$key] = $conduit->callMethod('file.allocate', $params); + // TOOLSETS: This should be a real future, but ConduitEngine and + // ConduitCall are currently built oddly and return pretend futures. + + $futures[$key] = new ImmediateFuture( + $conduit->resolveCall('file.allocate', $params)); } $iterator = id(new FutureIterator($futures))->limit(4); @@ -219,9 +215,9 @@ * @task internal */ private function uploadChunks(ArcanistFileDataRef $file, $file_phid) { - $conduit = $this->conduit; + $conduit = $this->conduitEngine; - $chunks = $conduit->callMethodSynchronous( + $chunks = $conduit->resolveCall( 'file.querychunks', array( 'filePHID' => $file_phid, @@ -262,7 +258,7 @@ foreach ($remaining as $chunk) { $data = $file->readBytes($chunk['byteStart'], $chunk['byteEnd']); - $conduit->callMethodSynchronous( + $conduit->resolveCall( 'file.uploadchunk', array( 'filePHID' => $file_phid, @@ -282,11 +278,11 @@ * @task internal */ private function uploadData(ArcanistFileDataRef $file) { - $conduit = $this->conduit; + $conduit = $this->conduitEngine; $data = $file->readBytes(0, $file->getByteSize()); - return $conduit->callMethodSynchronous( + return $conduit->resolveCall( 'file.upload', $this->getUploadParameters($file) + array( 'data_base64' => base64_encode($data), diff --git a/src/workflow/ArcanistUploadWorkflow.php b/src/workflow/ArcanistUploadWorkflow.php --- a/src/workflow/ArcanistUploadWorkflow.php +++ b/src/workflow/ArcanistUploadWorkflow.php @@ -1,70 +1,56 @@ newWorkflowInformation() + ->setSynopsis(pht('Upload files.')) + ->addExample(pht('**upload** [__options__] -- __file__ [__file__ ...]')) + ->setHelp($help); } - public function getArguments() { + public function getWorkflowArguments() { return array( - 'json' => array( - 'help' => pht('Output upload information in JSON format.'), - ), - 'temporary' => array( - 'help' => pht( - 'Mark the file as temporary. Temporary files will be deleted '. - 'automatically after 24 hours.'), - ), - '*' => 'paths', + $this->newWorkflowArgument('json') + ->setHelp(pht('Output upload information in JSON format.')), + $this->newWorkflowArgument('temporary') + ->setHelp( + pht( + 'Mark the file as temporary. Temporary files will be '. + 'deleted after 24 hours.')), + $this->newWorkflowArgument('paths') + ->setWildcard(true) + ->setIsPathArgument(true), ); } - protected function didParseArguments() { + public function runWorkflow() { if (!$this->getArgument('paths')) { - throw new ArcanistUsageException( - pht('Specify one or more files to upload.')); + throw new PhutilArgumentUsageException( + pht('Specify one or more paths to files you want to upload.')); } - $this->paths = $this->getArgument('paths'); - $this->json = $this->getArgument('json'); - } - - public function requiresAuthentication() { - return true; - } - - public function run() { $is_temporary = $this->getArgument('temporary'); + $is_json = $this->getArgument('json'); + $paths = $this->getArgument('paths'); - $conduit = $this->getConduit(); + $conduit = $this->getConduitEngine(); $results = array(); $uploader = id(new ArcanistFileUploader()) - ->setConduitClient($conduit); + ->setConduitEngine($conduit); - foreach ($this->paths as $key => $path) { + foreach ($paths as $key => $path) { $file = id(new ArcanistFileDataRef()) ->setName(basename($path)) ->setPath($path); @@ -89,7 +75,7 @@ $phid = $file->getPHID(); $name = $file->getName(); - $info = $conduit->callMethodSynchronous( + $info = $conduit->resolveCall( 'file.info', array( 'phid' => $phid, @@ -97,14 +83,15 @@ $results[$path] = $info; - if (!$this->json) { + if (!$is_json) { $id = $info['id']; echo " F{$id} {$name}: ".$info['uri']."\n\n"; } } - if ($this->json) { - echo json_encode($results)."\n"; + if ($is_json) { + $output = id(new PhutilJSON())->encodeFormatted($results); + echo $output; } else { $this->writeStatus(pht('Done.')); } @@ -125,7 +112,7 @@ } $this->writeStatus(pht('Beginning chunked upload of large file...')); - $chunks = $conduit->callMethodSynchronous( + $chunks = $conduit->resolveCall( 'file.querychunks', array( 'filePHID' => $file_phid, @@ -182,7 +169,7 @@ 'fread()')); } - $conduit->callMethodSynchronous( + $conduit->resolveCall( 'file.uploadchunk', array( 'filePHID' => $file_phid,