public function renderLintResult(ArcanistLintResult $result)
 {
     $messages = $result->getMessages();
     $path = $result->getPath();
     $lines = explode("\n", $result->getData());
     $text = array();
     foreach ($messages as $message) {
         if (!$this->showAutofixPatches && $message->isAutofix()) {
             continue;
         }
         if ($message->isError()) {
             $color = 'red';
         } else {
             $color = 'yellow';
         }
         $severity = ArcanistLintSeverity::getStringForSeverity($message->getSeverity());
         $code = $message->getCode();
         $name = $message->getName();
         $description = phutil_console_wrap($message->getDescription(), 4);
         $text[] = phutil_console_format("  **<bg:{$color}> %s </bg>** (%s) __%s__\n%s\n", $severity, $code, $name, $description);
         if ($message->hasFileContext()) {
             $text[] = $this->renderContext($message, $lines);
         }
     }
     if ($text) {
         $prefix = phutil_console_format("**>>>** Lint for __%s__:\n\n\n", $path);
         return $prefix . implode("\n", $text);
     } else {
         return null;
     }
 }
Пример #2
0
/**
 * @group console
 */
function phutil_console_prompt($prompt, $history = '')
{
    echo "\n\n";
    $prompt = phutil_console_wrap($prompt . ' ', 4);
    try {
        phutil_console_require_tty();
    } catch (PhutilConsoleStdinNotInteractiveException $ex) {
        // Throw after echoing the prompt so the user has some idea what happened.
        echo $prompt;
        throw $ex;
    }
    $use_history = true;
    if ($history == '') {
        $use_history = false;
    } else {
        // Test if bash is available by seeing if it can run `true`.
        list($err) = exec_manual('bash -c %s', 'true');
        if ($err) {
            $use_history = false;
        }
    }
    if (!$use_history) {
        echo $prompt;
        $response = fgets(STDIN);
    } else {
        // There's around 0% chance that readline() is available directly in PHP,
        // so we're using bash/read/history instead.
        $command = csprintf('bash -c %s', csprintf('history -r %s 2>/dev/null; ' . 'read -e -p %s; ' . 'echo "$REPLY"; ' . 'history -s "$REPLY" 2>/dev/null; ' . 'history -w %s 2>/dev/null', $history, $prompt, $history));
        // execx() doesn't work with input, phutil_passthru() doesn't return output.
        $response = shell_exec($command);
    }
    return rtrim($response, "\r\n");
}
 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 run()
 {
     $roots = array();
     $roots['libphutil'] = dirname(phutil_get_library_root('phutil'));
     $roots['arcanist'] = dirname(phutil_get_library_root('arcanist'));
     foreach ($roots as $lib => $root) {
         echo "Upgrading {$lib}...\n";
         if (!Filesystem::pathExists($root . '/.git')) {
             throw new ArcanistUsageException("{$lib} must be in its git working copy to be automatically " . "upgraded. This copy of {$lib} (in '{$root}') is not in a git " . "working copy.");
         }
         $working_copy = ArcanistWorkingCopyIdentity::newFromPath($root);
         $configuration_manager = clone $this->getConfigurationManager();
         $configuration_manager->setWorkingCopyIdentity($working_copy);
         $repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager($configuration_manager);
         $this->setRepositoryAPI($repository_api);
         // Require no local changes.
         $this->requireCleanWorkingCopy();
         // Require the library be on master.
         $branch_name = $repository_api->getBranchName();
         if ($branch_name != 'master') {
             throw new ArcanistUsageException("{$lib} must be on branch 'master' to be automatically upgraded. " . "This copy of {$lib} (in '{$root}') is on branch '{$branch_name}'.");
         }
         chdir($root);
         try {
             phutil_passthru('git pull --rebase');
         } catch (Exception $ex) {
             phutil_passthru('git rebase --abort');
             throw $ex;
         }
     }
     echo phutil_console_wrap(phutil_console_format("**Updated!** Your copy of arc is now up to date.\n"));
     return 0;
 }
 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 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));
 }
Пример #7
0
 public function run()
 {
     $roots = array();
     $roots['libphutil'] = dirname(phutil_get_library_root('phutil'));
     $roots['arcanist'] = dirname(phutil_get_library_root('arcanist'));
     foreach ($roots as $lib => $root) {
         echo "Upgrading {$lib}...\n";
         if (!Filesystem::pathExists($root . '/.git')) {
             throw new ArcanistUsageException("{$lib} must be in its git working copy to be automatically " . "upgraded. This copy of {$lib} (in '{$root}') is not in a git " . "working copy.");
         }
         $working_copy = ArcanistWorkingCopyIdentity::newFromPath($root);
         $repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($working_copy);
         // Force the range to HEAD^..HEAD, which is meaningless but keeps us
         // from triggering "base" rules or other commit range resolution rules
         // that might prompt the user when we pull the working copy status.
         $repository_api->setRelativeCommit('HEAD^');
         $this->setRepositoryAPI($repository_api);
         // Require no local changes.
         $this->requireCleanWorkingCopy();
         // Require the library be on master.
         $branch_name = $repository_api->getBranchName();
         if ($branch_name != 'master') {
             throw new ArcanistUsageException("{$lib} must be on branch 'master' to be automatically upgraded. " . "This copy of {$lib} (in '{$root}') is on branch '{$branch_name}'.");
         }
         chdir($root);
         try {
             phutil_passthru('git pull --rebase');
         } catch (Exception $ex) {
             phutil_passthru('git rebase --abort');
             throw $ex;
         }
     }
     echo phutil_console_wrap(phutil_console_format("**Updated!** Your copy of arc is now up to date.\n"));
     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)
 {
     $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;
 }
Пример #10
0
    public function testWrapIndent()
    {
        $turtles = <<<EOTURTLES
                    turtle turtle turtle turtle turtle turtle turtle turtle
                    turtle turtle turtle turtle turtle turtle turtle turtle
                    turtle turtle turtle turtle
EOTURTLES;
        $this->assertEqual($turtles, phutil_console_wrap(rtrim(str_repeat('turtle ', 20)), $indent = 20));
    }
