public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $public_keyfile = $args->getArg('public'); if (!strlen($public_keyfile)) { throw new PhutilArgumentUsageException(pht('You must specify the path to a public keyfile with %s.', '--public')); } if (!Filesystem::pathExists($public_keyfile)) { throw new PhutilArgumentUsageException(pht('Specified public keyfile "%s" does not exist!', $public_keyfile)); } $public_key = Filesystem::readFile($public_keyfile); $pkcs8_keyfile = $args->getArg('pkcs8'); if (!strlen($pkcs8_keyfile)) { throw new PhutilArgumentUsageException(pht('You must specify the path to a pkcs8 keyfile with %s.', '--pkc8s')); } if (!Filesystem::pathExists($pkcs8_keyfile)) { throw new PhutilArgumentUsageException(pht('Specified pkcs8 keyfile "%s" does not exist!', $pkcs8_keyfile)); } $pkcs8_key = Filesystem::readFile($pkcs8_keyfile); $warning = pht('Adding a PKCS8 keyfile to the cache can be very dangerous. If the ' . 'PKCS8 file really encodes a different public key than the one ' . 'specified, an attacker could use it to gain unauthorized access.' . "\n\n" . 'Generally, you should use this option only in a development ' . 'environment where ssh-keygen is broken and it is inconvenient to ' . 'fix it, and only if you are certain you understand the risks. You ' . 'should never cache a PKCS8 file you did not generate yourself.'); $console->writeOut("%s\n", phutil_console_wrap($warning)); $prompt = pht('Really trust this PKCS8 keyfile?'); if (!phutil_console_confirm($prompt)) { throw new PhutilArgumentUsageException(pht('Aborted workflow.')); } $key = PhabricatorAuthSSHPublicKey::newFromRawKey($public_key); $key->forcePopulatePKCS8Cache($pkcs8_key); $console->writeOut("%s\n", pht('Cached PKCS8 key for public key.')); return 0; }
public function didExecute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $patches = $this->getPatches(); if (!$this->isDryRun() && !$this->isForce()) { $console->writeOut(phutil_console_wrap(pht('Before running storage upgrades, you should take down the ' . 'Phabricator web interface and stop any running Phabricator ' . 'daemons (you can disable this warning with %s).', '--force'))); if (!phutil_console_confirm(pht('Are you ready to continue?'))) { $console->writeOut("%s\n", pht('Cancelled.')); return 1; } } $apply_only = $args->getArg('apply'); if ($apply_only) { if (empty($patches[$apply_only])) { throw new PhutilArgumentUsageException(pht("%s argument '%s' is not a valid patch. " . "Use '%s' to show patch status.", '--apply', $apply_only, './bin/storage status')); } } $no_quickstart = $args->getArg('no-quickstart'); $init_only = $args->getArg('init-only'); $no_adjust = $args->getArg('no-adjust'); $this->upgradeSchemata($apply_only, $no_quickstart, $init_only); if ($no_adjust || $init_only || $apply_only) { $console->writeOut("%s\n", pht('Declining to apply storage adjustments.')); return 0; } else { return $this->adjustSchemata(false); } }
public function execute(PhutilArgumentParser $args) { $supported_types = id(new PhutilSymbolLoader())->setAncestorClass('PhabricatorTestDataGenerator')->loadObjects(); echo "These are the types of data you can generate:\n"; foreach (array_keys($supported_types) as $typetmp) { echo "\t" . $typetmp . "\n"; } echo "\n"; $prompt = 'Are you sure you want to generate lots of test data?'; if (!phutil_console_confirm($prompt, $default_no = true)) { return; } $argv = $args->getArg('args'); if (count($argv) == 0 || count($argv) == 1 && $argv[0] == 'all') { $this->infinitelyGenerate($supported_types); } else { $new_supported_types = array(); for ($i = 0; $i < count($argv); $i++) { $arg = $argv[$i]; if (array_key_exists($arg, $supported_types)) { $new_supported_types[$arg] = $supported_types[$arg]; } else { echo "The type " . $arg . " is not supported by the lipsum generator.\n"; } } $this->infinitelyGenerate($new_supported_types); } echo "None of the input types were supported.\n"; echo "The supported types are:\n"; echo implode("\n", array_keys($supported_types)); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $id = $args->getArg('id'); if (!$id) { throw new PhutilArgumentUsageException(pht('Specify a public key to trust with --id.')); } $key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withIDs(array($id))->executeOne(); if (!$key) { throw new PhutilArgumentUsageException(pht('No public key exists with ID "%s".', $id)); } if ($key->getIsTrusted()) { throw new PhutilArgumentUsageException(pht('Public key with ID %s is already trusted.', $id)); } if (!$key->getObject() instanceof AlmanacDevice) { throw new PhutilArgumentUsageException(pht('You can only trust keys associated with Almanac devices.')); } $handle = id(new PhabricatorHandleQuery())->setViewer($this->getViewer())->withPHIDs(array($key->getObject()->getPHID()))->executeOne(); $console->writeOut("**<bg:red> %s </bg>**\n\n%s\n\n%s\n\n%s", pht('IMPORTANT!'), phutil_console_wrap(pht('Trusting a public key gives anyone holding the corresponding ' . 'private key complete, unrestricted access to all data in ' . 'Phabricator. The private key will be able to sign requests that ' . 'skip policy and security checks.')), phutil_console_wrap(pht('This is an advanced feature which should normally be used only ' . 'when building a Phabricator cluster. This feature is very ' . 'dangerous if misused.')), pht('This key is associated with device "%s".', $handle->getName())); $prompt = pht('Really trust this key?'); if (!phutil_console_confirm($prompt)) { throw new PhutilArgumentUsageException(pht('User aborted workflow.')); } $key->setIsTrusted(1); $key->save(); $console->writeOut("**<bg:green> %s </bg>** %s\n", pht('TRUSTED'), pht('Key %s has been marked as trusted.', $id)); }
public function execute(PhutilArgumentParser $args) { $is_dry = $args->getArg('dryrun'); $is_force = $args->getArg('force'); if (!$is_dry && !$is_force) { echo phutil_console_wrap("Are you completely sure you really want to permanently destroy all " . "storage for Phabricator data? This operation can not be undone and " . "your data will not be recoverable if you proceed."); if (!phutil_console_confirm('Permanently destroy all data?')) { echo "Cancelled.\n"; exit(1); } if (!phutil_console_confirm('Really destroy all data forever?')) { echo "Cancelled.\n"; exit(1); } } $api = $this->getAPI(); $patches = $this->getPatches(); $databases = $api->getDatabaseList($patches); $databases[] = $api->getDatabaseName('meta_data'); foreach ($databases as $database) { if ($is_dry) { echo "DRYRUN: Would drop database '{$database}'.\n"; } else { echo "Dropping database '{$database}'...\n"; queryfx($api->getConn('meta_data', $select_database = false), 'DROP DATABASE IF EXISTS %T', $database); } } if (!$is_dry) { echo "Storage was destroyed.\n"; } return 0; }
public function handleMessage(PhutilConsoleMessage $message) { $data = $message->getData(); $type = $message->getType(); switch ($type) { case PhutilConsoleMessage::TYPE_CONFIRM: $ok = phutil_console_confirm($data['prompt'], !$data['default']); return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $ok); case PhutilConsoleMessage::TYPE_PROMPT: $response = phutil_console_prompt($data['prompt'], idx($data, 'history')); return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $response); case PhutilConsoleMessage::TYPE_OUT: $this->writeText(STDOUT, $data); return null; case PhutilConsoleMessage::TYPE_ERR: $this->writeText(STDERR, $data); return null; case PhutilConsoleMessage::TYPE_LOG: if ($this->enableLog) { $this->writeText(STDERR, $data); } return null; default: if ($this->handler) { return call_user_func($this->handler, $message); } else { throw new Exception("Received unknown console message of type '{$type}'."); } } }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $supported_types = id(new PhutilClassMapQuery())->setAncestorClass('PhabricatorTestDataGenerator')->execute(); $console->writeOut("%s:\n\t%s\n", pht('These are the types of data you can generate'), implode("\n\t", array_keys($supported_types))); $prompt = pht('Are you sure you want to generate lots of test data?'); if (!phutil_console_confirm($prompt, true)) { return; } $argv = $args->getArg('args'); if (count($argv) == 0 || count($argv) == 1 && $argv[0] == 'all') { $this->infinitelyGenerate($supported_types); } else { $new_supported_types = array(); for ($i = 0; $i < count($argv); $i++) { $arg = $argv[$i]; if (array_key_exists($arg, $supported_types)) { $new_supported_types[$arg] = $supported_types[$arg]; } else { $console->writeErr("%s\n", pht('The type %s is not supported by the lipsum generator.', $arg)); } } $this->infinitelyGenerate($new_supported_types); } $console->writeOut("%s\n%s:\n%s\n", pht('None of the input types were supported.'), pht('The supported types are'), implode("\n", array_keys($supported_types))); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $repos = id(new PhabricatorRepositoryQuery())->setViewer($this->getViewer())->execute(); if (!$repos) { $console->writeErr("%s\n", pht('There are no repositories.')); return 0; } $from = $args->getArg('from'); if (!strlen($from)) { throw new Exception(pht('You must specify a path prefix to move from with --from.')); } $to = $args->getArg('to'); if (!strlen($to)) { throw new Exception(pht('You must specify a path prefix to move to with --to.')); } $rows = array(); $any_changes = false; foreach ($repos as $repo) { $src = $repo->getLocalPath(); $row = array('repository' => $repo, 'move' => false, 'monogram' => $repo->getMonogram(), 'src' => $src, 'dst' => ''); if (strncmp($src, $from, strlen($from))) { $row['action'] = pht('Ignore'); } else { $dst = $to . substr($src, strlen($from)); $row['action'] = phutil_console_format('**%s**', pht('Move')); $row['dst'] = $dst; $row['move'] = true; $any_changes = true; } $rows[] = $row; } $table = id(new PhutilConsoleTable())->addColumn('action', array('title' => pht('Action')))->addColumn('monogram', array('title' => pht('Repository')))->addColumn('src', array('title' => pht('Src')))->addColumn('dst', array('title' => pht('dst')))->setBorders(true); foreach ($rows as $row) { $display = array_select_keys($row, array('action', 'monogram', 'src', 'dst')); $table->addRow($display); } $table->draw(); if (!$any_changes) { $console->writeOut(pht('No matching repositories.') . "\n"); return 0; } $prompt = pht('Apply these changes?'); if (!phutil_console_confirm($prompt)) { throw new Exception(pht('Declining to apply changes.')); } foreach ($rows as $row) { if (empty($row['move'])) { continue; } $repo = $row['repository']; $details = $repo->getDetails(); $details['local-path'] = $row['dst']; queryfx($repo->establishConnection('w'), 'UPDATE %T SET details = %s WHERE id = %d', $repo->getTableName(), phutil_json_encode($details), $repo->getID()); } $console->writeOut(pht('Applied changes.') . "\n"); return 0; }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $viewer = $this->getViewer(); $subscription_phid = $args->getArg('subscription'); if (!$subscription_phid) { throw new PhutilArgumentUsageException(pht('Specify which subscription to invoice with %s.', '--subscription')); } $subscription = id(new PhortuneSubscriptionQuery())->setViewer($viewer)->withPHIDs(array($subscription_phid))->needTriggers(true)->executeOne(); if (!$subscription) { throw new PhutilArgumentUsageException(pht('Unable to load subscription with PHID "%s".', $subscription_phid)); } $now = $args->getArg('now'); $now = $this->parseTimeArgument($now); if (!$now) { $now = PhabricatorTime::getNow(); } $time_guard = PhabricatorTime::pushTime($now, date_default_timezone_get()); $console->writeOut("%s\n", pht('Set current time to %s.', phabricator_datetime(PhabricatorTime::getNow(), $viewer))); $auto_range = $args->getArg('auto-range'); $last_arg = $args->getArg('last'); $next_arg = $args->getARg('next'); if (!$auto_range && !$last_arg && !$next_arg) { throw new PhutilArgumentUsageException(pht('Specify a billing range with %s and %s, or use %s.', '--last', '--next', '--auto-range')); } else { if (!$auto_range & (!$last_arg || !$next_arg)) { throw new PhutilArgumentUsageException(pht('When specifying %s or %s, you must specify both arguments ' . 'to define the beginning and end of the billing range.', '--last', '--next')); } else { if (!$auto_range && ($last_arg && $next_arg)) { $last_time = $this->parseTimeArgument($args->getArg('last')); $next_time = $this->parseTimeArgument($args->getArg('next')); } else { if ($auto_range && ($last_arg || $next_arg)) { throw new PhutilArgumentUsageException(pht('Use either %s or %s and %s to specify the ' . 'billing range, but not both.', '--auto-range', '--last', '--next')); } else { $trigger = $subscription->getTrigger(); $event = $trigger->getEvent(); if (!$event) { throw new PhutilArgumentUsageException(pht('Unable to calculate %s, this subscription has not been ' . 'scheduled for billing yet. Wait for the trigger daemon to ' . 'schedule the subscription.', '--auto-range')); } $last_time = $event->getLastEventEpoch(); $next_time = $event->getNextEventEpoch(); } } } } $console->writeOut("%s\n", pht('Preparing to invoice subscription "%s" from %s to %s.', $subscription->getSubscriptionName(), $last_time ? phabricator_datetime($last_time, $viewer) : pht('subscription creation'), phabricator_datetime($next_time, $viewer))); PhabricatorWorker::setRunAllTasksInProcess(true); if (!$args->getArg('force')) { $console->writeOut("**<bg:yellow> %s </bg>**\n%s\n", pht('WARNING'), phutil_console_wrap(pht('Manually invoicing will double bill payment accounts if the ' . 'range overlaps an existing or future invoice. This script is ' . 'intended for testing and development, and should not be part ' . 'of routine billing operations. If you continue, you may ' . 'incorrectly overcharge customers.'))); if (!phutil_console_confirm(pht('Really invoice this subscription?'))) { throw new Exception(pht('Declining to invoice.')); } } PhabricatorWorker::scheduleTask('PhortuneSubscriptionWorker', array('subscriptionPHID' => $subscription->getPHID(), 'trigger.last-epoch' => $last_time, 'trigger.this-epoch' => $next_time, 'manual' => true), array('objectPHID' => $subscription->getPHID())); return 0; }
public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $ids = $args->getArg('id'); $active = $args->getArg('active'); if (!$ids && !$active) { throw new PhutilArgumentUsageException(pht('Use --id or --active to select builds.')); } if ($ids && $active) { throw new PhutilArgumentUsageException(pht('Use one of --id or --active to select builds, but not both.')); } $query = id(new HarbormasterBuildQuery())->setViewer($viewer); if ($ids) { $query->withIDs($ids); } else { $query->withBuildStatuses(HarbormasterBuildStatus::getActiveStatusConstants()); } $builds = $query->execute(); $console = PhutilConsole::getConsole(); $count = count($builds); if (!$count) { $console->writeOut("%s\n", pht('No builds to restart.')); return 0; } $prompt = pht('Restart %s build(s)?', new PhutilNumber($count)); if (!phutil_console_confirm($prompt)) { $console->writeOut("%s\n", pht('Cancelled.')); return 1; } $app_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); $editor = id(new HarbormasterBuildTransactionEditor())->setActor($viewer)->setActingAsPHID($app_phid)->setContentSource($this->newContentSource()); foreach ($builds as $build) { $console->writeOut("<bg:blue> %s </bg> %s\n", pht('RESTARTING'), pht('Build %d: %s', $build->getID(), $build->getName())); if (!$build->canRestartBuild()) { $console->writeOut("<bg:yellow> %s </bg> %s\n", pht('INVALID'), pht('Cannot be restarted.')); continue; } $xactions = array(); $xactions[] = id(new HarbormasterBuildTransaction())->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND)->setNewValue(HarbormasterBuildCommand::COMMAND_RESTART); try { $editor->applyTransactions($build, $xactions); } catch (Exception $e) { $message = phutil_console_wrap($e->getMessage(), 2); $console->writeOut("<bg:red> %s </bg>\n%s\n", pht('FAILED'), $message); continue; } $console->writeOut("<bg:green> %s </bg>\n", pht('SUCCESS')); } return 0; }
public function execute(PhutilArgumentParser $args) { $config_key = 'phabricator.developer-mode'; if (!PhabricatorEnv::getEnvConfig($config_key)) { throw new PhutilArgumentUsageException(pht('lipsum is a development and testing tool and may only be run ' . 'on installs in developer mode. Enable "%s" in your configuration ' . 'to enable lipsum.', $config_key)); } $all_generators = id(new PhutilClassMapQuery())->setAncestorClass('PhabricatorTestDataGenerator')->execute(); $argv = $args->getArg('args'); $all = 'all'; if (!$argv) { $names = mpull($all_generators, 'getGeneratorName'); sort($names); $list = id(new PhutilConsoleList())->setWrap(false)->addItems($names); id(new PhutilConsoleBlock())->addParagraph(pht('Choose which type or types of test data you want to generate, ' . 'or select "%s".', $all))->addList($list)->draw(); return 0; } $generators = array(); foreach ($argv as $arg_original) { $arg = phutil_utf8_strtolower($arg_original); $match = false; foreach ($all_generators as $generator) { $name = phutil_utf8_strtolower($generator->getGeneratorName()); if ($arg == $all) { $generators[] = $generator; $match = true; break; } if (strpos($name, $arg) !== false) { $generators[] = $generator; $match = true; break; } } if (!$match) { throw new PhutilArgumentUsageException(pht('Argument "%s" does not match the name of any generators.', $arg_original)); } } echo tsprintf("**<bg:blue> %s </bg>** %s\n", pht('GENERATORS'), pht('Selected generators: %s.', implode(', ', mpull($generators, 'getGeneratorName')))); echo tsprintf("**<bg:yellow> %s </bg>** %s\n", pht('WARNING'), pht('This command generates synthetic test data, including user ' . 'accounts. It is intended for use in development environments ' . 'so you can test features more easily. There is no easy way to ' . 'delete this data or undo the effects of this command. If you run ' . 'it in a production environment, it will pollute your data with ' . 'large amounts of meaningless garbage that you can not get rid of.')); $prompt = pht('Are you sure you want to generate piles of garbage?'); if (!phutil_console_confirm($prompt, true)) { return; } echo tsprintf("**<bg:green> %s </bg>** %s\n", pht('LIPSUM'), pht('Generating synthetic test objects forever. ' . 'Use ^C to stop when satisfied.')); $this->generate($generators); }
public function didExecute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); if (!$this->isDryRun() && !$this->isForce()) { $console->writeOut(phutil_console_wrap(pht('Are you completely sure you really want to permanently destroy ' . 'all storage for Phabricator data? This operation can not be ' . 'undone and your data will not be recoverable if you proceed.'))); if (!phutil_console_confirm(pht('Permanently destroy all data?'))) { $console->writeOut("%s\n", pht('Cancelled.')); exit(1); } if (!phutil_console_confirm(pht('Really destroy all data forever?'))) { $console->writeOut("%s\n", pht('Cancelled.')); exit(1); } } $apis = $this->getMasterAPIs(); foreach ($apis as $api) { $patches = $this->getPatches(); if ($args->getArg('unittest-fixtures')) { $conn = $api->getConn(null); $databases = queryfx_all($conn, 'SELECT DISTINCT(TABLE_SCHEMA) AS db ' . 'FROM INFORMATION_SCHEMA.TABLES ' . 'WHERE TABLE_SCHEMA LIKE %>', PhabricatorTestCase::NAMESPACE_PREFIX); $databases = ipull($databases, 'db'); } else { $databases = $api->getDatabaseList($patches); $databases[] = $api->getDatabaseName('meta_data'); // These are legacy databases that were dropped long ago. See T2237. $databases[] = $api->getDatabaseName('phid'); $databases[] = $api->getDatabaseName('directory'); } foreach ($databases as $database) { if ($this->isDryRun()) { $console->writeOut("%s\n", pht("DRYRUN: Would drop database '%s'.", $database)); } else { $console->writeOut("%s\n", pht("Dropping database '%s'...", $database)); queryfx($api->getConn(null), 'DROP DATABASE IF EXISTS %T', $database); } } if (!$this->isDryRun()) { $console->writeOut("%s\n", pht('Storage on "%s" was destroyed.', $api->getRef()->getRefKey())); } } return 0; }
public function execute(PhutilArgumentParser $args) { $is_dry = $args->getArg('dryrun'); $is_force = $args->getArg('force'); if (!$is_dry && !$is_force) { echo phutil_console_wrap('Are you completely sure you really want to permanently destroy all ' . 'storage for Phabricator data? This operation can not be undone and ' . 'your data will not be recoverable if you proceed.'); if (!phutil_console_confirm('Permanently destroy all data?')) { echo "Cancelled.\n"; exit(1); } if (!phutil_console_confirm('Really destroy all data forever?')) { echo "Cancelled.\n"; exit(1); } } $api = $this->getAPI(); $patches = $this->getPatches(); if ($args->getArg('unittest-fixtures')) { $conn = $api->getConn(null); $databases = queryfx_all($conn, 'SELECT DISTINCT(TABLE_SCHEMA) AS db ' . 'FROM INFORMATION_SCHEMA.TABLES ' . 'WHERE TABLE_SCHEMA LIKE %>', PhabricatorTestCase::NAMESPACE_PREFIX); $databases = ipull($databases, 'db'); } else { $databases = $api->getDatabaseList($patches); $databases[] = $api->getDatabaseName('meta_data'); // These are legacy databases that were dropped long ago. See T2237. $databases[] = $api->getDatabaseName('phid'); $databases[] = $api->getDatabaseName('directory'); } foreach ($databases as $database) { if ($is_dry) { echo "DRYRUN: Would drop database '{$database}'.\n"; } else { echo "Dropping database '{$database}'...\n"; queryfx($api->getConn(null), 'DROP DATABASE IF EXISTS %T', $database); } } if (!$is_dry) { echo "Storage was destroyed.\n"; } return 0; }
public function run() { $is_show = $this->getArgument('show'); $repository_api = $this->getRepositoryAPI(); if (!$is_show) { if (!$repository_api->supportsAmend()) { throw new ArcanistUsageException(pht("You may only run '%s' in a git or hg " . "(version 2.2 or newer) working copy.", 'arc amend')); } if ($this->isHistoryImmutable()) { throw new ArcanistUsageException(pht('This project is marked as adhering to a conservative history ' . 'mutability doctrine (having an immutable local history), which ' . 'precludes amending commit messages.')); } if ($repository_api->getUncommittedChanges()) { throw new ArcanistUsageException(pht('You have uncommitted changes in this branch. Stage and commit ' . '(or revert) them before proceeding.')); } } $revision_id = null; if ($this->getArgument('revision')) { $revision_id = $this->normalizeRevisionID($this->getArgument('revision')); } $repository_api->setBaseCommitArgumentRules('arc:this'); $in_working_copy = $repository_api->loadWorkingCopyDifferentialRevisions($this->getConduit(), array('status' => 'status-any')); $in_working_copy = ipull($in_working_copy, null, 'id'); if (!$revision_id) { if (count($in_working_copy) == 0) { throw new ArcanistUsageException(pht("No revision specified with '%s', and no revisions found " . "in the working copy. Use '%s' to specify which revision " . "you want to amend.", '--revision', '--revision <id>')); } else { if (count($in_working_copy) > 1) { $message = pht("More than one revision was found in the working copy:\n%s\n" . "Use '%s' to specify which revision you want to amend.", $this->renderRevisionList($in_working_copy), '--revision <id>'); throw new ArcanistUsageException($message); } else { $revision_id = key($in_working_copy); $revision = $in_working_copy[$revision_id]; if ($revision['authorPHID'] != $this->getUserPHID()) { $other_author = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($revision['authorPHID']))); $other_author = ipull($other_author, 'userName', 'phid'); $other_author = $other_author[$revision['authorPHID']]; $rev_title = $revision['title']; $ok = phutil_console_confirm(pht("You are amending the revision '%s' but you are not " . "the author. Amend this revision by %s?", "D{$revision_id}: {$rev_title}", $other_author)); if (!$ok) { throw new ArcanistUserAbortException(); } } } } } $conduit = $this->getConduit(); try { $message = $conduit->callMethodSynchronous('differential.getcommitmessage', array('revision_id' => $revision_id, 'edit' => false)); } catch (ConduitClientException $ex) { if (strpos($ex->getMessage(), 'ERR_NOT_FOUND') === false) { throw $ex; } else { throw new ArcanistUsageException(pht("Revision '%s' does not exist.", "D{$revision_id}")); } } $revision = $conduit->callMethodSynchronous('differential.query', array('ids' => array($revision_id))); if (empty($revision)) { throw new Exception(pht("Failed to lookup information for '%s'!", "D{$revision_id}")); } $revision = head($revision); $revision_title = $revision['title']; if (!$is_show) { if ($revision_id && empty($in_working_copy[$revision_id])) { $ok = phutil_console_confirm(pht("The revision '%s' does not appear to be in the working copy. Are " . "you sure you want to amend HEAD with the commit message for '%s'?", "D{$revision_id}", "D{$revision_id}: {$revision_title}")); if (!$ok) { throw new ArcanistUserAbortException(); } } } if ($is_show) { echo $message . "\n"; } else { echo pht("Amending commit message to reflect revision %s.\n", phutil_console_format('**D%d: %s**', $revision_id, $revision_title)); $repository_api->amendCommit($message); } return 0; }
define('SCHEMA_VERSION_TABLE_NAME', 'schema_version'); // TODO: getopt() is super terrible, move to something less terrible. $options = getopt('fhv:u:p:') + array('v' => null, 'u' => null, 'p' => null); foreach (array('h', 'f') as $key) { // By default, these keys are set to 'false' to indicate that the flag was // passed. if (array_key_exists($key, $options)) { $options[$key] = true; } } if (!empty($options['h']) || $options['v'] && !is_numeric($options['v'])) { usage(); } if (empty($options['f'])) { echo phutil_console_wrap("Before running this script, you should take down the Phabricator web " . "interface and stop any running Phabricator daemons."); if (!phutil_console_confirm('Are you ready to continue?')) { echo "Cancelled.\n"; exit(1); } } // Use always the version from the commandline if it is defined $next_version = isset($options['v']) ? (int) $options['v'] : null; $conf = DatabaseConfigurationProvider::getConfiguration(); if ($options['u']) { $conn_user = $options['u']; $conn_pass = $options['p']; } else { $conn_user = $conf->getUser(); $conn_pass = $conf->getPassword(); } $conn_host = $conf->getHost();
private function uploadFilesForChanges(array $changes) { assert_instances_of($changes, 'ArcanistDiffChange'); // Collect all the files we need to upload. $need_upload = array(); foreach ($changes as $key => $change) { if ($change->getFileType() != ArcanistDiffChangeType::FILE_BINARY) { continue; } if ($this->getArgument('skip-binaries')) { continue; } $name = basename($change->getCurrentPath()); $need_upload[] = array('type' => 'old', 'name' => $name, 'data' => $change->getOriginalFileData(), 'change' => $change); $need_upload[] = array('type' => 'new', 'name' => $name, 'data' => $change->getCurrentFileData(), 'change' => $change); } if (!$need_upload) { return; } // Determine mime types and file sizes. Update changes from "binary" to // "image" if the file is an image. Set image metadata. $type_image = ArcanistDiffChangeType::FILE_IMAGE; foreach ($need_upload as $key => $spec) { $change = $need_upload[$key]['change']; $type = $spec['type']; $size = strlen($spec['data']); $change->setMetadata("{$type}:file:size", $size); if ($spec['data'] === null) { // This covers the case where a file was added or removed; we don't // need to upload the other half of it (e.g., the old file data for // a file which was just added). This is distinct from an empty // file, which we do upload. unset($need_upload[$key]); continue; } $mime = $this->getFileMimeType($spec['data']); if (preg_match('@^image/@', $mime)) { $change->setFileType($type_image); } $change->setMetadata("{$type}:file:mime-type", $mime); } $uploader = id(new ArcanistFileUploader())->setConduitClient($this->getConduit()); foreach ($need_upload as $key => $spec) { $ref = id(new ArcanistFileDataRef())->setName($spec['name'])->setData($spec['data']); $uploader->addFile($ref, $key); } $files = $uploader->uploadFiles(); $errors = false; foreach ($files as $key => $file) { if ($file->getErrors()) { unset($files[$key]); $errors = true; echo pht('Failed to upload binary "%s".', $file->getName()); } } if ($errors) { $prompt = pht('Continue?'); $ok = phutil_console_confirm($prompt, $default_no = false); if (!$ok) { throw new ArcanistUsageException(pht('Aborted due to file upload failure. You can use %s ' . 'to skip binary uploads.', '--skip-binaries')); } } foreach ($files as $key => $file) { $spec = $need_upload[$key]; $phid = $file->getPHID(); $change = $spec['change']; $type = $spec['type']; $change->setMetadata("{$type}:binary-phid", $phid); echo pht('Uploaded binary data for "%s".', $file->getName()) . "\n"; } echo pht('Upload complete.') . "\n"; }
public function handleMessage(PhutilConsoleMessage $message) { $data = $message->getData(); $type = $message->getType(); switch ($type) { case PhutilConsoleMessage::TYPE_CONFIRM: $ok = phutil_console_confirm($data['prompt'], !$data['default']); return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $ok); case PhutilConsoleMessage::TYPE_PROMPT: $response = phutil_console_prompt($data['prompt'], idx($data, 'history')); return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $response); case PhutilConsoleMessage::TYPE_OUT: $this->writeText(STDOUT, $data); return null; case PhutilConsoleMessage::TYPE_ERR: $this->writeText(STDERR, $data); return null; case PhutilConsoleMessage::TYPE_LOG: if ($this->enableLog) { $this->writeText(STDERR, $data); } return null; case PhutilConsoleMessage::TYPE_ENABLED: switch ($data['which']) { case PhutilConsoleMessage::TYPE_LOG: $enabled = $this->enableLog; break; default: $enabled = true; break; } return $this->buildMessage(PhutilConsoleMessage::TYPE_IS_ENABLED, $enabled); case PhutilConsoleMessage::TYPE_TTY: case PhutilConsoleMessage::TYPE_COLS: switch ($data['which']) { case PhutilConsoleMessage::TYPE_OUT: $which = STDOUT; break; case PhutilConsoleMessage::TYPE_ERR: $which = STDERR; break; } switch ($type) { case PhutilConsoleMessage::TYPE_TTY: if (function_exists('posix_isatty')) { $is_a_tty = posix_isatty($which); } else { $is_a_tty = null; } return $this->buildMessage(PhutilConsoleMessage::TYPE_IS_TTY, $is_a_tty); case PhutilConsoleMessage::TYPE_COLS: // TODO: This is an approximation which might not be perfectly // accurate. $width = phutil_console_get_terminal_width(); return $this->buildMessage(PhutilConsoleMessage::TYPE_COL_WIDTH, $width); } break; default: if ($this->handler) { return call_user_func($this->handler, $message); } else { throw new Exception(pht("Received unknown console message of type '%s'.", $type)); } } }
private function uploadFilesForChanges(array $changes) { assert_instances_of($changes, 'ArcanistDiffChange'); // Collect all the files we need to upload. $need_upload = array(); foreach ($changes as $key => $change) { if ($change->getFileType() != ArcanistDiffChangeType::FILE_BINARY) { continue; } if ($this->getArgument('skip-binaries')) { continue; } $name = basename($change->getCurrentPath()); $need_upload[] = array('type' => 'old', 'name' => $name, 'data' => $change->getOriginalFileData(), 'change' => $change); $need_upload[] = array('type' => 'new', 'name' => $name, 'data' => $change->getCurrentFileData(), 'change' => $change); } if (!$need_upload) { return; } // Determine mime types and file sizes. Update changes from "binary" to // "image" if the file is an image. Set image metadata. $type_image = ArcanistDiffChangeType::FILE_IMAGE; foreach ($need_upload as $key => $spec) { $change = $need_upload[$key]['change']; $type = $spec['type']; $size = strlen($spec['data']); $change->setMetadata("{$type}:file:size", $size); if ($spec['data'] === null) { // This covers the case where a file was added or removed; we don't // need to upload it. (This is distinct from an empty file, which we // do upload.) unset($need_upload[$key]); continue; } $mime = $this->getFileMimeType($spec['data']); if (preg_match('@^image/@', $mime)) { $change->setFileType($type_image); } $change->setMetadata("{$type}:file:mime-type", $mime); } echo pht("Uploading %d files...", count($need_upload)) . "\n"; // Now we're ready to upload the actual file data. If possible, we'll just // transmit a hash of the file instead of the actual file data. If the data // already exists, Phabricator can share storage. Check if we can use // "file.uploadhash" yet (i.e., if the server is up to date enough). // TODO: Drop this check once we bump the protocol version. $conduit_methods = $this->getConduit()->callMethodSynchronous('conduit.query', array()); $can_use_hash_upload = isset($conduit_methods['file.uploadhash']); if ($can_use_hash_upload) { $hash_futures = array(); foreach ($need_upload as $key => $spec) { $hash_futures[$key] = $this->getConduit()->callMethod('file.uploadhash', array('name' => $spec['name'], 'hash' => sha1($spec['data']))); } foreach (Futures($hash_futures)->limit(8) as $key => $future) { $type = $need_upload[$key]['type']; $change = $need_upload[$key]['change']; $name = $need_upload[$key]['name']; $phid = null; try { $phid = $future->resolve(); } catch (Exception $e) { // Just try uploading normally if the hash upload failed. continue; } if ($phid) { $change->setMetadata("{$type}:binary-phid", $phid); unset($need_upload[$key]); echo pht("Uploaded '%s' (%s).", $name, $type) . "\n"; } } } $upload_futures = array(); foreach ($need_upload as $key => $spec) { $upload_futures[$key] = $this->getConduit()->callMethod('file.upload', array('name' => $spec['name'], 'data_base64' => base64_encode($spec['data']))); } foreach (Futures($upload_futures)->limit(4) as $key => $future) { $type = $need_upload[$key]['type']; $change = $need_upload[$key]['change']; $name = $need_upload[$key]['name']; try { $phid = $future->resolve(); $change->setMetadata("{$type}:binary-phid", $phid); echo pht("Uploaded '%s' (%s).", $name, $type) . "\n"; } catch (Exception $e) { echo "Failed to upload {$type} binary '{$name}'.\n"; if (!phutil_console_confirm('Continue?', $default_no = false)) { throw new ArcanistUsageException('Aborted due to file upload failure. You can use --skip-binaries ' . 'to skip binary uploads.'); } } } echo pht("Upload complete.") . "\n"; }
/** * Do the best we can to prevent PEBKAC and id10t issues. */ private function sanityCheck(ArcanistBundle $bundle) { $repository_api = $this->getRepositoryAPI(); // Check to see if the bundle's base revision matches the working copy // base revision if ($repository_api->supportsLocalCommits()) { $bundle_base_rev = $bundle->getBaseRevision(); if (empty($bundle_base_rev)) { // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version < 2 // they don't have a base rev so just do nothing $commit_exists = true; } else { $commit_exists = $repository_api->hasLocalCommit($bundle_base_rev); } if (!$commit_exists) { // we have a problem...! lots of work because we need to ask // differential for revision information for these base revisions // to improve our error message. $bundle_base_rev_str = null; $source_base_rev = $repository_api->getWorkingCopyRevision(); $source_base_rev_str = null; if ($repository_api instanceof ArcanistGitAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT; } else { if ($repository_api instanceof ArcanistMercurialAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT; } else { $hash_type = null; } } if ($hash_type) { // 2 round trips because even though we could send off one query // we wouldn't be able to tell which revisions were for which hash $hash = array($hash_type, $bundle_base_rev); $bundle_revision = $this->loadRevisionFromHash($hash); $hash = array($hash_type, $source_base_rev); $source_revision = $this->loadRevisionFromHash($hash); if ($bundle_revision) { $bundle_base_rev_str = $bundle_base_rev . ' \\ D' . $bundle_revision['id']; } if ($source_revision) { $source_base_rev_str = $source_base_rev . ' \\ D' . $source_revision['id']; } } $bundle_base_rev_str = nonempty($bundle_base_rev_str, $bundle_base_rev); $source_base_rev_str = nonempty($source_base_rev_str, $source_base_rev); $ok = phutil_console_confirm(pht('This diff is against commit %s, but the commit is nowhere ' . 'in the working copy. Try to apply it against the current ' . 'working copy state? (%s)', $bundle_base_rev_str, $source_base_rev_str), $default_no = false); if (!$ok) { throw new ArcanistUserAbortException(); } } } }
public function run() { $working_copy = $this->getWorkingCopy(); $engine = $this->getArgument('engine'); if (!$engine) { $engine = $working_copy->getConfigFromAnySource('lint.engine'); if (!$engine) { throw new ArcanistNoEngineException("No lint engine configured for this project. Edit .arcconfig to " . "specify a lint engine."); } } $rev = $this->getArgument('rev'); $paths = $this->getArgument('paths'); if ($rev && $paths) { throw new ArcanistUsageException("Specify either --rev or paths."); } $should_lint_all = $this->getArgument('lintall'); if ($paths) { // NOTE: When the user specifies paths, we imply --lintall and show all // warnings for the paths in question. This is easier to deal with for // us and less confusing for users. $should_lint_all = true; } $paths = $this->selectPathsForWorkflow($paths, $rev); // is_subclass_of() doesn't autoload under HPHP. if (!class_exists($engine) || !is_subclass_of($engine, 'ArcanistLintEngine')) { throw new ArcanistUsageException("Configured lint engine '{$engine}' is not a subclass of " . "'ArcanistLintEngine'."); } $engine = newv($engine, array()); $this->engine = $engine; $engine->setWorkingCopy($working_copy); if ($this->getArgument('advice')) { $engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_ADVICE); } else { $engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX); } // Propagate information about which lines changed to the lint engine. // This is used so that the lint engine can drop warning messages // concerning lines that weren't in the change. $engine->setPaths($paths); if (!$should_lint_all) { foreach ($paths as $path) { // Note that getChangedLines() returns null to indicate that a file // is binary or a directory (i.e., changed lines are not relevant). $engine->setPathChangedLines($path, $this->getChangedLines($path, 'new')); } } // Enable possible async linting only for 'arc diff' not 'arc unit' if ($this->getParentWorkflow()) { $engine->setEnableAsyncLint(true); } else { $engine->setEnableAsyncLint(false); } $results = $engine->run(); // It'd be nice to just return a single result from the run method above // which contains both the lint messages and the postponed linters. // However, to maintain compatibility with existing lint subclasses, use // a separate method call to grab the postponed linters. $this->postponedLinters = $engine->getPostponedLinters(); if ($this->getArgument('never-apply-patches')) { $apply_patches = false; } else { $apply_patches = true; } if ($this->getArgument('apply-patches')) { $prompt_patches = false; } else { $prompt_patches = true; } if ($this->getArgument('amend-all')) { $this->shouldAmendChanges = true; $this->shouldAmendWithoutPrompt = true; } if ($this->getArgument('amend-autofixes')) { $prompt_autofix_patches = false; $this->shouldAmendChanges = true; $this->shouldAmendAutofixesWithoutPrompt = true; } else { $prompt_autofix_patches = true; } $wrote_to_disk = false; switch ($this->getArgument('output')) { case 'json': $renderer = new ArcanistLintJSONRenderer(); $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); break; case 'summary': $renderer = new ArcanistLintSummaryRenderer(); break; case 'compiler': $renderer = new ArcanistLintLikeCompilerRenderer(); $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); break; default: $renderer = new ArcanistLintConsoleRenderer(); $renderer->setShowAutofixPatches($prompt_autofix_patches); break; } $all_autofix = true; foreach ($results as $result) { $result_all_autofix = $result->isAllAutofix(); if (!$result->getMessages() && !$result_all_autofix) { continue; } if (!$result_all_autofix) { $all_autofix = false; } $lint_result = $renderer->renderLintResult($result); if ($lint_result) { echo $lint_result; } if ($apply_patches && $result->isPatchable()) { $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result); if ($prompt_patches && !($result_all_autofix && !$prompt_autofix_patches)) { $old_file = $result->getFilePathOnDisk(); if (!Filesystem::pathExists($old_file)) { $old_file = '/dev/null'; } $new_file = new TempFile(); $new = $patcher->getModifiedFileContent(); Filesystem::writeFile($new_file, $new); // TODO: Improve the behavior here, make it more like // difference_render(). passthru(csprintf("diff -u %s %s", $old_file, $new_file)); $prompt = phutil_console_format("Apply this patch to __%s__?", $result->getPath()); if (!phutil_console_confirm($prompt, $default_no = false)) { continue; } } $patcher->writePatchToDisk(); $wrote_to_disk = true; } } $repository_api = $this->getRepositoryAPI(); if ($wrote_to_disk && $repository_api instanceof ArcanistGitAPI && $this->shouldAmendChanges) { if ($this->shouldAmendWithoutPrompt || $this->shouldAmendAutofixesWithoutPrompt && $all_autofix) { echo phutil_console_format("<bg:yellow>** LINT NOTICE **</bg> Automatically amending HEAD " . "with lint patches.\n"); $amend = true; } else { $amend = phutil_console_confirm("Amend HEAD with lint patches?"); } if ($amend) { execx('(cd %s; git commit -a --amend -C HEAD)', $repository_api->getPath()); } else { throw new ArcanistUsageException("Sort out the lint changes that were applied to the working " . "copy and relint."); } } $unresolved = array(); $has_warnings = false; $has_errors = false; foreach ($results as $result) { foreach ($result->getMessages() as $message) { if (!$message->isPatchApplied()) { if ($message->isError()) { $has_errors = true; } else { if ($message->isWarning()) { $has_warnings = true; } } $unresolved[] = $message; } } } $this->unresolvedMessages = $unresolved; // Take the most severe lint message severity and use that // as the result code. if ($has_errors) { $result_code = self::RESULT_ERRORS; } else { if ($has_warnings) { $result_code = self::RESULT_WARNINGS; } else { if (!empty($this->postponedLinters)) { $result_code = self::RESULT_POSTPONED; } else { $result_code = self::RESULT_OKAY; } } } if (!$this->getParentWorkflow()) { if ($result_code == self::RESULT_OKAY) { echo $renderer->renderOkayResult(); } } return $result_code; }
/** * @task message */ private function validateCommitMessage(ArcanistDifferentialCommitMessage $message) { $reviewers = $message->getFieldValue('reviewerPHIDs'); if (!$reviewers) { $confirm = "You have not specified any reviewers. Continue anyway?"; if (!phutil_console_confirm($confirm)) { throw new ArcanistUsageException('Specify reviewers and retry.'); } } else { if (in_array($this->getUserPHID(), $reviewers)) { throw new ArcanistUsageException("You can not be a reviewer for your own revision."); } else { $users = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => $reviewers)); $untils = array(); foreach ($users as $user) { if (idx($user, 'currentStatus') == 'away') { $untils[] = $user['currentStatusUntil']; } } if (count($untils) == count($reviewers)) { $until = date('l, M j Y', min($untils)); $confirm = "All reviewers are away until {$until}. Continue anyway?"; if (!phutil_console_confirm($confirm)) { throw new ArcanistUsageException('Specify available reviewers and retry.'); } } } } }
public static function readUserConfigurationFile() { $user_config = array(); $user_config_path = self::getUserConfigurationFileLocation(); if (Filesystem::pathExists($user_config_path)) { if (!phutil_is_windows()) { $mode = fileperms($user_config_path); if (!$mode) { throw new Exception("Unable to get perms of '{$user_config_path}'!"); } if ($mode & 0177) { // Mode should allow only owner access. $prompt = "File permissions on your ~/.arcrc are too open. " . "Fix them by chmod'ing to 600?"; if (!phutil_console_confirm($prompt, $default_no = false)) { throw new ArcanistUsageException("Set ~/.arcrc to file mode 600."); } execx('chmod 600 %s', $user_config_path); // Drop the stat cache so we don't read the old permissions if // we end up here again. If we don't do this, we may prompt the user // to fix permissions multiple times. clearstatcache(); } } $user_config_data = Filesystem::readFile($user_config_path); $user_config = json_decode($user_config_data, true); if (!is_array($user_config)) { throw new ArcanistUsageException("Your '~/.arcrc' file is not a valid JSON file."); } } return $user_config; }
private function askForAdd(array $files) { if ($this->commitMode == self::COMMIT_DISABLE) { return false; } if ($this->commitMode == self::COMMIT_ENABLE) { return true; } $prompt = $this->getAskForAddPrompt($files); return phutil_console_confirm($prompt); }
public function run() { $is_finalize = $this->getArgument('finalize'); $conduit = $this->getConduit(); $revision_list = $this->getArgument('revision', array()); if (!$revision_list) { throw new ArcanistUsageException("close-revision requires a revision number."); } if (count($revision_list) != 1) { throw new ArcanistUsageException("close-revision requires exactly one revision."); } $revision_id = reset($revision_list); $revision_id = $this->normalizeRevisionID($revision_id); $revision = null; try { $revision = $conduit->callMethodSynchronous('differential.getrevision', array('revision_id' => $revision_id)); } catch (Exception $ex) { if (!$is_finalize) { throw new ArcanistUsageException("Revision D{$revision_id} does not exist."); } } $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED; $status_closed = ArcanistDifferentialRevisionStatus::CLOSED; if (!$is_finalize && $revision['status'] != $status_accepted) { throw new ArcanistUsageException("Revision D{$revision_id} can not be closed. You can only close " . "revisions which have been 'accepted'."); } if ($revision) { if (!$is_finalize && $revision['authorPHID'] != $this->getUserPHID()) { $prompt = "You are not the author of revision D{$revision_id}, " . 'are you sure you want to close it?'; if (!phutil_console_confirm($prompt)) { throw new ArcanistUserAbortException(); } } $actually_close = true; if ($is_finalize) { $project_info = $this->getProjectInfo(); if (idx($project_info, 'tracked') || $revision['status'] != $status_accepted) { $actually_close = false; } } if ($actually_close) { $revision_name = $revision['title']; echo "Closing revision D{$revision_id} '{$revision_name}'...\n"; $conduit->callMethodSynchronous('differential.close', array('revisionID' => $revision_id)); } } $status = $revision['status']; if ($status == $status_accepted || $status == $status_closed) { // If this has already been attached to commits, don't show the // "you can push this commit" message since we know it's been pushed // already. $is_finalized = empty($revision['commits']); } else { $is_finalized = false; } if (!$this->getArgument('quiet')) { if ($is_finalized) { $message = $this->getRepositoryAPI()->getFinalizedRevisionMessage(); echo phutil_console_wrap($message) . "\n"; } else { echo "Done.\n"; } } return 0; }
private final function doUpgradeSchemata($apply_only, $no_quickstart, $init_only) { $api = $this->getAPI(); $applied = $this->getApi()->getAppliedPatches(); if ($applied === null) { if ($this->dryRun) { echo pht("DRYRUN: Patch metadata storage doesn't exist yet, " . "it would be created.\n"); return 0; } if ($apply_only) { throw new PhutilArgumentUsageException(pht('Storage has not been initialized yet, you must initialize ' . 'storage before selectively applying patches.')); return 1; } $legacy = $api->getLegacyPatches($this->patches); if ($legacy || $no_quickstart || $init_only) { // If we have legacy patches, we can't quickstart. $api->createDatabase('meta_data'); $api->createTable('meta_data', 'patch_status', array('patch VARCHAR(255) NOT NULL PRIMARY KEY COLLATE utf8_general_ci', 'applied INT UNSIGNED NOT NULL')); foreach ($legacy as $patch) { $api->markPatchApplied($patch); } } else { echo pht('Loading quickstart template...') . "\n"; $root = dirname(phutil_get_library_root('phabricator')); $sql = $root . '/resources/sql/quickstart.sql'; $api->applyPatchSQL($sql); } } if ($init_only) { echo pht('Storage initialized.') . "\n"; return 0; } $applied = $api->getAppliedPatches(); $applied = array_fuse($applied); $skip_mark = false; if ($apply_only) { if (isset($applied[$apply_only])) { unset($applied[$apply_only]); $skip_mark = true; if (!$this->force && !$this->dryRun) { echo phutil_console_wrap(pht("Patch '%s' has already been applied. Are you sure you want " . "to apply it again? This may put your storage in a state " . "that the upgrade scripts can not automatically manage.", $apply_only)); if (!phutil_console_confirm(pht('Apply patch again?'))) { echo pht('Cancelled.') . "\n"; return 1; } } } } while (true) { $applied_something = false; foreach ($this->patches as $key => $patch) { if (isset($applied[$key])) { unset($this->patches[$key]); continue; } if ($apply_only && $apply_only != $key) { unset($this->patches[$key]); continue; } $can_apply = true; foreach ($patch->getAfter() as $after) { if (empty($applied[$after])) { if ($apply_only) { echo pht("Unable to apply patch '%s' because it depends " . "on patch '%s', which has not been applied.\n", $apply_only, $after); return 1; } $can_apply = false; break; } } if (!$can_apply) { continue; } $applied_something = true; if ($this->dryRun) { echo pht("DRYRUN: Would apply patch '%s'.", $key) . "\n"; } else { echo pht("Applying patch '%s'...", $key) . "\n"; $t_begin = microtime(true); $api->applyPatch($patch); $t_end = microtime(true); if (!$skip_mark) { $api->markPatchApplied($key, $t_end - $t_begin); } } unset($this->patches[$key]); $applied[$key] = true; } if (!$applied_something) { if (count($this->patches)) { throw new Exception(pht('Some patches could not be applied: %s', implode(', ', array_keys($this->patches)))); } else { if (!$this->dryRun && !$apply_only) { echo pht("Storage is up to date. Use '%s' for details.", 'storage status') . "\n"; } } break; } } }
echo "\n\n" . pht('ACCOUNT SUMMARY') . "\n\n"; $tpl = "%12s %-30s %-30s\n"; printf($tpl, null, pht('OLD VALUE'), pht('NEW VALUE')); printf($tpl, pht('Username'), $original->getUsername(), $user->getUsername()); printf($tpl, pht('Real Name'), $original->getRealName(), $user->getRealName()); if ($is_new) { printf($tpl, pht('Email'), '', $create_email); } printf($tpl, pht('Password'), null, $changed_pass !== false ? pht('Updated') : pht('Unchanged')); printf($tpl, pht('Bot'), $original->getIsSystemAgent() ? 'Y' : 'N', $set_system_agent ? 'Y' : 'N'); if ($verify_email) { printf($tpl, pht('Verify Email'), $verify_email->getIsVerified() ? 'Y' : 'N', $set_verified ? 'Y' : 'N'); } printf($tpl, pht('Admin'), $original->getIsAdmin() ? 'Y' : 'N', $set_admin ? 'Y' : 'N'); echo "\n"; if (!phutil_console_confirm(pht('Save these changes?'), $default_no = false)) { echo pht('Cancelled.') . "\n"; exit(1); } $user->openTransaction(); $editor = new PhabricatorUserEditor(); // TODO: This is wrong, but we have a chicken-and-egg problem when you use // this script to create the first user. $editor->setActor($user); if ($is_new) { $email = id(new PhabricatorUserEmail())->setAddress($create_email)->setIsVerified(1); // Unconditionally approve new accounts created from the CLI. $user->setIsApproved(1); $editor->createNewUser($user, $email); } else { if ($verify_email) {
* See the License for the specific language governing permissions and * limitations under the License. */ $root = dirname(dirname(dirname(__FILE__))); require_once $root . '/scripts/__init_script__.php'; require_once $root . '/scripts/__init_env__.php'; phutil_require_module('phutil', 'console'); phutil_require_module('phutil', 'future/exec'); if (empty($argv[1])) { echo "usage: test_connection.php <repository_callsign>\n"; exit(1); } echo phutil_console_wrap(phutil_console_format('This script will test that you have configured valid credentials for ' . 'access to a repository, so the Phabricator daemons can pull from it. ' . 'You should run this as the **same user you will run the daemons as**, ' . 'from the **same machine they will run from**. Doing this will help ' . 'detect various problems with your configuration, such as SSH issues.')); list($whoami) = execx('whoami'); $whoami = trim($whoami); $ok = phutil_console_confirm("Do you want to continue as '{$whoami}'?"); if (!$ok) { die(1); } $callsign = $argv[1]; echo "Loading '{$callsign}' repository...\n"; $repository = id(new PhabricatorRepository())->loadOneWhere('callsign = %s', $argv[1]); if (!$repository) { throw new Exception("No such repository exists!"); } $vcs = $repository->getVersionControlSystem(); PhutilServiceProfiler::installEchoListener(); echo "Trying to connect to the remote...\n"; switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $err = $repository->passthruRemoteCommand('--limit 1 log %s', $repository->getRemoteURI());
/** * Do the best we can to prevent PEBKAC and id10t issues. */ private function sanityCheck(ArcanistBundle $bundle) { $repository_api = $this->getRepositoryAPI(); // Require clean working copy $this->requireCleanWorkingCopy(); // Check to see if the bundle's project id matches the working copy // project id $bundle_project_id = $bundle->getProjectID(); $working_copy_project_id = $this->getWorkingCopy()->getProjectID(); if (empty($bundle_project_id)) { // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version = 0 // they don't come with a project id so just do nothing } else { if ($bundle_project_id != $working_copy_project_id) { if ($working_copy_project_id) { $issue = "This patch is for the '{$bundle_project_id}' project, but the " . "working copy belongs to the '{$working_copy_project_id}' project."; } else { $issue = "This patch is for the '{$bundle_project_id}' project, but the " . "working copy does not have an '.arcconfig' file to identify which " . "project it belongs to."; } $ok = phutil_console_confirm("{$issue} Still try to apply the patch?", $default_no = false); if (!$ok) { throw new ArcanistUserAbortException(); } } } // Check to see if the bundle's base revision matches the working copy // base revision if ($repository_api->supportsLocalCommits()) { $bundle_base_rev = $bundle->getBaseRevision(); if (empty($bundle_base_rev)) { // this means $source is SOURCE_PATCH || SOURCE_BUNDLE w/ $version < 2 // they don't have a base rev so just do nothing $commit_exists = true; } else { $commit_exists = $repository_api->hasLocalCommit($bundle_base_rev); } if (!$commit_exists) { // we have a problem...! lots of work because we need to ask // differential for revision information for these base revisions // to improve our error message. $bundle_base_rev_str = null; $source_base_rev = $repository_api->getWorkingCopyRevision(); $source_base_rev_str = null; if ($repository_api instanceof ArcanistGitAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_GIT_COMMIT; } else { if ($repository_api instanceof ArcanistMercurialAPI) { $hash_type = ArcanistDifferentialRevisionHash::HASH_MERCURIAL_COMMIT; } else { $hash_type = null; } } if ($hash_type) { // 2 round trips because even though we could send off one query // we wouldn't be able to tell which revisions were for which hash $hash = array($hash_type, $bundle_base_rev); $bundle_revision = $this->loadRevisionFromHash($hash); $hash = array($hash_type, $source_base_rev); $source_revision = $this->loadRevisionFromHash($hash); if ($bundle_revision) { $bundle_base_rev_str = $bundle_base_rev . ' \\ D' . $bundle_revision['id']; } if ($source_revision) { $source_base_rev_str = $source_base_rev . ' \\ D' . $source_revision['id']; } } $bundle_base_rev_str = nonempty($bundle_base_rev_str, $bundle_base_rev); $source_base_rev_str = nonempty($source_base_rev_str, $source_base_rev); $ok = phutil_console_confirm("This diff is against commit {$bundle_base_rev_str}, but the " . "commit is nowhere in the working copy. Try to apply it against " . "the current working copy state? ({$source_base_rev_str})", $default_no = false); if (!$ok) { throw new ArcanistUserAbortException(); } } } // TODO -- more sanity checks here }
private function removeAlias(array $aliases, $alias) { if (empty($aliases[$alias])) { echo tsprintf("%s\n", pht('No alias "%s" to remove.', $alias)); return; } $command = implode(' ', $aliases[$alias]); if (self::isShellCommandAlias($command)) { echo tsprintf("%s\n", pht('"%s" is currently aliased to shell command "%s".', "arc {$alias}", substr($command, 1))); } else { echo tsprintf("%s\n", pht('"%s" is currently aliased to "%s".', "arc {$alias}", "arc {$command}")); } $ok = phutil_console_confirm(pht('Delete this alias?')); if (!$ok) { throw new ArcanistUserAbortException(); } unset($aliases[$alias]); $this->writeAliases($aliases); echo tsprintf("%s\n", pht('Removed alias "%s".', "arc {$alias}")); }
spurious audit requests during import. EOSYNOPSIS ); $args->parseStandardArguments(); $args->parse(array(array('name' => 'more', 'wildcard' => true))); $more = $args->getArg('more'); if (count($more) !== 1) { $args->printHelpAndExit(); } $callsign = reset($more); $repository = id(new PhabricatorRepository())->loadOneWhere('callsign = %s', $callsign); if (!$repository) { throw new Exception("No repository exists with callsign '{$callsign}'!"); } $ok = phutil_console_confirm('This will reset all open audit requests ("Audit Required" or "Concern ' . 'Raised") for commits in this repository to "Audit Not Required". This ' . 'operation destroys information and can not be undone! Are you sure ' . 'you want to proceed?'); if (!$ok) { echo "OK, aborting.\n"; die(1); } echo "Loading commits...\n"; $all_commits = id(new PhabricatorRepositoryCommit())->loadAllWhere('repositoryID = %d', $repository->getID()); echo "Clearing audit requests...\n"; foreach ($all_commits as $commit) { $query = id(new PhabricatorAuditQuery())->withStatus(PhabricatorAuditQuery::STATUS_OPEN)->withCommitPHIDs(array($commit->getPHID())); $requests = $query->execute(); echo "Clearing " . $commit->getPHID() . "... "; if (!$requests) { echo "nothing to do.\n"; continue; } else {