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

Port "arc liberate" to Toolsets

Summary: Ref T13395. Depends on D20991. Make "arc liberate" run as a toolset command, not a classic command.

Test Plan: Ran "arc liberate"; created this diff.

Maniphest Tasks: T13395

Differential Revision: https://secure.phabricator.com/D20992
This commit is contained in:
epriestley 2020-02-13 14:38:36 -08:00
parent 0e95fcbb7f
commit cf9469e0d1
4 changed files with 165 additions and 134 deletions

View file

@ -1205,7 +1205,7 @@ phutil_register_library_map(array(
'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistLanguageConstructParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistLesscLinter' => 'ArcanistExternalLinter', 'ArcanistLesscLinter' => 'ArcanistExternalLinter',
'ArcanistLesscLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistLesscLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistLiberateWorkflow' => 'ArcanistWorkflow', 'ArcanistLiberateWorkflow' => 'ArcanistArcWorkflow',
'ArcanistLintEngine' => 'Phobject', 'ArcanistLintEngine' => 'Phobject',
'ArcanistLintMessage' => 'Phobject', 'ArcanistLintMessage' => 'Phobject',
'ArcanistLintMessageTestCase' => 'PhutilTestCase', 'ArcanistLintMessageTestCase' => 'PhutilTestCase',

View file

@ -5,7 +5,7 @@ abstract class ArcanistArcWorkflow
public function supportsToolset(ArcanistToolset $toolset) { public function supportsToolset(ArcanistToolset $toolset) {
$key = $toolset->getToolsetKey(); $key = $toolset->getToolsetKey();
return ($key === 'arc'); return ($key === ArcanistArcToolset::TOOLSETKEY);
} }
} }

View file