Пример #11
0
/**
 * @group console
 */
function phutil_console_prompt($prompt)
{
    $prompt = "\n\n    " . $prompt . " ";
    $prompt = phutil_console_wrap($prompt, 4);
    echo $prompt;
    // Require after echoing the prompt so the user has some idea what happened
    // if this throws.
    phutil_console_require_tty();
    $response = fgets(STDIN);
    return rtrim($response, "\n");
}
 protected function drawView()
 {
     $output = array();
     foreach ($this->getItems() as $item) {
         if ($this->wrap) {
             $item = phutil_console_wrap($item, 8);
         }
         $item = '      - ' . $item;
         $output[] = $item;
     }
     return $this->drawLines($output);
 }
 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)
 {
     $api = $this->getAPI();
     $patches = $this->getPatches();
     $applied = $api->getAppliedPatches();
     if ($applied === null) {
         $namespace = $api->getNamespace();
         echo phutil_console_wrap(phutil_console_format("**No Storage**: There is no database storage initialized in this " . "storage namespace ('{$namespace}'). Use '**storage upgrade**' to " . "initialize storage.\n"));
         return 1;
     }
     $databases = $api->getDatabaseList($patches);
     list($host, $port) = $this->getBareHostAndPort($api->getHost());
     $flag_password = $api->getPassword() ? csprintf('-p %s', $api->getPassword()) : '';
     $flag_port = $port ? csprintf('--port %d', $port) : '';
     return phutil_passthru('mysqldump --default-character-set=utf8 ' . '-u %s %C -h %s %C --databases %Ls', $api->getUser(), $flag_password, $host, $flag_port, $databases);
 }
 public function renderHelpForArcanist()
 {
     $text = '';
     $levels = $this->getLevels();
     $default = $this->getDefaultLevel();
     foreach ($levels as $level) {
         $name = $this->getNameForLevel($level);
         $description = $this->getDescriptionForLevel($level);
         $default_marker = ' ';
         if ($level === $default) {
             $default_marker = '*';
         }
         $text .= "    {$default_marker} **{$name}**\n";
         $text .= phutil_console_wrap($description . "\n", 8);
     }
     return $text;
 }
 protected function drawView()
 {
     $output = array();
     foreach ($this->items as $spec) {
         $type = $spec['type'];
         $item = $spec['item'];
         switch ($type) {
             case 'paragraph':
                 $item = phutil_console_wrap($item) . "\n";
                 break;
             case 'list':
                 $item = $item;
                 break;
         }
         $output[] = $item;
     }
     return $this->drawLines($output);
 }
Пример #17
0
 protected function drawView()
 {
     $indent_depth = 6;
     $indent_string = str_repeat(' ', $indent_depth);
     if ($this->bullet !== null) {
         $bullet = $this->bullet . ' ';
         $indent_depth = $indent_depth + phutil_utf8_console_strlen($bullet);
     } else {
         $bullet = '';
     }
     $output = array();
     foreach ($this->getItems() as $item) {
         if ($this->wrap) {
             $item = phutil_console_wrap($item, $indent_depth);
         }
         $output[] = $indent_string . $bullet . $item;
     }
     return $this->drawLines($output);
 }
 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 renderLintResult(ArcanistLintResult $result)
 {
     $messages = $result->getMessages();
     $path = $result->getPath();
     $lines = explode("\n", $result->getData());
     $text = array();
     foreach ($messages as $message) {
         if (!$this->showAutofixPatches && $message->isAutofix()) {
             continue;
         }
         if ($message->isError()) {
             $color = 'red';
         } else {
             $color = 'yellow';
         }
         $severity = ArcanistLintSeverity::getStringForSeverity($message->getSeverity());
         $code = $message->getCode();
         $name = $message->getName();
         $description = $message->getDescription();
         if ($message->getOtherLocations()) {
             $locations = array();
             foreach ($message->getOtherLocations() as $location) {
                 $locations[] = idx($location, 'path', $path) . (!empty($location['line']) ? ":{$location['line']}" : '');
             }
             $description .= "\n" . pht('Other locations: %s', implode(', ', $locations));
         }
         $text[] = phutil_console_format("  **<bg:{$color}> %s </bg>** (%s) __%s__\n%s\n", $severity, $code, $name, phutil_console_wrap($description, 4));
         if ($message->hasFileContext()) {
             $text[] = $this->renderContext($message, $lines);
         }
     }
     if ($text) {
         $prefix = phutil_console_format("**>>>** %s\n\n\n", pht('Lint for %s:', phutil_console_format('__%s__', $path)));
         return $prefix . implode("\n", $text);
     } else {
         return null;
     }
 }
