mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 06:42:41 +01:00
Update "arc upload" for Toolsets
Summary: Ref T13490. This largely makes "arc upload" work, although the Future stuff is still a bit wonky. See T11968. Test Plan: Ran "arc upload README.md", got an upload. Maniphest Tasks: T13490 Differential Revision: https://secure.phabricator.com/D21030
This commit is contained in:
parent
9bd5c23b2a
commit
1b97f8b408
5 changed files with 105 additions and 100 deletions
|
@ -1401,7 +1401,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistUnsafeDynamicStringXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistUnsafeDynamicStringXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistUpgradeWorkflow' => 'ArcanistArcWorkflow',
|
'ArcanistUpgradeWorkflow' => 'ArcanistArcWorkflow',
|
||||||
'ArcanistUploadWorkflow' => 'ArcanistWorkflow',
|
'ArcanistUploadWorkflow' => 'ArcanistArcWorkflow',
|
||||||
'ArcanistUsageException' => 'Exception',
|
'ArcanistUsageException' => 'Exception',
|
||||||
'ArcanistUseStatementNamespacePrefixXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistUseStatementNamespacePrefixXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistUseStatementNamespacePrefixXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistUseStatementNamespacePrefixXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
|
|
|
@ -7,8 +7,6 @@ final class ArcanistConduitEngine
|
||||||
private $conduitToken;
|
private $conduitToken;
|
||||||
|
|
||||||
private $conduitTimeout;
|
private $conduitTimeout;
|
||||||
private $basicAuthUser;
|
|
||||||
private $basicAuthPass;
|
|
||||||
|
|
||||||
private $client;
|
private $client;
|
||||||
private $callKey = 0;
|
private $callKey = 0;
|
||||||
|
@ -46,24 +44,6 @@ final class ArcanistConduitEngine
|
||||||
return $this->conduitTimeout;
|
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();
|
||||||
|
@ -102,12 +82,6 @@ final class ArcanistConduitEngine
|
||||||
$client->setTimeout($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());
|
||||||
|
@ -148,10 +122,10 @@ final class ArcanistConduitEngine
|
||||||
'Run in a working copy with "phabricator.uri" set in ".arcconfig".'))
|
'Run in a working copy with "phabricator.uri" set in ".arcconfig".'))
|
||||||
->addItem(
|
->addItem(
|
||||||
pht(
|
pht(
|
||||||
'Set a default URI with `arc set-config default <uri>`.'))
|
'Set a default URI with `arc set-config phabricator.uri <uri>`.'))
|
||||||
->addItem(
|
->addItem(
|
||||||
pht(
|
pht(
|
||||||
'Specify a URI explicitly with `--conduit-uri=<uri>`.'));
|
'Specify a URI explicitly with `--config phabricator.uri=<uri>`.'));
|
||||||
|
|
||||||
$block = id(new PhutilConsoleBlock())
|
$block = id(new PhutilConsoleBlock())
|
||||||
->addParagraph(
|
->addParagraph(
|
||||||
|
|
|
@ -107,6 +107,8 @@ final class ArcanistRuntime {
|
||||||
$workflows = $this->newWorkflows($toolset);
|
$workflows = $this->newWorkflows($toolset);
|
||||||
$this->workflows = $workflows;
|
$this->workflows = $workflows;
|
||||||
|
|
||||||
|
$conduit_engine = $this->newConduitEngine($config);
|
||||||
|
|
||||||
$phutil_workflows = array();
|
$phutil_workflows = array();
|
||||||
foreach ($workflows as $key => $workflow) {
|
foreach ($workflows as $key => $workflow) {
|
||||||
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
|
$phutil_workflows[$key] = $workflow->newPhutilWorkflow();
|
||||||
|
@ -114,7 +116,8 @@ final class ArcanistRuntime {
|
||||||
$workflow
|
$workflow
|
||||||
->setRuntime($this)
|
->setRuntime($this)
|
||||||
->setConfigurationEngine($config_engine)
|
->setConfigurationEngine($config_engine)
|
||||||
->setConfigurationSourceList($config);
|
->setConfigurationSourceList($config)
|
||||||
|
->setConduitEngine($conduit_engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -673,4 +676,49 @@ final class ArcanistRuntime {
|
||||||
return $this->stack;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,22 +25,14 @@
|
||||||
*/
|
*/
|
||||||
final class ArcanistFileUploader extends Phobject {
|
final class ArcanistFileUploader extends Phobject {
|
||||||
|
|
||||||
private $conduit;
|
private $conduitEngine;
|
||||||
private $files = array();
|
private $files = array();
|
||||||
|
|
||||||
|
|
||||||
/* -( Configuring the Uploader )------------------------------------------- */
|
/* -( Configuring the Uploader )------------------------------------------- */
|
||||||
|
|
||||||
|
public function setConduitEngine(ArcanistConduitEngine $engine) {
|
||||||
/**
|
$this->conduitEngine = $engine;
|
||||||
* 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;
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,8 +91,8 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
* @task upload
|
* @task upload
|
||||||
*/
|
*/
|
||||||
public function uploadFiles() {
|
public function uploadFiles() {
|
||||||
if (!$this->conduit) {
|
if (!$this->conduitEngine) {
|
||||||
throw new PhutilInvalidStateException('setConduitClient');
|
throw new PhutilInvalidStateException('setConduitEngine');
|
||||||
}
|
}
|
||||||
|
|
||||||
$files = $this->files;
|
$files = $this->files;
|
||||||
|
@ -113,7 +105,7 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$conduit = $this->conduit;
|
$conduit = $this->conduitEngine;
|
||||||
$futures = array();
|
$futures = array();
|
||||||
foreach ($files as $key => $file) {
|
foreach ($files as $key => $file) {
|
||||||
$params = $this->getUploadParameters($file) + array(
|
$params = $this->getUploadParameters($file) + array(
|
||||||
|
@ -126,7 +118,11 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
$params['deleteAfterEpoch'] = $delete_after;
|
$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);
|
$iterator = id(new FutureIterator($futures))->limit(4);
|
||||||
|
@ -219,9 +215,9 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
private function uploadChunks(ArcanistFileDataRef $file, $file_phid) {
|
private function uploadChunks(ArcanistFileDataRef $file, $file_phid) {
|
||||||
$conduit = $this->conduit;
|
$conduit = $this->conduitEngine;
|
||||||
|
|
||||||
$chunks = $conduit->callMethodSynchronous(
|
$chunks = $conduit->resolveCall(
|
||||||
'file.querychunks',
|
'file.querychunks',
|
||||||
array(
|
array(
|
||||||
'filePHID' => $file_phid,
|
'filePHID' => $file_phid,
|
||||||
|
@ -262,7 +258,7 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
foreach ($remaining as $chunk) {
|
foreach ($remaining as $chunk) {
|
||||||
$data = $file->readBytes($chunk['byteStart'], $chunk['byteEnd']);
|
$data = $file->readBytes($chunk['byteStart'], $chunk['byteEnd']);
|
||||||
|
|
||||||
$conduit->callMethodSynchronous(
|
$conduit->resolveCall(
|
||||||
'file.uploadchunk',
|
'file.uploadchunk',
|
||||||
array(
|
array(
|
||||||
'filePHID' => $file_phid,
|
'filePHID' => $file_phid,
|
||||||
|
@ -282,11 +278,11 @@ final class ArcanistFileUploader extends Phobject {
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
private function uploadData(ArcanistFileDataRef $file) {
|
private function uploadData(ArcanistFileDataRef $file) {
|
||||||
$conduit = $this->conduit;
|
$conduit = $this->conduitEngine;
|
||||||
|
|
||||||
$data = $file->readBytes(0, $file->getByteSize());
|
$data = $file->readBytes(0, $file->getByteSize());
|
||||||
|
|
||||||
return $conduit->callMethodSynchronous(
|
return $conduit->resolveCall(
|
||||||
'file.upload',
|
'file.upload',
|
||||||
$this->getUploadParameters($file) + array(
|
$this->getUploadParameters($file) + array(
|
||||||
'data_base64' => base64_encode($data),
|
'data_base64' => base64_encode($data),
|
||||||
|
|
|
@ -1,70 +1,56 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
final class ArcanistUploadWorkflow
|
||||||
* Upload a file to Phabricator.
|
extends ArcanistArcWorkflow {
|
||||||
*/
|
|
||||||
final class ArcanistUploadWorkflow extends ArcanistWorkflow {
|
|
||||||
|
|
||||||
private $paths;
|
|
||||||
private $json;
|
|
||||||
|
|
||||||
public function getWorkflowName() {
|
public function getWorkflowName() {
|
||||||
return 'upload';
|
return 'upload';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCommandSynopses() {
|
public function getWorkflowInformation() {
|
||||||
return phutil_console_format(<<<EOTEXT
|
$help = pht(<<<EOTEXT
|
||||||
**upload** __file__ [__file__ ...] [--json]
|
Upload one or more files from local disk to Phabricator.
|
||||||
EOTEXT
|
EOTEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return $this->newWorkflowInformation()
|
||||||
|
->setSynopsis(pht('Upload files.'))
|
||||||
|
->addExample(pht('**upload** [__options__] -- __file__ [__file__ ...]'))
|
||||||
|
->setHelp($help);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCommandHelp() {
|
public function getWorkflowArguments() {
|
||||||
return phutil_console_format(<<<EOTEXT
|
|
||||||
Supports: filesystems
|
|
||||||
Upload a file from local disk.
|
|
||||||
EOTEXT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArguments() {
|
|
||||||
return array(
|
return array(
|
||||||
'json' => array(
|
$this->newWorkflowArgument('json')
|
||||||
'help' => pht('Output upload information in JSON format.'),
|
->setHelp(pht('Output upload information in JSON format.')),
|
||||||
),
|
$this->newWorkflowArgument('temporary')
|
||||||
'temporary' => array(
|
->setHelp(
|
||||||
'help' => pht(
|
pht(
|
||||||
'Mark the file as temporary. Temporary files will be deleted '.
|
'Mark the file as temporary. Temporary files will be '.
|
||||||
'automatically after 24 hours.'),
|
'deleted after 24 hours.')),
|
||||||
),
|
$this->newWorkflowArgument('paths')
|
||||||
'*' => 'paths',
|
->setWildcard(true)
|
||||||
|
->setIsPathArgument(true),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didParseArguments() {
|
public function runWorkflow() {
|
||||||
if (!$this->getArgument('paths')) {
|
if (!$this->getArgument('paths')) {
|
||||||
throw new ArcanistUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht('Specify one or more files to upload.'));
|
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_temporary = $this->getArgument('temporary');
|
||||||
|
$is_json = $this->getArgument('json');
|
||||||
|
$paths = $this->getArgument('paths');
|
||||||
|
|
||||||
$conduit = $this->getConduit();
|
$conduit = $this->getConduitEngine();
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
$uploader = id(new ArcanistFileUploader())
|
$uploader = id(new ArcanistFileUploader())
|
||||||
->setConduitClient($conduit);
|
->setConduitEngine($conduit);
|
||||||
|
|
||||||
foreach ($this->paths as $key => $path) {
|
foreach ($paths as $key => $path) {
|
||||||
$file = id(new ArcanistFileDataRef())
|
$file = id(new ArcanistFileDataRef())
|
||||||
->setName(basename($path))
|
->setName(basename($path))
|
||||||
->setPath($path);
|
->setPath($path);
|
||||||
|
@ -89,7 +75,7 @@ EOTEXT
|
||||||
$phid = $file->getPHID();
|
$phid = $file->getPHID();
|
||||||
$name = $file->getName();
|
$name = $file->getName();
|
||||||
|
|
||||||
$info = $conduit->callMethodSynchronous(
|
$info = $conduit->resolveCall(
|
||||||
'file.info',
|
'file.info',
|
||||||
array(
|
array(
|
||||||
'phid' => $phid,
|
'phid' => $phid,
|
||||||
|
@ -97,14 +83,15 @@ EOTEXT
|
||||||
|
|
||||||
$results[$path] = $info;
|
$results[$path] = $info;
|
||||||
|
|
||||||
if (!$this->json) {
|
if (!$is_json) {
|
||||||
$id = $info['id'];
|
$id = $info['id'];
|
||||||
echo " F{$id} {$name}: ".$info['uri']."\n\n";
|
echo " F{$id} {$name}: ".$info['uri']."\n\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->json) {
|
if ($is_json) {
|
||||||
echo json_encode($results)."\n";
|
$output = id(new PhutilJSON())->encodeFormatted($results);
|
||||||
|
echo $output;
|
||||||
} else {
|
} else {
|
||||||
$this->writeStatus(pht('Done.'));
|
$this->writeStatus(pht('Done.'));
|
||||||
}
|
}
|
||||||
|
@ -125,7 +112,7 @@ EOTEXT
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->writeStatus(pht('Beginning chunked upload of large file...'));
|
$this->writeStatus(pht('Beginning chunked upload of large file...'));
|
||||||
$chunks = $conduit->callMethodSynchronous(
|
$chunks = $conduit->resolveCall(
|
||||||
'file.querychunks',
|
'file.querychunks',
|
||||||
array(
|
array(
|
||||||
'filePHID' => $file_phid,
|
'filePHID' => $file_phid,
|
||||||
|
@ -182,7 +169,7 @@ EOTEXT
|
||||||
'fread()'));
|
'fread()'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$conduit->callMethodSynchronous(
|
$conduit->resolveCall(
|
||||||
'file.uploadchunk',
|
'file.uploadchunk',
|
||||||
array(
|
array(
|
||||||
'filePHID' => $file_phid,
|
'filePHID' => $file_phid,
|
||||||
|
|
Loading…
Reference in a new issue