Changeset View
Changeset View
Standalone View
Standalone View
src/workflow/ArcanistLiberateWorkflow.php
| <?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__] | |||||
| EOTEXT | |||||
| ); | |||||
| } | |||||
| public function getCommandHelp() { | $help = pht(<<<EOTEXT | ||||
| return phutil_console_format(<<<EOTEXT | Create or update an Arcanist library. | ||||
| Supports: libphutil | |||||
| Create or update a libphutil library, generating required metadata | |||||
| files like \__init__.php. | |||||
| EOTEXT | EOTEXT | ||||
| ); | ); | ||||
| return $this->newWorkflowInformation() | |||||
| ->addExample(pht('**liberate**')) | |||||
| ->addExample(pht('**liberate** [__path__]')) | |||||
| ->setHelp($help); | |||||
| } | } | ||||
| public function getArguments() { | public function getWorkflowArguments() { | ||||
| 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'); | |||||
| $is_verify = $this->getArgument('verify'); | |||||
| $path = Filesystem::resolvePath($path); | $init_files = id(new FileFinder(getcwd())) | ||||
| 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'), | |||||
| pht( | |||||
| 'Updating library: %s', | |||||
| Filesystem::readablePath($path).DIRECTORY_SEPARATOR)); | |||||
| $this->liberatePath($path); | |||||
| } | } | ||||
| if (!$found) { | |||||
| echo pht("No library currently exists at that path...\n"); | $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->liberateCreateDirectory($path); | ||||
| $this->liberateCreateLibrary($path); | $this->liberateCreateLibrary($path); | ||||
| return; | 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( | ||||
| pht("Unknown library version '%s'!", $version)); | pht("Unknown library version '%s'!", $version)); | ||||
| } | } | ||||
| echo tsprintf("%s\n", pht('Done.')); | echo tsprintf("%s\n", pht('Done.')); | ||||
| } | } | ||||
| Show All 14 Lines | private function getLibraryFormatVersion($path) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| private function liberateVersion2($path) { | private function liberateVersion2($path) { | ||||
| $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)) { | ||||
| throw new ArcanistUsageException( | throw new ArcanistUsageException( | ||||
| pht( | pht( | ||||
| 'Provide a directory to create or update a libphutil library in.')); | 'Provide a directory to create or update a libphutil library in.')); | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| 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) { | ||||
| $init_path = $path.'/__phutil_library_init__.php'; | $init_path = $path.'/__phutil_library_init__.php'; | ||||
| if (Filesystem::pathExists($init_path)) { | if (Filesystem::pathExists($init_path)) { | ||||
| return; | return; | ||||
| } | } | ||||
| 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'); | |||||
| if ($name === null) { | |||||
| echo pht('Choose a name for the new library.')."\n"; | echo pht('Choose a name for the new library.')."\n"; | ||||
| $name = phutil_console_prompt( | $name = phutil_console_prompt( | ||||
| pht('What do you want to name this library?')); | 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 { | ||||
| echo phutil_console_format( | echo phutil_console_format( | ||||
| "%s\n", | "%s\n", | ||||
| pht( | pht( | ||||
| 'Library name should contain only lowercase letters and hyphens.')); | 'Library name should contain only lowercase letters and hyphens.')); | ||||
| } | } | ||||
| Show All 21 Lines | |||||