Пример #21
0
 protected function buildBaseCommit($symbolic_commit)
 {
     if ($symbolic_commit !== null) {
         if ($symbolic_commit == ArcanistGitAPI::GIT_MAGIC_ROOT_COMMIT) {
             $this->setBaseCommitExplanation('you explicitly specified the empty tree.');
             return $symbolic_commit;
         }
         list($err, $merge_base) = $this->execManualLocal('merge-base %s %s', $symbolic_commit, $this->getHeadCommit());
         if ($err) {
             throw new ArcanistUsageException("Unable to find any git commit named '{$symbolic_commit}' in " . "this repository.");
         }
         if ($this->symbolicHeadCommit === null) {
             $this->setBaseCommitExplanation("it is the merge-base of the explicitly specified base commit " . "'{$symbolic_commit}' and HEAD.");
         } else {
             $this->setBaseCommitExplanation("it is the merge-base of the explicitly specified base commit " . "'{$symbolic_commit}' and the explicitly specified head " . "commit '{$this->symbolicHeadCommit}'.");
         }
         return trim($merge_base);
     }
     // Detect zero-commit or one-commit repositories. There is only one
     // relative-commit value that makes any sense in these repositories: the
     // empty tree.
     list($err) = $this->execManualLocal('rev-parse --verify HEAD^');
     if ($err) {
         list($err) = $this->execManualLocal('rev-parse --verify HEAD');
         if ($err) {
             $this->repositoryHasNoCommits = true;
         }
         if ($this->repositoryHasNoCommits) {
             $this->setBaseCommitExplanation('the repository has no commits.');
         } else {
             $this->setBaseCommitExplanation('the repository has only one commit.');
         }
         return self::GIT_MAGIC_ROOT_COMMIT;
     }
     if ($this->getBaseCommitArgumentRules() || $this->getConfigurationManager()->getConfigFromAnySource('base')) {
         $base = $this->resolveBaseCommit();
         if (!$base) {
             throw new ArcanistUsageException("None of the rules in your 'base' configuration matched a valid " . "commit. Adjust rules or specify which commit you want to use " . "explicitly.");
         }
         return $base;
     }
     $do_write = false;
     $default_relative = null;
     $working_copy = $this->getWorkingCopyIdentity();
     if ($working_copy) {
         $default_relative = $working_copy->getProjectConfig('git.default-relative-commit');
         $this->setBaseCommitExplanation("it is the merge-base of '{$default_relative}' and HEAD, as " . "specified in 'git.default-relative-commit' in '.arcconfig'. This " . "setting overrides other settings.");
     }
     if (!$default_relative) {
         list($err, $upstream) = $this->execManualLocal('rev-parse --abbrev-ref --symbolic-full-name %s', '@{upstream}');
         if (!$err) {
             $default_relative = trim($upstream);
             $this->setBaseCommitExplanation("it is the merge-base of '{$default_relative}' (the Git upstream " . "of the current branch) HEAD.");
         }
     }
     if (!$default_relative) {
         $default_relative = $this->readScratchFile('default-relative-commit');
         $default_relative = trim($default_relative);
         if ($default_relative) {
             $this->setBaseCommitExplanation("it is the merge-base of '{$default_relative}' and HEAD, as " . "specified in '.git/arc/default-relative-commit'.");
         }
     }
     if (!$default_relative) {
         // TODO: Remove the history lesson soon.
         echo phutil_console_format("<bg:green>** Select a Default Commit Range **</bg>\n\n");
         echo phutil_console_wrap("You're running a command which operates on a range of revisions " . "(usually, from some revision to HEAD) but have not specified the " . "revision that should determine the start of the range.\n\n" . "Previously, arc assumed you meant 'HEAD^' when you did not specify " . "a start revision, but this behavior does not make much sense in " . "most workflows outside of Facebook's historic git-svn workflow.\n\n" . "arc no longer assumes 'HEAD^'. You must specify a relative commit " . "explicitly when you invoke a command (e.g., `arc diff HEAD^`, not " . "just `arc diff`) or select a default for this working copy.\n\n" . "In most cases, the best default is 'origin/master'. You can also " . "select 'HEAD^' to preserve the old behavior, or some other remote " . "or branch. But you almost certainly want to select " . "'origin/master'.\n\n" . "(Technically: the merge-base of the selected revision and HEAD is " . "used to determine the start of the commit range.)");
         $prompt = 'What default do you want to use? [origin/master]';
         $default = phutil_console_prompt($prompt);
         if (!strlen(trim($default))) {
             $default = 'origin/master';
         }
         $default_relative = $default;
         $do_write = true;
     }
     list($object_type) = $this->execxLocal('cat-file -t %s', $default_relative);
     if (trim($object_type) !== 'commit') {
         throw new Exception("Relative commit '{$default_relative}' is not the name of a commit!");
     }
     if ($do_write) {
         // Don't perform this write until we've verified that the object is a
         // valid commit name.
         $this->writeScratchFile('default-relative-commit', $default_relative);
         $this->setBaseCommitExplanation("it is the merge-base of '{$default_relative}' and HEAD, as you " . "just specified.");
     }
     list($merge_base) = $this->execxLocal('merge-base %s HEAD', $default_relative);
     return trim($merge_base);
 }
Пример #22
0
 public function run()
 {
     $console = PhutilConsole::getConsole();
     $linters = id(new PhutilClassMapQuery())->setAncestorClass('ArcanistLinter')->execute();
     try {
         $built = $this->newLintEngine()->buildLinters();
     } catch (ArcanistNoEngineException $ex) {
         $built = array();
     }
     $linter_info = $this->getLintersInfo($linters, $built);
     $status_map = $this->getStatusMap();
     $pad = '    ';
     $color_map = array('configured' => 'green', 'available' => 'yellow', 'error' => 'red');
     $is_verbose = $this->getArgument('verbose');
     $exact = $this->getArgument('exact');
     $search_terms = $this->getArgument('search');
     if ($exact && $search_terms) {
         throw new ArcanistUsageException('Specify either search expression or exact name');
     }
     if ($exact) {
         $linter_info = $this->findExactNames($linter_info, $exact);
         if (!$linter_info) {
             $console->writeOut("%s\n", pht('No match found. Try `%s %s` to search for a linter.', 'arc linters --search', $exact[0]));
             return;
         }
         $is_verbose = true;
     }
     if ($search_terms) {
         $linter_info = $this->filterByNames($linter_info, $search_terms);
     }
     foreach ($linter_info as $key => $linter) {
         $status = $linter['status'];
         $color = $color_map[$status];
         $text = $status_map[$status];
         $print_tail = false;
         $console->writeOut("<bg:" . $color . ">** %s **</bg> **%s** (%s)\n", $text, nonempty($linter['name'], '-'), $linter['short']);
         if ($linter['exception']) {
             $console->writeOut("\n%s**%s**\n%s\n", $pad, get_class($linter['exception']), phutil_console_wrap($linter['exception']->getMessage(), strlen($pad)));
             $print_tail = true;
         }
         if ($is_verbose) {
             $version = $linter['version'];
             $uri = $linter['uri'];
             if ($version || $uri) {
                 $console->writeOut("\n");
                 $print_tail = true;
             }
             if ($version) {
                 $console->writeOut("%s%s **%s**\n", $pad, pht('Version'), $version);
             }
             if ($uri) {
                 $console->writeOut("%s__%s__\n", $pad, $linter['uri']);
             }
             $description = $linter['description'];
             if ($description) {
                 $console->writeOut("\n%s\n", phutil_console_wrap($linter['description'], strlen($pad)));
                 $print_tail = true;
             }
             $options = $linter['options'];
             if ($options) {
                 $console->writeOut("\n%s**%s**\n\n", $pad, pht('Configuration Options'));
                 $last_option = last_key($options);
                 foreach ($options as $option => $option_spec) {
                     $console->writeOut("%s__%s__ (%s)\n", $pad, $option, $option_spec['type']);
                     $console->writeOut("%s\n", phutil_console_wrap($option_spec['help'], strlen($pad) + 2));
                     if ($option != $last_option) {
                         $console->writeOut("\n");
                     }
                 }
                 $print_tail = true;
             }
             if ($print_tail) {
                 $console->writeOut("\n");
             }
         }
     }
     if (!$is_verbose) {
         $console->writeOut("%s\n", pht('(Run `%s` for more details.)', 'arc linters --verbose'));
     }
 }
