Differential D20467 Diff 48843 src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php
| <?php | <?php | ||||
| final class PhabricatorRepositoryManagementReparseWorkflow | final class PhabricatorRepositoryManagementReparseWorkflow | ||||
| extends PhabricatorRepositoryManagementWorkflow { | extends PhabricatorRepositoryManagementWorkflow { | ||||
| protected function didConstruct() { | protected function didConstruct() { | ||||
| $this | $this | ||||
| ->setName('reparse') | ->setName('reparse') | ||||
| ->setExamples('**reparse** [options] __commit__') | ->setExamples('**reparse** [options] __commit__') | ||||
| ->setSynopsis( | ->setSynopsis( | ||||
| pht( | pht( | ||||
| '**reparse** __what__ __which_parts__ [--trace] [--force]'."\n\n". | '**reparse** __what__ __which_parts__ [--trace] [--force]'."\n\n". | ||||
| 'Rerun the Diffusion parser on specific commits and repositories. '. | 'Rerun the Diffusion parser on specific commits and repositories. '. | ||||
| 'Mostly useful for debugging changes to Diffusion.'."\n\n". | 'Mostly useful for debugging changes to Diffusion.'."\n\n". | ||||
| 'e.g. enqueue reparse owners in the TEST repo for all commits:'."\n". | |||||
| 'repository reparse --all TEST --owners'."\n\n". | |||||
| 'e.g. do same but exclude before yesterday (local time):'."\n". | 'e.g. do same but exclude before yesterday (local time):'."\n". | ||||
| 'repository reparse --all TEST --owners --min-date yesterday'."\n". | 'repository reparse --all TEST --change --min-date yesterday'."\n". | ||||
| 'repository reparse --all TEST --owners --min-date "today -1 day".'. | 'repository reparse --all TEST --change --min-date "today -1 day".'. | ||||
| "\n\n". | "\n\n". | ||||
| 'e.g. do same but exclude before 03/31/2013 (local time):'."\n". | 'e.g. do same but exclude before 03/31/2013 (local time):'."\n". | ||||
| 'repository reparse --all TEST --owners --min-date "03/31/2013"')) | 'repository reparse --all TEST --change --min-date "03/31/2013"')) | ||||
| ->setArguments( | ->setArguments( | ||||
| array( | array( | ||||
| array( | array( | ||||
| 'name' => 'revision', | 'name' => 'revision', | ||||
| 'wildcard' => true, | 'wildcard' => true, | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'all', | 'name' => 'all', | ||||
| 'param' => 'repository', | 'param' => 'repository', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| 'Reparse all commits in the specified repository. This mode '. | 'Reparse all commits in the specified repository.'), | ||||
| 'queues parsers into the task queue; you must run taskmasters '. | |||||
| 'to actually do the parses. Use with __%s__ to run '. | |||||
| 'the tasks locally instead of with taskmasters.', | |||||
| '--force-local'), | |||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'min-date', | 'name' => 'min-date', | ||||
| 'param' => 'date', | 'param' => 'date', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| "Must be used with __%s__, this will exclude commits which ". | "Must be used with __%s__, this will exclude commits which ". | ||||
| "are earlier than __date__.\n". | "are earlier than __date__.\n". | ||||
| "Valid examples:\n". | "Valid examples:\n". | ||||
| " 'today', 'today 2pm', '-1 hour', '-2 hours', '-24 hours',\n". | " 'today', 'today 2pm', '-1 hour', '-2 hours', '-24 hours',\n". | ||||
| " 'yesterday', 'today -1 day', 'yesterday 2pm', '2pm -1 day',\n". | " 'yesterday', 'today -1 day', 'yesterday 2pm', '2pm -1 day',\n". | ||||
| " 'last Monday', 'last Monday 14:00', 'last Monday 2pm',\n". | " 'last Monday', 'last Monday 14:00', 'last Monday 2pm',\n". | ||||
| " '31 March 2013', '31 Mar', '03/31', '03/31/2013',\n". | " '31 March 2013', '31 Mar', '03/31', '03/31/2013',\n". | ||||
| "See __%s__ for more.", | "See __%s__ for more.", | ||||
| '--all', | '--all', | ||||
| 'http://www.php.net/manual/en/datetime.formats.php'), | 'http://www.php.net/manual/en/datetime.formats.php'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'message', | 'name' => 'message', | ||||
| 'help' => pht('Reparse commit messages.'), | 'help' => pht('Reparse commit messages.'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'change', | 'name' => 'change', | ||||
| 'help' => pht('Reparse changes.'), | 'help' => pht('Reparse source changes.'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'herald', | 'name' => 'publish', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| 'Reevaluate Herald rules (may send huge amounts of email!)'), | 'Publish changes: send email, publish Feed stories, run '. | ||||
| ), | 'Herald rules, etc.'), | ||||
amckinley: 'Publish changes. Includes evaluating Owners packages and Herald rules.'? | |||||
| array( | |||||
| 'name' => 'owners', | |||||
| 'help' => pht( | |||||
| 'Reevaluate related commits for owners packages (may delete '. | |||||
| 'existing relationship entries between your package and some '. | |||||
| 'old commits!)'), | |||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'force', | 'name' => 'force', | ||||
| 'short' => 'f', | 'short' => 'f', | ||||
| 'help' => pht('Act noninteractively, without prompting.'), | 'help' => pht('Act noninteractively, without prompting.'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'force-local', | 'name' => 'background', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| 'Only used with __%s__, use this to run the tasks locally '. | 'Queue tasks for the daemons instead of running them in the '. | ||||
| 'instead of deferring them to taskmaster daemons.', | 'foreground.'), | ||||
| '--all'), | |||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'importing', | 'name' => 'importing', | ||||
| 'help' => pht( | 'help' => pht('Reparse all steps which have not yet completed.'), | ||||
| 'Reparse all steps which have not yet completed.'), | |||||
| ), | ), | ||||
| )); | )); | ||||
| } | } | ||||
| public function execute(PhutilArgumentParser $args) { | public function execute(PhutilArgumentParser $args) { | ||||
| $console = PhutilConsole::getConsole(); | $console = PhutilConsole::getConsole(); | ||||
| $all_from_repo = $args->getArg('all'); | $all_from_repo = $args->getArg('all'); | ||||
| $reparse_message = $args->getArg('message'); | $reparse_message = $args->getArg('message'); | ||||
| $reparse_change = $args->getArg('change'); | $reparse_change = $args->getArg('change'); | ||||
| $reparse_herald = $args->getArg('herald'); | $reparse_publish = $args->getArg('publish'); | ||||
| $reparse_owners = $args->getArg('owners'); | |||||
| $reparse_what = $args->getArg('revision'); | $reparse_what = $args->getArg('revision'); | ||||
| $force = $args->getArg('force'); | $force = $args->getArg('force'); | ||||
| $force_local = $args->getArg('force-local'); | $background = $args->getArg('background'); | ||||
| $min_date = $args->getArg('min-date'); | $min_date = $args->getArg('min-date'); | ||||
| $importing = $args->getArg('importing'); | $importing = $args->getArg('importing'); | ||||
| if (!$all_from_repo && !$reparse_what) { | if (!$all_from_repo && !$reparse_what) { | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht('Specify a commit or repository to reparse.')); | pht('Specify a commit or repository to reparse.')); | ||||
| } | } | ||||
| if ($all_from_repo && $reparse_what) { | if ($all_from_repo && $reparse_what) { | ||||
| $commits = implode(', ', $reparse_what); | $commits = implode(', ', $reparse_what); | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| "Specify a commit or repository to reparse, not both:\n". | "Specify a commit or repository to reparse, not both:\n". | ||||
| "All from repo: %s\n". | "All from repo: %s\n". | ||||
| "Commit(s) to reparse: %s", | "Commit(s) to reparse: %s", | ||||
| $all_from_repo, | $all_from_repo, | ||||
| $commits)); | $commits)); | ||||
| } | } | ||||
| $any_step = ($reparse_message || | $any_step = ($reparse_message || $reparse_change || $reparse_publish); | ||||
| $reparse_change || | |||||
| $reparse_herald || | |||||
| $reparse_owners); | |||||
| if ($any_step && $importing) { | if ($any_step && $importing) { | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| 'Choosing steps with %s conflicts with flags which select '. | 'Choosing steps with "--importing" conflicts with flags which '. | ||||
| 'specific steps.', | 'select specific steps.')); | ||||
| '--importing')); | |||||
| } else if ($any_step) { | } else if ($any_step) { | ||||
| // OK. | // OK. | ||||
| } else if ($importing) { | } else if ($importing) { | ||||
| // OK. | // OK. | ||||
| } else if (!$any_step && !$importing) { | } else if (!$any_step && !$importing) { | ||||
Not Done Inline ActionsNever saw control flow like this before. Makes sense, but it took me a second. amckinley: Never saw control flow like this before. Makes sense, but it took me a second. | |||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| 'Specify which steps to reparse with %s, or %s, %s, %s, or %s.', | 'Specify which steps to reparse with "--message", "--change", '. | ||||
| '--importing', | 'and/or "--publish"; or "--importing" to run all missing steps.')); | ||||
Not Done Inline ActionsExtra "or" amckinley: Extra "or" | |||||
Not Done Inline Actions"; or --importing for all uncompleted steps" amckinley: "; or --importing for all uncompleted steps" | |||||
| '--message', | |||||
| '--change', | |||||
| '--herald', | |||||
| '--owners')); | |||||
| } | } | ||||
| $min_timestamp = false; | $min_timestamp = false; | ||||
| if ($min_date) { | if ($min_date) { | ||||
| $min_timestamp = strtotime($min_date); | $min_timestamp = strtotime($min_date); | ||||
| if (!$all_from_repo) { | if (!$all_from_repo) { | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| "You must use --all if you specify --min-date\n". | 'You must use "--all" if you specify "--min-date".')); | ||||
| "e.g.\n". | |||||
| " repository reparse --all TEST --owners --min-date yesterday")); | |||||
| } | } | ||||
| // previous to PHP 5.1.0 you would compare with -1, instead of false | // previous to PHP 5.1.0 you would compare with -1, instead of false | ||||
| if (false === $min_timestamp) { | if (false === $min_timestamp) { | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| "Supplied --min-date is not valid. See help for valid examples.\n". | "Supplied --min-date is not valid. See help for valid examples.\n". | ||||
| "Supplied value: '%s'\n", | "Supplied value: '%s'\n", | ||||
| $min_date)); | $min_date)); | ||||
| } | } | ||||
| } | } | ||||
| if ($reparse_owners && !$force) { | |||||
| $console->writeOut( | |||||
| "%s\n", | |||||
| pht( | |||||
| 'You are about to recreate the relationship entries between the '. | |||||
| 'commits and the packages they touch. This might delete some '. | |||||
| 'existing relationship entries for some old commits.')); | |||||
| if (!phutil_console_confirm(pht('Are you ready to continue?'))) { | |||||
| throw new PhutilArgumentUsageException(pht('Cancelled.')); | |||||
| } | |||||
| } | |||||
| $commits = array(); | $commits = array(); | ||||
| if ($all_from_repo) { | if ($all_from_repo) { | ||||
| $repository = id(new PhabricatorRepositoryQuery()) | $repository = id(new PhabricatorRepositoryQuery()) | ||||
| ->setViewer(PhabricatorUser::getOmnipotentUser()) | ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
| ->withIdentifiers(array($all_from_repo)) | ->withIdentifiers(array($all_from_repo)) | ||||
| ->executeOne(); | ->executeOne(); | ||||
| if (!$repository) { | if (!$repository) { | ||||
| Show All 20 Lines | if ($all_from_repo) { | ||||
| pht( | pht( | ||||
| 'No commits have been discovered in the "%s" repository!', | 'No commits have been discovered in the "%s" repository!', | ||||
| $repository->getDisplayName())); | $repository->getDisplayName())); | ||||
| } | } | ||||
| } else { | } else { | ||||
| $commits = $this->loadNamedCommits($reparse_what); | $commits = $this->loadNamedCommits($reparse_what); | ||||
| } | } | ||||
| if ($all_from_repo && !$force_local) { | if (!$background) { | ||||
| $console->writeOut("%s\n", pht( | PhabricatorWorker::setRunAllTasksInProcess(true); | ||||
| "**NOTE**: This script will queue tasks to reparse the data. Once the ". | |||||
| "tasks have been queued, you need to run Taskmaster daemons to ". | |||||
| "execute them.\n\n%s", | |||||
| pht( | |||||
| 'QUEUEING TASKS (%s Commit(s)):', | |||||
| phutil_count($commits)))); | |||||
Not Done Inline ActionsI think it's a little unfair to remove this and the similar warning back on line 22. amckinley: I think it's a little unfair to remove this //and// the similar warning back on line 22. | |||||
| } | } | ||||
| $progress = new PhutilConsoleProgressBar(); | $progress = new PhutilConsoleProgressBar(); | ||||
| $progress->setTotal(count($commits)); | $progress->setTotal(count($commits)); | ||||
| $tasks = array(); | $tasks = array(); | ||||
| foreach ($commits as $commit) { | foreach ($commits as $commit) { | ||||
| $repository = $commit->getRepository(); | $repository = $commit->getRepository(); | ||||
| if ($importing) { | if ($importing) { | ||||
| $status = $commit->getImportStatus(); | $status = $commit->getImportStatus(); | ||||
| // Find the first missing import step and queue that up. | // Find the first missing import step and queue that up. | ||||
| $reparse_message = false; | $reparse_message = false; | ||||
| $reparse_change = false; | $reparse_change = false; | ||||
| $reparse_owners = false; | $reparse_publish = false; | ||||
| $reparse_herald = false; | |||||
| if (!($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE)) { | if (!($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE)) { | ||||
| $reparse_message = true; | $reparse_message = true; | ||||
| } else if (!($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE)) { | } else if (!($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE)) { | ||||
| $reparse_change = true; | $reparse_change = true; | ||||
| } else if (!($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS)) { | } else if (!($status & PhabricatorRepositoryCommit::IMPORTED_PUBLISH)) { | ||||
| $reparse_owners = true; | $reparse_publish = true; | ||||
| } else if (!($status & PhabricatorRepositoryCommit::IMPORTED_HERALD)) { | |||||
| $reparse_herald = true; | |||||
| } else { | } else { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| $classes = array(); | $classes = array(); | ||||
| switch ($repository->getVersionControlSystem()) { | switch ($repository->getVersionControlSystem()) { | ||||
| case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: | case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: | ||||
| Show All 18 Lines | foreach ($commits as $commit) { | ||||
| $classes[] = 'PhabricatorRepositorySvnCommitMessageParserWorker'; | $classes[] = 'PhabricatorRepositorySvnCommitMessageParserWorker'; | ||||
| } | } | ||||
| if ($reparse_change) { | if ($reparse_change) { | ||||
| $classes[] = 'PhabricatorRepositorySvnCommitChangeParserWorker'; | $classes[] = 'PhabricatorRepositorySvnCommitChangeParserWorker'; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| if ($reparse_herald) { | if ($reparse_publish) { | ||||
| $classes[] = 'PhabricatorRepositoryCommitHeraldWorker'; | $classes[] = 'PhabricatorRepositoryCommitPublishWorker'; | ||||
| } | |||||
| if ($reparse_owners) { | |||||
| $classes[] = 'PhabricatorRepositoryCommitOwnersWorker'; | |||||
| } | } | ||||
| // NOTE: With "--importing", we queue the first unparsed step and let | // NOTE: With "--importing", we queue the first unparsed step and let | ||||
| // it queue the other ones normally. Without "--importing", we queue | // it queue the other ones normally. Without "--importing", we queue | ||||
| // all the requested steps explicitly. | // all the requested steps explicitly. | ||||
| $spec = array( | $spec = array( | ||||
| 'commitID' => $commit->getID(), | 'commitID' => $commit->getID(), | ||||
| 'only' => !$importing, | 'only' => !$importing, | ||||
| ); | ); | ||||
| if ($all_from_repo && !$force_local) { | |||||
| $background = true; | |||||
| } else { | |||||
| $background = false; | |||||
| } | |||||
| if (!$background) { | |||||
| PhabricatorWorker::setRunAllTasksInProcess(true); | |||||
| } | |||||
| foreach ($classes as $class) { | foreach ($classes as $class) { | ||||
| PhabricatorWorker::scheduleTask( | PhabricatorWorker::scheduleTask( | ||||
| $class, | $class, | ||||
| $spec, | $spec, | ||||
| array( | array( | ||||
| 'priority' => PhabricatorWorker::PRIORITY_IMPORT, | 'priority' => PhabricatorWorker::PRIORITY_IMPORT, | ||||
| )); | )); | ||||
| } | } | ||||
| Show All 10 Lines | |||||
'Publish changes. Includes evaluating Owners packages and Herald rules.'?