@ -1,141 +1,106 @@
<?php <?php
/** final class ArcanistLiberateWorkflow
* Create and update libphutil libraries. extends ArcanistArcWorkflow {
*
* This workflow is unusual and involves re-executing 'arc liberate' as a
* subprocess with `--remap` and `--verify`. This is because there is no way
* to unload or reload a library, so every process is stuck with the library
* definition it had when it first loaded. This is normally fine, but
* problematic in this case because `arc liberate` modifies library definitions.
*/
final class ArcanistLiberateWorkflow extends ArcanistWorkflow {
public function getWorkflowName() { public function getWorkflowName() {
return 'liberate'; return 'liberate';
} }
public function getCommandSynopses() { public function getWorkflowInformation() {
return phutil_console_format(<<<EOTEXT // TOOLSETS: Expand this help.
**liberate** [__path__]
$help = pht(<<<EOTEXT
Create or update an Arcanist library.
EOTEXT EOTEXT
); );
return $this->newWorkflowInformation()
->addExample(pht('**liberate**'))
->addExample(pht('**liberate** [__path__]'))
->setHelp($help);
} }
public function getCommandHelp() { public function getWorkflowArguments() {
return phutil_console_format(<<<EOTEXT
Supports: libphutil
Create or update a libphutil library, generating required metadata
files like \__init__.php.
EOTEXT
);
}
public function getArguments() {
return array( return array(
'all' => array( $this->newWorkflowArgument('clean')
'help' => pht( ->setHelp(
'Drop the module cache before liberating. This will completely '. pht('Perform a clean rebuild, ignoring caches. Thorough, but slow.')),
'reanalyze the entire library. Thorough, but slow!'), $this->newWorkflowArgument('argv')
), ->setWildcard(true),
'force-update' => array(
'help' => pht(
'Force the library map to be updated, even in the presence of '.
'lint errors.'),
),
'library-name' => array(
'param' => 'name',
'help' =>
pht('Use a flag for library name rather than awaiting user input.'),
),
'remap' => array(
'hide' => true,
'help' => pht(
'Internal. Run the remap step of liberation. You do not need to '.
'run this unless you are debugging the workflow.'),
),
'verify' => array(
'hide' => true,
'help' => pht(
'Internal. Run the verify step of liberation. You do not need to '.
'run this unless you are debugging the workflow.'),
),
'upgrade' => array(
'hide' => true,
'help' => pht('Experimental. Upgrade library to v2.'),
),
'*' => 'argv',
); );
} }
public function run() { public function runWorkflow() {
$log = $this->getLogEngine();
$argv = $this->getArgument('argv'); $argv = $this->getArgument('argv');
if (count($argv) > 1) { if (count($argv) > 1) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
pht( pht(
"Provide only one path to '%s'. The path should be a directory ". 'Provide only one path to "arc liberate". The path should identify '.
"where you want to create or update a libphutil library.", 'a directory where you want to create or update a library.'));
'arc liberate')); } else if (!$argv) {
} else if (count($argv) == 0) { $log->writeStatus(
$path = getcwd(); pht('SCAN'),
} else { pht('Searching for libraries in the current working directory...'));
$path = reset($argv);
}
$is_remap = $this->getArgument('remap'); $init_files = id(new FileFinder(getcwd()))
$is_verify = $this->getArgument('verify');
$path = Filesystem::resolvePath($path);
if (Filesystem::pathExists($path) && is_dir($path)) {
$init = id(new FileFinder($path))
->withPath('*/__phutil_library_init__.php') ->withPath('*/__phutil_library_init__.php')
->find(); ->find();
} else {
$init = null;
}
if ($init) { if (!$init_files) {
if (count($init) > 1) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
pht( pht(
'Specified directory contains more than one libphutil library. '. 'Unable to find any libraries under the current working '.
'Use a more specific path.')); 'directory. To create a library, provide a path.'));
}
$paths = array();
foreach ($init_files as $init) {
$paths[] = Filesystem::resolvePath(dirname($init));
} }
$path = Filesystem::resolvePath(dirname(reset($init)), $path);
} else { } else {
$found = false; $paths = array(
foreach (Filesystem::walkToRoot($path) as $dir) { Filesystem::resolvePath(head($argv)),
if (Filesystem::pathExists($dir.'/__phutil_library_init__.php')) { );
$path = $dir; }
$found = true;
break; foreach ($paths as $path) {
} $log->writeStatus(
} pht('WORK'),
if (!$found) { pht(
echo pht("No library currently exists at that path...\n"); 'Updating library: %s',
$this->liberateCreateDirectory($path); Filesystem::readablePath($path).DIRECTORY_SEPARATOR));
$this->liberateCreateLibrary($path); $this->liberatePath($path);
return; }
}
$log->writeSuccess(
pht('DONE'),
pht('Updated %s librarie(s).', phutil_count($paths)));
return 0;
}
private function liberatePath($path) {
if (!Filesystem::pathExists($path.'/__phutil_library_init__.php')) {
echo tsprintf(
"%s\n",
pht(
'No library currently exists at the path "%s"...',
$path));
$this->liberateCreateDirectory($path);
$this->liberateCreateLibrary($path);
return;
} }
$version = $this->getLibraryFormatVersion($path); $version = $this->getLibraryFormatVersion($path);
switch ($version) { switch ($version) {
case 1: case 1:
if ($this->getArgument('upgrade')) {
return $this->upgradeLibrary($path);
}
throw new ArcanistUsageException( throw new ArcanistUsageException(
pht( pht(
"This library is using libphutil v1, which is no ". 'This very old library is no longer supported.'));
"longer supported. Run '%s' to upgrade to v2.",
'arc liberate --upgrade'));
case 2: case 2:
if ($this->getArgument('upgrade')) {
throw new ArcanistUsageException(
pht("Can't upgrade a v2 library!"));
}
return $this->liberateVersion2($path); return $this->liberateVersion2($path);
default: default:
throw new ArcanistUsageException( throw new ArcanistUsageException(
@ -166,32 +131,17 @@ EOTEXT
$bin = $this->getScriptPath('support/lib/rebuild-map.php'); $bin = $this->getScriptPath('support/lib/rebuild-map.php');
$argv = array(); $argv = array();
if ($this->getArgument('all')) { if ($this->getArgument('clean')) {
$argv[] = '--drop-cache'; $argv[] = '--drop-cache';
} }
return phutil_passthru( return phutil_passthru(
'php -f %s -- %Ls %s', 'php -f %R -- %Ls %R',
$bin, $bin,
$argv, $argv,
$path); $path);
} }
private function upgradeLibrary($path) {
$inits = id(new FileFinder($path))
->withPath('*/__init__.php')
->withType('f')
->find();
echo pht('Removing %s files...', '__init__.php')."\n";
foreach ($inits as $init) {
Filesystem::remove($path.'/'.$init);
}
echo pht('Upgrading library to v2...')."\n";
$this->liberateVersion2($path);
}
private function liberateCreateDirectory($path) { private function liberateCreateDirectory($path) {
if (Filesystem::pathExists($path)) { if (Filesystem::pathExists($path)) {
if (!is_dir($path)) { if (!is_dir($path)) {
@ -204,10 +154,10 @@ EOTEXT
echo pht("The directory '%s' does not exist.", $path); echo pht("The directory '%s' does not exist.", $path);
if (!phutil_console_confirm(pht('Do you want to create it?'))) { if (!phutil_console_confirm(pht('Do you want to create it?'))) {
throw new ArcanistUsageException(pht('Canceled.')); throw new ArcanistUsageException(pht('Cancelled.'));
} }
execx('mkdir -p %s', $path); execx('mkdir -p %R', $path);
} }
private function liberateCreateLibrary($path) { private function liberateCreateLibrary($path) {
@ -219,14 +169,10 @@ EOTEXT
echo pht("Creating new libphutil library in '%s'.", $path)."\n"; echo pht("Creating new libphutil library in '%s'.", $path)."\n";
do { do {
$name = $this->getArgument('library-name'); echo pht('Choose a name for the new library.')."\n";
if ($name === null) { $name = phutil_console_prompt(
echo pht('Choose a name for the new library.')."\n"; pht('What do you want to name this library?'));
$name = phutil_console_prompt(
pht('What do you want to name this library?'));
} else {
echo pht('Using library name %s.', $name)."\n";
}
if (preg_match('/^[a-z-]+$/', $name)) { if (preg_match('/^[a-z-]+$/', $name)) {
break; break;
} else { } else {

View file

@ -115,6 +115,86 @@ abstract class ArcanistWorkflow extends Phobject {
return $this->configurationSourceList; return $this->configurationSourceList;
} }
public function newPhutilWorkflow() {
$arguments = $this->getWorkflowArguments();
assert_instances_of($arguments, 'ArcanistWorkflowArgument');
$specs = mpull($arguments, 'getPhutilSpecification');
$phutil_workflow = id(new ArcanistPhutilWorkflow())
->setName($this->getWorkflowName())
->setWorkflow($this)
->setArguments($specs);
$information = $this->getWorkflowInformation();
if ($information) {
$examples = $information->getExamples();
if ($examples) {
$examples = implode("\n", $examples);
$phutil_workflow->setExamples($examples);
}
$help = $information->getHelp();
if (strlen($help)) {
// Unwrap linebreaks in the help text so we don't get weird formatting.
$help = preg_replace("/(?<=\S)\n(?=\S)/", ' ', $help);
$phutil_workflow->setHelp($help);
}
}
return $phutil_workflow;
}
final protected function newWorkflowArgument($key) {
return id(new ArcanistWorkflowArgument())
->setKey($key);
}
final protected function newWorkflowInformation() {
return new ArcanistWorkflowInformation();
}
final public function executeWorkflow(PhutilArgumentParser $args) {
$runtime = $this->getRuntime();
$this->arguments = $args;
$caught = null;
$runtime->pushWorkflow($this);
try {
$err = $this->runWorkflow($args);
} catch (Exception $ex) {
$caught = $ex;
}
try {
$this->runWorkflowCleanup();
} catch (Exception $ex) {
phlog($ex);
}
$runtime->popWorkflow();
if ($caught) {
throw $caught;
}
return $err;
}
final protected function getLogEngine() {
return $this->getRuntime()->getLogEngine();
}
protected function runWorkflowCleanup() {
// TOOLSETS: Do we need this?
return;
}
public function __construct() {} public function __construct() {}
public function run() { public function run() {
@ -662,7 +742,12 @@ abstract class ArcanistWorkflow extends Phobject {
} }
final public function getArgument($key, $default = null) { final public function getArgument($key, $default = null) {
return idx($this->arguments, $key, $default); // TOOLSETS: Remove this legacy code.
if (is_array($this->arguments)) {
return idx($this->arguments, $key, $default);
}
return $this->arguments->getArg($key);
} }
final public function getPassedArguments() { final public function getPassedArguments() {