Пример #23
0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * 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";
 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;
         }
     }
 }
Пример #25
0
phutil_require_module('phabricator', 'infrastructure/setup/sql');
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();
}
Пример #26
0
 /**
  * @task message
  */
 private function getCommitMessageFromUser()
 {
     $conduit = $this->getConduit();
     $template = null;
     if (!$this->getArgument('verbatim')) {
         $saved = $this->readScratchFile('create-message');
         if ($saved) {
             $where = $this->getReadableScratchFilePath('create-message');
             $preview = explode("\n", $saved);
             $preview = array_shift($preview);
             $preview = trim($preview);
             $preview = id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(64)->truncateString($preview);
             if ($preview) {
                 $preview = pht('Message begins:') . "\n\n       {$preview}\n\n";
             } else {
                 $preview = null;
             }
             echo pht("You have a saved revision message in '%s'.\n%s" . "You can use this message, or discard it.", $where, $preview);
             $use = phutil_console_confirm(pht('Do you want to use this message?'), $default_no = false);
             if ($use) {
                 $template = $saved;
             } else {
                 $this->removeScratchFile('create-message');
             }
         }
     }
     $template_is_default = false;
     $notes = array();
     $included = array();
     list($fields, $notes, $included_commits) = $this->getDefaultCreateFields();
     if ($template) {
         $fields = array();
         $notes = array();
     } else {
         if (!$fields) {
             $template_is_default = true;
         }
         if ($notes) {
             $commit = head($this->getRepositoryAPI()->getLocalCommitInformation());
             $template = $commit['message'];
         } else {
             $template = $conduit->callMethodSynchronous('differential.getcommitmessage', array('revision_id' => null, 'edit' => 'create', 'fields' => $fields));
         }
     }
     $old_message = $template;
     $included = array();
     if ($included_commits) {
         foreach ($included_commits as $commit) {
             $included[] = '        ' . $commit;
         }
         if (!$this->isRawDiffSource()) {
             $message = pht('Included commits in branch %s:', $this->getRepositoryAPI()->getBranchName());
         } else {
             $message = pht('Included commits:');
         }
         $included = array_merge(array('', $message, ''), $included);
     }
     $issues = array_merge(array(pht('NEW DIFFERENTIAL REVISION'), pht('Describe the changes in this new revision.')), $included, array('', pht('arc could not identify any existing revision in your working copy.'), pht('If you intended to update an existing revision, use:'), '', '  $ arc diff --update <revision>'));
     if ($notes) {
         $issues = array_merge($issues, array(''), $notes);
     }
     $done = false;
     $first = true;
     while (!$done) {
         $template = rtrim($template, "\r\n") . "\n\n";
         foreach ($issues as $issue) {
             $template .= rtrim('# ' . $issue) . "\n";
         }
         $template .= "\n";
         if ($first && $this->getArgument('verbatim') && !$template_is_default) {
             $new_template = $template;
         } else {
             $new_template = $this->newInteractiveEditor($template)->setName('new-commit')->editInteractively();
         }
         $first = false;
         if ($template_is_default && $new_template == $template) {
             throw new ArcanistUsageException(pht('Template not edited.'));
         }
         $template = ArcanistCommentRemover::removeComments($new_template);
         // With --raw-command, we may not have a repository API.
         if ($this->hasRepositoryAPI()) {
             $repository_api = $this->getRepositoryAPI();
             // special check for whether to amend here. optimizes a common git
             // workflow. we can't do this for mercurial because the mq extension
             // is popular and incompatible with hg commit --amend ; see T2011.
             $should_amend = count($included_commits) == 1 && $repository_api instanceof ArcanistGitAPI && $this->shouldAmend();
         } else {
             $should_amend = false;
         }
         if ($should_amend) {
             $wrote = rtrim($old_message) != rtrim($template);
             if ($wrote) {
                 $repository_api->amendCommit($template);
                 $where = pht('commit message');
             }
         } else {
             $wrote = $this->writeScratchFile('create-message', $template);
             $where = "'" . $this->getReadableScratchFilePath('create-message') . "'";
         }
         try {
             $message = ArcanistDifferentialCommitMessage::newFromRawCorpus($template);
             $message->pullDataFromConduit($conduit);
             $this->validateCommitMessage($message);
             $done = true;
         } catch (ArcanistDifferentialCommitMessageParserException $ex) {
             echo pht('Commit message has errors:') . "\n\n";
             $issues = array(pht('Resolve these errors:'));
             foreach ($ex->getParserErrors() as $error) {
                 echo phutil_console_wrap("- " . $error . "\n", 6);
                 $issues[] = '  - ' . $error;
             }
             echo "\n";
             echo pht('You must resolve these errors to continue.');
             $again = phutil_console_confirm(pht('Do you want to edit the message?'), $default_no = false);
             if ($again) {
                 // Keep going.
             } else {
                 $saved = null;
                 if ($wrote) {
                     $saved = pht('A copy was saved to %s.', $where);
                 }
                 throw new ArcanistUsageException(pht('Message has unresolved errors.') . " {$saved}");
             }
         } catch (Exception $ex) {
             if ($wrote) {
                 echo phutil_console_wrap(pht('(Message saved to %s.)', $where) . "\n");
             }
             throw $ex;
         }
     }
     return $message;
 }
Пример #27
0
ini_set('display_errors', 1);
$include_path = ini_get('include_path');
ini_set('include_path', $include_path . PATH_SEPARATOR . dirname(__FILE__) . '/../../');
@(include_once 'libphutil/scripts/__init_script__.php');
if (!@constant('__LIBPHUTIL__')) {
    echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to " . "include the parent directory of libphutil/.\n";
    exit(1);
}
phutil_load_library(dirname(__FILE__) . '/../src/');
// NOTE: This is dangerous in general, but we know we're in a script context and
// are not vulnerable to CSRF.
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
require_once dirname(dirname(__FILE__)) . '/conf/__init_conf__.php';
$env = isset($_SERVER['PHABRICATOR_ENV']) ? $_SERVER['PHABRICATOR_ENV'] : getenv('PHABRICATOR_ENV');
if (!$env) {
    echo phutil_console_wrap(phutil_console_format("**ERROR**: PHABRICATOR_ENV Not Set\n\n" . "Define the __PHABRICATOR_ENV__ environment variable before running " . "this script. You can do it on the command line like this:\n\n" . "  \$ PHABRICATOR_ENV=__custom/myconfig__ %s ...\n\n" . "Replace __custom/myconfig__ with the path to your configuration file. " . "For more information, see the 'Configuration Guide' in the " . "Phabricator documentation.\n\n", $argv[0]));
    exit(1);
}
$conf = phabricator_read_config_file($env);
$conf['phabricator.env'] = $env;
PhabricatorEnv::setEnvConfig($conf);
phutil_load_library('arcanist/src');
foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) {
    phutil_load_library($library);
}
PhutilErrorHandler::initialize();
PhabricatorEventEngine::initialize();
$tz = PhabricatorEnv::getEnvConfig('phabricator.timezone');
if ($tz) {
    date_default_timezone_set($tz);
}
 public function run()
 {
     $svnargs = $this->getArgument('svnargs');
     $repository = $svnargs[0];
     $transaction = $svnargs[1];
     list($commit_message) = execx('svnlook log --transaction %s %s', $transaction, $repository);
     if (strpos($commit_message, '@bypass-lint') !== false) {
         return 0;
     }
     // TODO: Do stuff with commit message.
     list($changed) = execx('svnlook changed --transaction %s %s', $transaction, $repository);
     $paths = array();
     $changed = explode("\n", trim($changed));
     foreach ($changed as $line) {
         $matches = null;
         preg_match('/^..\\s*(.*)$/', $line, $matches);
         $paths[$matches[1]] = strlen($matches[1]);
     }
     $resolved = array();
     $failed = array();
     $missing = array();
     $found = array();
     asort($paths);
     foreach ($paths as $path => $length) {
         foreach ($resolved as $rpath => $root) {
             if (!strncmp($path, $rpath, strlen($rpath))) {
                 $resolved[$path] = $root;
                 continue 2;
             }
         }
         $config = $path;
         if (basename($config) == '.arcconfig') {
             $resolved[$config] = $config;
             continue;
         }
         $config = rtrim($config, '/');
         $last_config = $config;
         do {
             if (!empty($missing[$config])) {
                 break;
             } else {
                 if (!empty($found[$config])) {
                     $resolved[$path] = $found[$config];
                     break;
                 }
             }
             list($err) = exec_manual('svnlook cat --transaction %s %s %s', $transaction, $repository, $config ? $config . '/.arcconfig' : '.arcconfig');
             if ($err) {
                 $missing[$path] = true;
             } else {
                 $resolved[$path] = $config ? $config . '/.arcconfig' : '.arcconfig';
                 $found[$config] = $resolved[$path];
                 break;
             }
             $config = dirname($config);
             if ($config == '.') {
                 $config = '';
             }
             if ($config == $last_config) {
                 break;
             }
             $last_config = $config;
         } while (true);
         if (empty($resolved[$path])) {
             $failed[] = $path;
         }
     }
     if ($failed && $resolved) {
         $failed_paths = '        ' . implode("\n        ", $failed);
         $resolved_paths = '        ' . implode("\n        ", array_keys($resolved));
         throw new ArcanistUsageException("This commit includes a mixture of files in Arcanist projects and " . "outside of Arcanist projects. A commit which affects an Arcanist " . "project must affect only that project.\n\n" . "Files in projects:\n\n" . $resolved_paths . "\n\n" . "Files not in projects:\n\n" . $failed_paths);
     }
     if (!$resolved) {
         // None of the affected paths are beneath a .arcconfig file.
         return 0;
     }
     $groups = array();
     foreach ($resolved as $path => $project) {
         $groups[$project][] = $path;
     }
     if (count($groups) > 1) {
         $message = array();
         foreach ($groups as $project => $group) {
             $message[] = "Files underneath '{$project}':\n\n";
             $message[] = "        " . implode("\n        ", $group) . "\n\n";
         }
         $message = implode('', $message);
         throw new ArcanistUsageException("This commit includes a mixture of files from different Arcanist " . "projects. A commit which affects an Arcanist project must affect " . "only that project.\n\n" . $message);
     }
     $config_file = key($groups);
     $project_root = dirname($config_file);
     $paths = reset($groups);
     list($config) = execx('svnlook cat --transaction %s %s %s', $transaction, $repository, $config_file);
     $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile($project_root, $config, $config_file . " (svnlook: {$transaction} {$repository})");
     $repository_api = new ArcanistSubversionHookAPI($project_root, $transaction, $repository);
     $lint_engine = $working_copy->getConfig('lint_engine');
     if (!$lint_engine) {
         return 0;
     }
     $engine = newv($lint_engine, array());
     $engine->setWorkingCopy($working_copy);
     $engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
     $engine->setPaths($paths);
     $engine->setCommitHookMode(true);
     $engine->setHookAPI($repository_api);
     try {
         $results = $engine->run();
     } catch (ArcanistNoEffectException $no_effect) {
         // Nothing to do, bail out.
         return 0;
     }
     $failures = array();
     foreach ($results as $result) {
         if (!$result->getMessages()) {
             continue;
         }
         $failures[] = $result;
     }
     if ($failures) {
         $at = "@";
         $msg = phutil_console_format("\n**LINT ERRORS**\n\n" . "This changeset has lint errors. You must fix all lint errors before " . "you can commit.\n\n" . "You can add '{$at}bypass-lint' to your commit message to disable " . "lint checks for this commit, or '{$at}nolint' to the file with " . "errors to disable lint for that file.\n\n");
         echo phutil_console_wrap($msg);
         $renderer = new ArcanistLintConsoleRenderer();
         foreach ($failures as $result) {
             echo $renderer->renderLintResult($result);
         }
         return 1;
     }
     return 0;
 }
 public function run()
 {
     $source = $this->getSource();
     $param = $this->getSourceParam();
     try {
         switch ($source) {
             case self::SOURCE_PATCH:
                 if ($param == '-') {
                     $patch = @file_get_contents('php://stdin');
                     if (!strlen($patch)) {
                         throw new ArcanistUsageException(pht('Failed to read patch from stdin!'));
                     }
                 } else {
                     $patch = Filesystem::readFile($param);
                 }
                 $bundle = ArcanistBundle::newFromDiff($patch);
                 break;
             case self::SOURCE_BUNDLE:
                 $path = $this->getArgument('arcbundle');
                 $bundle = ArcanistBundle::newFromArcBundle($path);
                 break;
             case self::SOURCE_REVISION:
                 $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $param);
                 break;
             case self::SOURCE_DIFF:
                 $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $param);
                 break;
         }
     } catch (ConduitClientException $ex) {
         if ($ex->getErrorCode() == 'ERR-INVALID-SESSION') {
             // Phabricator is not configured to allow anonymous access to
             // Differential.
             $this->authenticateConduit();
             return $this->run();
         } else {
             throw $ex;
         }
     }
     $try_encoding = nonempty($this->getArgument('encoding'), null);
     if (!$try_encoding) {
         if ($this->requiresConduit()) {
             try {
                 $try_encoding = $this->getRepositoryEncoding();
             } catch (ConduitClientException $e) {
                 $try_encoding = null;
             }
         }
     }
     if ($try_encoding) {
         $bundle->setEncoding($try_encoding);
     }
     $sanity_check = !$this->getArgument('force', false);
     // we should update the working copy before we do ANYTHING else to
     // the working copy
     if ($this->shouldUpdateWorkingCopy()) {
         $this->updateWorkingCopy();
     }
     if ($sanity_check) {
         $this->requireCleanWorkingCopy();
     }
     $repository_api = $this->getRepositoryAPI();
     $has_base_revision = $repository_api->hasLocalCommit($bundle->getBaseRevision());
     if ($this->canBranch() && ($this->shouldBranch() || $this->shouldCommit() && $has_base_revision)) {
         if ($repository_api instanceof ArcanistGitAPI) {
             $original_branch = $repository_api->getBranchName();
         } else {
             if ($repository_api instanceof ArcanistMercurialAPI) {
                 $original_branch = $repository_api->getActiveBookmark();
             }
         }
         // If we weren't on a branch, then record the ref we'll return to
         // instead.
         if ($original_branch === null) {
             if ($repository_api instanceof ArcanistGitAPI) {
                 $original_branch = $repository_api->getCanonicalRevisionName('HEAD');
             } else {
                 if ($repository_api instanceof ArcanistMercurialAPI) {
                     $original_branch = $repository_api->getCanonicalRevisionName('.');
                 }
             }
         }
         $new_branch = $this->createBranch($bundle, $has_base_revision);
     }
     if (!$has_base_revision && $this->shouldApplyDependencies()) {
         $this->applyDependencies($bundle);
     }
     if ($sanity_check) {
         $this->sanityCheck($bundle);
     }
     if ($repository_api instanceof ArcanistSubversionAPI) {
         $patch_err = 0;
         $copies = array();
         $deletes = array();
         $patches = array();
         $propset = array();
         $adds = array();
         $symlinks = array();
         $changes = $bundle->getChanges();
         foreach ($changes as $change) {
             $type = $change->getType();
             $should_patch = true;
             $filetype = $change->getFileType();
             switch ($filetype) {
                 case ArcanistDiffChangeType::FILE_SYMLINK:
                     $should_patch = false;
                     $symlinks[] = $change;
                     break;
             }
             switch ($type) {
                 case ArcanistDiffChangeType::TYPE_MOVE_AWAY:
                 case ArcanistDiffChangeType::TYPE_MULTICOPY:
                 case ArcanistDiffChangeType::TYPE_DELETE:
                     $path = $change->getCurrentPath();
                     $fpath = $repository_api->getPath($path);
                     if (!@file_exists($fpath)) {
                         $ok = phutil_console_confirm(pht("Patch deletes file '%s', but the file does not exist in " . "the working copy. Continue anyway?", $path));
                         if (!$ok) {
                             throw new ArcanistUserAbortException();
                         }
                     } else {
                         $deletes[] = $change->getCurrentPath();
                     }
                     $should_patch = false;
                     break;
                 case ArcanistDiffChangeType::TYPE_COPY_HERE:
                 case ArcanistDiffChangeType::TYPE_MOVE_HERE:
                     $path = $change->getOldPath();
                     $fpath = $repository_api->getPath($path);
                     if (!@file_exists($fpath)) {
                         $cpath = $change->getCurrentPath();
                         if ($type == ArcanistDiffChangeType::TYPE_COPY_HERE) {
                             $verbs = pht('copies');
                         } else {
                             $verbs = pht('moves');
                         }
                         $ok = phutil_console_confirm(pht("Patch %s '%s' to '%s', but source path does not exist " . "in the working copy. Continue anyway?", $verbs, $path, $cpath));
                         if (!$ok) {
                             throw new ArcanistUserAbortException();
                         }
                     } else {
                         $copies[] = array($change->getOldPath(), $change->getCurrentPath());
                     }
                     break;
                 case ArcanistDiffChangeType::TYPE_ADD:
                     $adds[] = $change->getCurrentPath();
                     break;
             }
             if ($should_patch) {
                 $cbundle = ArcanistBundle::newFromChanges(array($change));
                 $patches[$change->getCurrentPath()] = $cbundle->toUnifiedDiff();
                 $prop_old = $change->getOldProperties();
                 $prop_new = $change->getNewProperties();
                 $props = $prop_old + $prop_new;
                 foreach ($props as $key => $ignored) {
                     if (idx($prop_old, $key) !== idx($prop_new, $key)) {
                         $propset[$change->getCurrentPath()][$key] = idx($prop_new, $key);
                     }
                 }
             }
         }
         // Before we start doing anything, create all the directories we're going
         // to add files to if they don't already exist.
         foreach ($copies as $copy) {
             list($src, $dst) = $copy;
             $this->createParentDirectoryOf($dst);
         }
         foreach ($patches as $path => $patch) {
             $this->createParentDirectoryOf($path);
         }
         foreach ($adds as $add) {
             $this->createParentDirectoryOf($add);
         }
         // TODO: The SVN patch workflow likely does not work on windows because
         // of the (cd ...) stuff.
         foreach ($copies as $copy) {
             list($src, $dst) = $copy;
             passthru(csprintf('(cd %s; svn cp %s %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($src), ArcanistSubversionAPI::escapeFileNameForSVN($dst)));
         }
         foreach ($deletes as $delete) {
             passthru(csprintf('(cd %s; svn rm %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($delete)));
         }
         foreach ($symlinks as $symlink) {
             $link_target = $symlink->getSymlinkTarget();
             $link_path = $symlink->getCurrentPath();
             switch ($symlink->getType()) {
                 case ArcanistDiffChangeType::TYPE_ADD:
                 case ArcanistDiffChangeType::TYPE_CHANGE:
                 case ArcanistDiffChangeType::TYPE_MOVE_HERE:
                 case ArcanistDiffChangeType::TYPE_COPY_HERE:
                     execx('(cd %s && ln -sf %s %s)', $repository_api->getPath(), $link_target, $link_path);
                     break;
             }
         }
         foreach ($patches as $path => $patch) {
             $err = null;
             if ($patch) {
                 $tmp = new TempFile();
                 Filesystem::writeFile($tmp, $patch);
                 passthru(csprintf('(cd %s; patch -p0 < %s)', $repository_api->getPath(), $tmp), $err);
             } else {
                 passthru(csprintf('(cd %s; touch %s)', $repository_api->getPath(), $path), $err);
             }
             if ($err) {
                 $patch_err = max($patch_err, $err);
             }
         }
         foreach ($adds as $add) {
             passthru(csprintf('(cd %s; svn add %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($add)));
         }
         foreach ($propset as $path => $changes) {
             foreach ($changes as $prop => $value) {
                 if ($prop == 'unix:filemode') {
                     // Setting this property also changes the file mode.
                     $prop = 'svn:executable';
                     $value = octdec($value) & 0111 ? 'on' : null;
                 }
                 if ($value === null) {
                     passthru(csprintf('(cd %s; svn propdel %s %s)', $repository_api->getPath(), $prop, ArcanistSubversionAPI::escapeFileNameForSVN($path)));
                 } else {
                     passthru(csprintf('(cd %s; svn propset %s %s %s)', $repository_api->getPath(), $prop, $value, ArcanistSubversionAPI::escapeFileNameForSVN($path)));
                 }
             }
         }
         if ($patch_err == 0) {
             echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully applied patch to the working copy.'));
         } else {
             echo phutil_console_format("\n\n<bg:yellow>** %s **</bg> %s\n", pht('WARNING'), pht("Some hunks could not be applied cleanly by the unix '%s' " . "utility. Your working copy may be different from the revision's " . "base, or you may be in the wrong subdirectory. You can export " . "the raw patch file using '%s', and then try to apply it by " . "fiddling with options to '%s' (particularly, %s), or manually. " . "The output above, from '%s', may be helpful in " . "figuring out what went wrong.", 'patch', 'arc export --unified', 'patch', '-p', 'patch'));
         }
         return $patch_err;
     } else {
         if ($repository_api instanceof ArcanistGitAPI) {
             $patchfile = new TempFile();
             Filesystem::writeFile($patchfile, $bundle->toGitPatch());
             $passthru = new PhutilExecPassthru('git apply --index --reject -- %s', $patchfile);
             $passthru->setCWD($repository_api->getPath());
             $err = $passthru->execute();
             if ($err) {
                 echo phutil_console_format("\n<bg:red>** %s **</bg>\n", pht('Patch Failed!'));
                 // NOTE: Git patches may fail if they change the case of a filename
                 // (for instance, from 'example.c' to 'Example.c'). As of now, Git
                 // can not apply these patches on case-insensitive filesystems and
                 // there is no way to build a patch which works.
                 throw new ArcanistUsageException(pht('Unable to apply patch!'));
             }
             // in case there were any submodule changes involved
             $repository_api->execpassthru('submodule update --init --recursive');
             if ($this->shouldCommit()) {
                 if ($bundle->getFullAuthor()) {
                     $author_cmd = csprintf('--author=%s', $bundle->getFullAuthor());
                 } else {
                     $author_cmd = '';
                 }
                 $commit_message = $this->getCommitMessage($bundle);
                 $future = $repository_api->execFutureLocal('commit -a %C -F - --no-verify', $author_cmd);
                 $future->write($commit_message);
                 $future->resolvex();
                 $verb = pht('committed');
             } else {
                 $verb = pht('applied');
             }
             if ($this->canBranch() && !$this->shouldBranch() && $this->shouldCommit() && $has_base_revision) {
                 $repository_api->execxLocal('checkout %s', $original_branch);
                 $ex = null;
                 try {
                     $repository_api->execxLocal('cherry-pick %s', $new_branch);
                 } catch (Exception $ex) {
                     // do nothing
                 }
                 $repository_api->execxLocal('branch -D %s', $new_branch);
                 if ($ex) {
                     echo phutil_console_format("\n<bg:red>** %s**</bg>\n", pht('Cherry Pick Failed!'));
                     throw $ex;
                 }
             }
             echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully %s patch.', $verb));
         } else {
             if ($repository_api instanceof ArcanistMercurialAPI) {
                 $future = $repository_api->execFutureLocal('import --no-commit -');
                 $future->write($bundle->toGitPatch());
                 try {
                     $future->resolvex();
                 } catch (CommandException $ex) {
                     echo phutil_console_format("\n<bg:red>** %s **</bg>\n", pht('Patch Failed!'));
                     $stderr = $ex->getStdErr();
                     if (preg_match('/case-folding collision/', $stderr)) {
                         echo phutil_console_wrap(phutil_console_format("\n<bg:yellow>** %s **</bg> %s\n", pht('WARNING'), pht("This patch may have failed because it attempts to change " . "the case of a filename (for instance, from '%s' to '%s'). " . "Mercurial cannot apply patches like this on case-insensitive " . "filesystems. You must apply this patch manually.", 'example.c', 'Example.c')));
                     }
                     throw $ex;
                 }
                 if ($this->shouldCommit()) {
                     $author = coalesce($bundle->getFullAuthor(), $bundle->getAuthorName());
                     if ($author !== null) {
                         $author_cmd = csprintf('-u %s', $author);
                     } else {
                         $author_cmd = '';
                     }
                     $commit_message = $this->getCommitMessage($bundle);
                     $future = $repository_api->execFutureLocal('commit %C -l -', $author_cmd);
                     $future->write($commit_message);
                     $future->resolvex();
                     if (!$this->shouldBranch() && $has_base_revision) {
                         $original_rev = $repository_api->getCanonicalRevisionName($original_branch);
                         $current_parent = $repository_api->getCanonicalRevisionName(hgsprintf('%s^', $new_branch));
                         $err = 0;
                         if ($original_rev != $current_parent) {
                             list($err) = $repository_api->execManualLocal('rebase --dest %s --rev %s', hgsprintf('%s', $original_branch), hgsprintf('%s', $new_branch));
                         }
                         $repository_api->execxLocal('bookmark --delete %s', $new_branch);
                         if ($err) {
                             $repository_api->execManualLocal('rebase --abort');
                             throw new ArcanistUsageException(phutil_console_format("\n<bg:red>** %s**</bg>\n", pht('Rebase onto %s failed!', $original_branch)));
                         }
                     }
                     $verb = pht('committed');
                 } else {
                     $verb = pht('applied');
                 }
                 echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully %s patch.', $verb));
             } else {
                 throw new Exception(pht('Unknown version control system.'));
             }
         }
     }
     return 0;
 }
Пример #30
0
 public function run()
 {
     $console = PhutilConsole::getConsole();
     $linters = id(new PhutilSymbolLoader())->setAncestorClass('ArcanistLinter')->loadObjects();
     try {
         $built = $this->newLintEngine()->buildLinters();
     } catch (ArcanistNoEngineException $ex) {
         $built = array();
     }
     // Note that an engine can emit multiple linters of the same class to run
     // different rulesets on different groups of files, so these linters do not
     // necessarily have unique classes or types.
     $groups = array();
     foreach ($built as $linter) {
         $groups[get_class($linter)][] = $linter;
     }
     $linter_info = array();
     foreach ($linters as $key => $linter) {
         $installed = idx($groups, $key, array());
         $exception = null;
         if ($installed) {
             $status = 'configured';
             try {
                 $version = head($installed)->getVersion();
             } catch (Exception $ex) {
                 $status = 'error';
                 $exception = $ex;
             }
         } else {
             $status = 'available';
             $version = null;
         }
         $linter_info[$key] = array('short' => $linter->getLinterConfigurationName(), 'class' => get_class($linter), 'status' => $status, 'version' => $version, 'name' => $linter->getInfoName(), 'uri' => $linter->getInfoURI(), 'description' => $linter->getInfoDescription(), 'exception' => $exception, 'options' => $linter->getLinterConfigurationOptions());
     }
     $linter_info = isort($linter_info, 'short');
     $status_map = $this->getStatusMap();
     $pad = '    ';
     $color_map = array('configured' => 'green', 'available' => 'yellow', 'error' => 'red');
     foreach ($linter_info as $key => $linter) {
         $status = $linter['status'];
         $color = $color_map[$status];
         $text = $status_map[$status];
         $print_tail = false;
         $console->writeOut("<bg:" . $color . ">** %s **</bg> **%s** (%s)\n", $text, nonempty($linter['short'], '-'), $linter['name']);
         if ($linter['exception']) {
             $console->writeOut("\n%s**%s**\n%s\n", $pad, get_class($linter['exception']), phutil_console_wrap($linter['exception']->getMessage(), strlen($pad)));
             $print_tail = true;
         }
         $version = $linter['version'];
         $uri = $linter['uri'];
         if ($version || $uri) {
             $console->writeOut("\n");
             $print_tail = true;
         }
         if ($version) {
             $console->writeOut("%s%s **%s**\n", $pad, pht('Version'), $version);
         }
         if ($uri) {
             $console->writeOut("%s__%s__\n", $pad, $linter['uri']);
         }
         $description = $linter['description'];
         if ($description) {
             $console->writeOut("\n%s\n", phutil_console_wrap($linter['description'], strlen($pad)));
             $print_tail = true;
         }
         $options = $linter['options'];
         if ($options && $this->getArgument('verbose')) {
             $console->writeOut("\n%s**%s**\n\n", $pad, pht('Configuration Options'));
             $last_option = last_key($options);
             foreach ($options as $option => $option_spec) {
                 $console->writeOut("%s__%s__ (%s)\n", $pad, $option, $option_spec['type']);
                 $console->writeOut("%s\n", phutil_console_wrap($option_spec['help'], strlen($pad) + 2));
                 if ($option != $last_option) {
                     $console->writeOut("\n");
                 }
             }
             $print_tail = true;
         }
         if ($print_tail) {
             $console->writeOut("\n");
         }
     }
     if (!$this->getArgument('verbose')) {
         $console->writeOut("%s\n", pht('(Run `%s` for more details.)', 'arc linters --verbose'));
     }
 }