public function testPasswords() { // Normal "%s" doesn't do anything special. $command = csprintf('echo %s', 'hunter2trustno1'); $this->assertTrue(strpos($command, 'hunter2trustno1') !== false); // "%P" takes a PhutilOpaqueEnvelope. $caught = null; try { csprintf('echo %P', 'hunter2trustno1'); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof Exception); // "%P" masks the provided value. $command = csprintf('echo %P', new PhutilOpaqueEnvelope('hunter2trustno1')); $this->assertFalse(strpos($command, 'hunter2trustno1')); // Executing the command works as expected. list($out) = execx('%C', $command); $this->assertTrue(strpos($out, 'hunter2trustno1') !== false); // Escaping should be robust even when used to escape commands which take // other commands. if (!phutil_is_windows()) { list($out) = execx('sh -c %s', csprintf('sh -c %s', csprintf('sh -c %s', csprintf('echo %P', new PhutilOpaqueEnvelope('!@#$%^&*()'))))); $this->assertTrue(strpos($out, '!@#$%^&*()') !== false); } }
public function buildTestFuture($test_output, $cover_output) { $paths = $this->getPaths(); $cmd_line = csprintf('source bin/activate; coverage run --source nylas -m py.test --junitxml tests/output tests; coverage xml -i -o tests/coverage'); return new ExecFuture('%C', $cmd_line); }
/** * @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"); }
/** * Format a command so it executes as the daemon user, if a daemon user is * defined. This wraps the provided command in `sudo -u ...`, roughly. * * @param PhutilCommandString Command to execute. * @return PhutilCommandString `sudo` version of the command. */ public static function sudoCommandAsDaemonUser($command) { $user = PhabricatorEnv::getEnvConfig('phd.user'); if (!$user) { // No daemon user is set, so just run this as ourselves. return $command; } // We may reach this method while already running as the daemon user: for // example, active and passive synchronization of clustered repositories // run the same commands through the same code, but as different users. // By default, `sudo` won't let you sudo to yourself, so we can get into // trouble if we're already running as the daemon user unless the host has // been configured to let the daemon user run commands as itself. // Since this is silly and more complicated than doing this check, don't // use `sudo` if we're already running as the correct user. if (function_exists('posix_getuid')) { $uid = posix_getuid(); $info = posix_getpwuid($uid); if ($info && $info['name'] == $user) { return $command; } } // Get the absolute path so we're safe against the caller wiping out // PATH. $sudo = Filesystem::resolveBinary('sudo'); if (!$sudo) { throw new Exception(pht("Unable to find 'sudo'!")); } // Flags here are: // // -E: Preserve the environment. // -n: Non-interactive. Exit with an error instead of prompting. // -u: Which user to sudo to. return csprintf('%s -E -n -u %s -- %C', $sudo, $user, $command); }
public function renderPropertyViewValue(array $handles) { $revision = $this->getObject(); $diff = $revision->getActiveDiff(); $status = $revision->getStatus(); if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) { return null; } $local_vcs = $diff->getSourceControlSystem(); switch ($local_vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $bookmark = $diff->getBookmark(); if (strlen($bookmark)) { $next_step = csprintf('arc land %R', $bookmark); } else { $next_step = csprintf('arc land'); } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $branch = $diff->getBranch(); if (strlen($branch)) { $next_step = csprintf('arc land %R', $branch); } else { $next_step = csprintf('arc land'); } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $next_step = csprintf('arc commit'); break; default: return null; } $next_step = phutil_tag('tt', array(), (string) $next_step); return $next_step; }
protected function getProxyCommand() { $uri = new PhutilURI($this->proxyURI); $username = PhabricatorEnv::getEnvConfig('cluster.instance'); if (!strlen($username)) { $username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user'); if (!strlen($username)) { throw new Exception(pht('Unable to determine the username to connect with when trying ' . 'to proxy an SSH request within the Phabricator cluster.')); } } $port = $uri->getPort(); $host = $uri->getDomain(); $key_path = AlmanacKeys::getKeyPath('device.key'); if (!Filesystem::pathExists($key_path)) { throw new Exception(pht('Unable to proxy this SSH request within the cluster: this device ' . 'is not registered and has a missing device key (expected to ' . 'find key at "%s").', $key_path)); } $options = array(); $options[] = '-o'; $options[] = 'StrictHostKeyChecking=no'; $options[] = '-o'; $options[] = 'UserKnownHostsFile=/dev/null'; // This is suppressing "added <address> to the list of known hosts" // messages, which are confusing and irrelevant when they arise from // proxied requests. It might also be suppressing lots of useful errors, // of course. Ideally, we would enforce host keys eventually. $options[] = '-o'; $options[] = 'LogLevel=quiet'; // NOTE: We prefix the command with "@username", which the far end of the // connection will parse in order to act as the specified user. This // behavior is only available to cluster requests signed by a trusted // device key. return csprintf('ssh %Ls -l %s -i %s -p %s %s -- %s %Ls', $options, $username, $key_path, $port, $host, '@' . $this->getUser()->getUsername(), $this->getOriginalArguments()); }
public function getPEP8Path() { $working_copy = $this->getEngine()->getWorkingCopy(); $prefix = $working_copy->getConfig('lint.pep8.prefix'); $bin = $working_copy->getConfig('lint.pep8.bin'); if ($bin === null && $prefix === null) { $bin = csprintf('/usr/bin/env python2.6 %s', phutil_get_library_root('arcanist') . '/../externals/pep8/pep8.py'); } else { if ($bin === null) { $bin = 'pep8'; } if ($prefix !== null) { if (!Filesystem::pathExists($prefix . '/' . $bin)) { throw new ArcanistUsageException("Unable to find PEP8 binary in a specified directory. Make sure " . "that 'lint.pep8.prefix' and 'lint.pep8.bin' keys are set " . "correctly. If you'd rather use a copy of PEP8 installed " . "globally, you can just remove these keys from your .arcconfig"); } $bin = csprintf("%s/%s", $prefix, $bin); return $bin; } // Look for globally installed PEP8 list($err) = exec_manual('which %s', $bin); if ($err) { throw new ArcanistUsageException("PEP8 does not appear to be installed on this system. Install it " . "(e.g., with 'easy_install pep8') or configure " . "'lint.pep8.prefix' in your .arcconfig to point to the directory " . "where it resides."); } } return $bin; }
public function executeAcquireLease(DrydockResource $resource, DrydockLease $lease) { $key = Filesystem::readRandomCharacters(12); $ports = $resource->getAttribute('ports', array()); for ($ii = 2000;; $ii++) { if (empty($ports[$ii])) { $ports[$ii] = $lease->getID(); $port = $ii; break; } } $resource->setAttribute('ports', $ports); $resource->save(); $host = $resource->getAttribute('host'); $lease->setAttribute('port', $port); $lease->setAttribute('key', $key); $lease->save(); $config = <<<EOCONFIG Listen *:{$port} <VirtualHost *:{$port}> DocumentRoot /opt/drydock/webroot/{$key}/ ServerName {$host} </VirtualHost> EOCONFIG; $cmd = $this->getInterface($resource, $lease, 'command'); $cmd->execx(<<<EOSETUP sudo mkdir -p %s && sudo sh -c %s && sudo /etc/init.d/httpd restart EOSETUP , "/opt/drydock/webroot/{$key}/", csprintf('echo %s > %s', $config, "/etc/httpd/conf.d/drydock-{$key}.conf")); $lease->setAttribute('uri', "http://{$host}:{$port}/"); $lease->save(); }
public function getExecFuture($command) { $this->openCredentialsIfNotOpen(); $argv = func_get_args(); if ($this->getConfig('platform') === 'windows') { // Handle Windows by executing the command under PowerShell. $command = id(new PhutilCommandString($argv))->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); $change_directory = ''; if ($this->getWorkingDirectory() !== null) { $change_directory .= 'cd ' . $this->getWorkingDirectory(); } $script = <<<EOF {$change_directory} {$command} if (\$LastExitCode -ne 0) { exit \$LastExitCode } EOF; // When Microsoft says "Unicode" they don't mean UTF-8. $script = mb_convert_encoding($script, 'UTF-16LE'); $script = base64_encode($script); $powershell = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; $powershell .= ' -ExecutionPolicy Bypass' . ' -NonInteractive' . ' -InputFormat Text' . ' -OutputFormat Text' . ' -EncodedCommand ' . $script; $full_command = $powershell; } else { // Handle UNIX by executing under the native shell. $argv = $this->applyWorkingDirectoryToArgv($argv); $full_command = call_user_func_array('csprintf', $argv); } $command_timeout = ''; if ($this->connectTimeout !== null) { $command_timeout = csprintf('-o %s', 'ConnectTimeout=' . $this->connectTimeout); } return new ExecFuture('ssh ' . '-o LogLevel=quiet ' . '-o StrictHostKeyChecking=no ' . '-o UserKnownHostsFile=/dev/null ' . '-o BatchMode=yes ' . '%C -p %s -i %P %P@%s -- %s', $command_timeout, $this->getConfig('port'), $this->passphraseSSHKey->getKeyfileEnvelope(), $this->passphraseSSHKey->getUsernameEnvelope(), $this->getConfig('host'), $full_command); }
protected function executeRepositoryOperations() { $repository = $this->getRepository(); $args = $this->getArgs(); if (!$args->getArg('stdio')) { throw new Exception(pht('Expected `%s`!', 'hg ... --stdio')); } if ($args->getArg('command') !== array('serve')) { throw new Exception(pht('Expected `%s`!', 'hg ... serve')); } if ($this->shouldProxy()) { $command = $this->getProxyCommand(); } else { $command = csprintf('hg -R %s serve --stdio', $repository->getLocalPath()); } $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment()); $io_channel = $this->getIOChannel(); $protocol_channel = new DiffusionMercurialWireClientSSHProtocolChannel($io_channel); $err = id($this->newPassthruCommand())->setIOChannel($protocol_channel)->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->execute(); // TODO: It's apparently technically possible to communicate errors to // Mercurial over SSH by writing a special "\n<error>\n-\n" string. However, // my attempt to implement that resulted in Mercurial closing the socket and // then hanging, without showing the error. This might be an issue on our // side (we need to close our half of the socket?), or maybe the code // for this in Mercurial doesn't actually work, or maybe something else // is afoot. At some point, we should look into doing this more cleanly. // For now, when we, e.g., reject writes for policy reasons, the user will // see "abort: unexpected response: empty string" after the diagnostically // useful, e.g., "remote: This repository is read-only over SSH." message. if (!$err && $this->didSeeWrite) { $repository->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY); } return $err; }
protected function executeRepositoryOperations() { $repository = $this->getRepository(); $viewer = $this->getUser(); $device = AlmanacKeys::getLiveDevice(); $skip_sync = $this->shouldSkipReadSynchronization(); if ($this->shouldProxy()) { $command = $this->getProxyCommand(); if ($device) { $this->writeClusterEngineLogMessage(pht("# Fetch received by \"%s\", forwarding to cluster host.\n", $device->getName())); } } else { $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath()); if (!$skip_sync) { $cluster_engine = id(new DiffusionRepositoryClusterEngine())->setViewer($viewer)->setRepository($repository)->setLog($this)->synchronizeWorkingCopyBeforeRead(); if ($device) { $this->writeClusterEngineLogMessage(pht("# Cleared to fetch on cluster host \"%s\".\n", $device->getName())); } } } $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment()); $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute(); if (!$err) { $this->waitForGitClient(); } return $err; }
public function writeFile($path, $data) { $source = new TempFile(); Filesystem::writeFile($source, $data); $future = $this->getExecFuture($path); $future->write(csprintf('put %s %s', $source, $path)); $future->resolvex(); }
protected function getVersion() { $cmd = csprintf('%s version', $this->getBinary()); list($stdout) = execx('%C', $cmd); $matches = array(); preg_match('/^go version go(?P<version>[0-9\\.]+).*/', $stdout, $matches); return $matches['version']; }
public function buildTestFuture($junit_tmp, $cover_tmp) { $paths = $this->getPaths(); $config_manager = $this->getConfigurationManager(); $coverage_command = $config_manager->getConfigFromAnySource('unit.pytest.command'); $cmd_line = csprintf($coverage_command, $junit_tmp); return new ExecFuture('%C', $cmd_line); }
public function buildTestFuture($path, $xunit_tmp, $cover_tmp) { $cmd_line = csprintf('nosetests --with-xunit --xunit-file=%s', $xunit_tmp); if ($this->getEnableCoverage() !== false) { $cmd_line .= csprintf(' --with-coverage --cover-xml --cover-xml-file=%s', $cover_tmp); } return new ExecFuture('%C %s', $cmd_line, $path); }
/** * Execute the unit tests * * @return array Unit test results */ public function run() { $this->console = PhutilConsole::getConsole(); $this->project_root = $this->getWorkingCopy()->getProjectRoot() . '/'; $this->prepareConfigFile(); if ($this->getRunAllTests()) { $paths = [$this->test_directory, $this->source_directory]; } else { $paths = $this->getPaths(); } // Expand relative path to its absolute counterpart foreach ($paths as &$path) { $path = Filesystem::resolvePath($path, $this->project_root); } $this->console->writeLog("##Finding tests##\n"); $this->test_cases = $this->getTestsForPaths($paths); if (!$this->test_cases) { throw new ArcanistNoEffectException('No tests to run.'); } $futures = array(); $temp_files = array(); // This is a really hacky but safe way to determine where libphutil // came from: simply search the list of included files for the init // script. This should allow `arc` to live absolutely anywhere without // making any assumptions about how you entered the workflow or whether // arcanist and libphutil are even used by the codebase. $search = 'libphutil' . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . '__init_script__.php'; $include_path = null; foreach (get_included_files() as $include) { if ($search === substr($include, -strlen($search))) { $include_path = csprintf('--include-path %s', substr($include, 0, -strlen($search))); break; } } foreach ($this->test_cases as $test_path) { $json_tmp = new TempFile(); $clover_tmp = null; $clover = null; if ($this->getEnableCoverage() !== false) { $clover_tmp = new TempFile(); $clover = csprintf('--coverage-clover %s', $clover_tmp); } $config = file_exists($this->phpunit_config_file) ? csprintf('--configuration %s', $this->phpunit_config_file) : null; $bin = 'vendor/bin/phpunit'; $phpunit = Filesystem::resolvePath($bin, $this->project_root); $stderr = '-d display_errors=stderr'; $futures[$test_path] = new ExecFuture('%C %C %C %C --log-json %s %C --whitelist %s %s', $phpunit, $stderr, $config, $include_path, $json_tmp, $clover, $this->source_directory, $test_path); $temp_files[$test_path] = ['json' => $json_tmp, 'clover' => $clover_tmp]; } unset($test_path); $this->console->writeLog("##Executing tests##\n"); $results = []; foreach ((new FutureIterator($futures))->limit(4) as $test => $future) { list($err, $stdout, $stderr) = $future->resolve(); $results[] = $this->parseTestResults($test, $temp_files[$test]['json'], $temp_files[$test]['clover'], $stderr); } return array_mergev($results); }
public function buildTestFuture($test_output, $cover_output) { $paths = $this->getPaths(); # We want to run the tests inside the VM. # `vagrant ssh -c` will return the exit code of whatever command you pass, # but we need it to always return 0. Hence the `|| true`. $cmd_line = csprintf('vagrant ssh -c \'export INBOX_ENV="test"; cd /vagrant; coverage run --source /vagrant/inbox -m py.test --junitxml /vagrant/tests/output /vagrant/tests; coverage xml -i -o /vagrant/tests/coverage; true\''); return new ExecFuture('%C', $cmd_line); }
public function buildTestFuture($junit_tmp, $cover_tmp) { $paths = $this->getPaths(); $cmd_line = csprintf('py.test --junit-xml=%s', $junit_tmp); if ($this->getEnableCoverage() !== false) { $cmd_line = csprintf('coverage run --source %s -m %C', $this->projectRoot, $cmd_line); } return new ExecFuture('%C', $cmd_line); }
public function buildTestFuture($junit_tmp, $cover_tmp) { $paths = $this->getPaths(); $config_manager = $this->getConfigurationManager(); $coverage_command = $config_manager->getConfigFromAnySource('unit.golang.command'); $cmd_line = csprintf($coverage_command, $junit_tmp, $cover_tmp); $future = new ExecFuture('%C', $cmd_line); $future->setCWD($this->projectRoot); return $future; }
public function testEscapingIsRobust() { if (phutil_is_windows()) { $this->assertSkipped(pht("This test doesn't work on Windows.")); } // Escaping should be robust even when used to escape commands which take // other commands. list($out) = execx('sh -c %s', csprintf('sh -c %s', csprintf('sh -c %s', csprintf('echo %P', new PhutilOpaqueEnvelope('!@#$%^&*()'))))); $this->assertTrue(strpos($out, '!@#$%^&*()') !== false); }
public function run() { $this->projectRoot = $this->getWorkingCopy()->getProjectRoot(); $this->affectedTests = array(); foreach ($this->getPaths() as $path) { $path = Filesystem::resolvePath($path, $this->projectRoot); // TODO: add support for directories // Users can call phpunit on the directory themselves if (is_dir($path)) { continue; } // Not sure if it would make sense to go further if // it is not a .php file if (substr($path, -4) != '.php') { continue; } if (substr($path, -8) == 'Test.php') { // Looks like a valid test file name. $this->affectedTests[$path] = $path; continue; } if ($test = $this->findTestFile($path)) { $this->affectedTests[$path] = $test; } } if (empty($this->affectedTests)) { throw new ArcanistNoEffectException(pht('No tests to run.')); } $this->prepareConfigFile(); $futures = array(); $tmpfiles = array(); foreach ($this->affectedTests as $class_path => $test_path) { if (!Filesystem::pathExists($test_path)) { continue; } $json_tmp = new TempFile(); $clover_tmp = null; $clover = null; if ($this->getEnableCoverage() !== false) { $clover_tmp = new TempFile(); $clover = csprintf('--coverage-clover %s', $clover_tmp); } $config = $this->configFile ? csprintf('-c %s', $this->configFile) : null; $stderr = '-d display_errors=stderr'; $futures[$test_path] = new ExecFuture('%C %C %C --log-json %s %C %s', $this->phpunitBinary, $config, $stderr, $json_tmp, $clover, $test_path); $tmpfiles[$test_path] = array('json' => $json_tmp, 'clover' => $clover_tmp); } $results = array(); $futures = id(new FutureIterator($futures))->limit(4); foreach ($futures as $test => $future) { list($err, $stdout, $stderr) = $future->resolve(); $results[] = $this->parseTestResults($test, $tmpfiles[$test]['json'], $tmpfiles[$test]['clover'], $stderr); } return array_mergev($results); }
public function getExecFuture($command) { $this->openCredentialsIfNotOpen(); $argv = func_get_args(); $argv = $this->applyWorkingDirectoryToArgv($argv); $full_command = call_user_func_array('csprintf', $argv); $command_timeout = ''; if ($this->connectTimeout !== null) { $command_timeout = csprintf('-o %s', 'ConnectTimeout=' . $this->connectTimeout); } return new ExecFuture('ssh ' . '-o LogLevel=quiet ' . '-o StrictHostKeyChecking=no ' . '-o UserKnownHostsFile=/dev/null ' . '-o BatchMode=yes ' . '%C -p %s -i %P %P@%s -- %s', $command_timeout, $this->getConfig('port'), $this->passphraseSSHKey->getKeyfileEnvelope(), $this->passphraseSSHKey->getUsernameEnvelope(), $this->getConfig('host'), $full_command); }
public function applyOperation(DrydockRepositoryOperation $operation, DrydockInterface $interface) { $viewer = $this->getViewer(); $repository = $operation->getRepository(); $cmd = array(); $arg = array(); $object = $operation->getObject(); if ($object instanceof DifferentialRevision) { $revision = $object; $diff = $this->loadDiff($operation); $dict = $diff->getDiffAuthorshipDict(); $author_name = idx($dict, 'authorName'); $author_email = idx($dict, 'authorEmail'); $api_method = 'differential.getcommitmessage'; $api_params = array('revision_id' => $revision->getID()); $commit_message = id(new ConduitCall($api_method, $api_params))->setUser($viewer)->execute(); } else { throw new Exception(pht('Invalid or unknown object ("%s") for land operation, expected ' . 'Differential Revision.', $operation->getObjectPHID())); } $target = $operation->getRepositoryTarget(); list($type, $name) = explode(':', $target, 2); switch ($type) { case 'branch': $push_dst = 'refs/heads/' . $name; break; default: throw new Exception(pht('Unknown repository operation target type "%s" (in target "%s").', $type, $target)); } $committer_info = $this->getCommitterInfo($operation); // NOTE: We're doing this commit with "-F -" so we don't run into trouble // with enormous commit messages which might otherwise exceed the maximum // size of a command. $future = $interface->getExecFuture('git -c user.name=%s -c user.email=%s commit --author %s -F - --', $committer_info['name'], $committer_info['email'], "{$author_name} <{$author_email}>"); $future->write($commit_message); try { $future->resolvex(); } catch (CommandException $ex) { $display_command = csprintf('git commit'); // TODO: One reason this can fail is if the changes have already been // merged. We could try to detect that. $error = DrydockCommandError::newFromCommandException($ex)->setPhase(self::PHASE_COMMIT)->setDisplayCommand($display_command); $operation->setCommandError($error->toDictionary()); throw $ex; } try { $interface->execx('git push origin -- %s:%s', 'HEAD', $push_dst); } catch (CommandException $ex) { $display_command = csprintf('git push origin %R:%R', 'HEAD', $push_dst); $error = DrydockCommandError::newFromCommandException($ex)->setPhase(self::PHASE_PUSH)->setDisplayCommand($display_command); $operation->setCommandError($error->toDictionary()); throw $ex; } }
protected function executeChecks() { $ancient_config = self::getAncientConfig(); $all_keys = PhabricatorEnv::getAllConfigKeys(); $all_keys = array_keys($all_keys); sort($all_keys); $defined_keys = PhabricatorApplicationConfigOptions::loadAllOptions(); foreach ($all_keys as $key) { if (isset($defined_keys[$key])) { continue; } if (isset($ancient_config[$key])) { $summary = pht('This option has been removed. You may delete it at your ' . 'convenience.'); $message = pht("The configuration option '%s' has been removed. You may delete " . "it at your convenience." . "\n\n%s", $key, $ancient_config[$key]); $short = pht('Obsolete Config'); $name = pht('Obsolete Configuration Option "%s"', $key); } else { $summary = pht('This option is not recognized. It may be misspelled.'); $message = pht("The configuration option '%s' is not recognized. It may be " . "misspelled, or it might have existed in an older version of " . "Phabricator. It has no effect, and should be corrected or deleted.", $key); $short = pht('Unknown Config'); $name = pht('Unknown Configuration Option "%s"', $key); } $issue = $this->newIssue('config.unknown.' . $key)->setShortName($short)->setName($name)->setSummary($summary); $stack = PhabricatorEnv::getConfigSourceStack(); $stack = $stack->getStack(); $found = array(); $found_local = false; $found_database = false; foreach ($stack as $source_key => $source) { $value = $source->getKeys(array($key)); if ($value) { $found[] = $source->getName(); if ($source instanceof PhabricatorConfigDatabaseSource) { $found_database = true; } if ($source instanceof PhabricatorConfigLocalSource) { $found_local = true; } } } $message = $message . "\n\n" . pht('This configuration value is defined in these %d ' . 'configuration source(s): %s.', count($found), implode(', ', $found)); $issue->setMessage($message); if ($found_local) { $command = csprintf('phabricator/ $ ./bin/config delete %s', $key); $issue->addCommand($command); } if ($found_database) { $issue->addPhabricatorConfig($key); } } $this->executeManiphestFieldChecks(); }
protected function executeChecks() { $repo_path = PhabricatorEnv::getEnvConfig('repository.default-local-path'); if (!$repo_path) { $summary = pht("The configuration option '%s' is not set.", 'repository.default-local-path'); $this->newIssue('repository.default-local-path.empty')->setName(pht('Missing Repository Local Path'))->setSummary($summary)->addPhabricatorConfig('repository.default-local-path'); return; } if (!Filesystem::pathExists($repo_path)) { $summary = pht('The path for local repositories does not exist, or is not ' . 'readable by the webserver.'); $message = pht("The directory for local repositories (%s) does not exist, or is not " . "readable by the webserver. Phabricator uses this directory to store " . "information about repositories. If this directory does not exist, " . "create it:\n\n" . "%s\n" . "If this directory exists, make it readable to the webserver. You " . "can also edit the configuration below to use some other directory.", phutil_tag('tt', array(), $repo_path), phutil_tag('pre', array(), csprintf('$ mkdir -p %s', $repo_path))); $this->newIssue('repository.default-local-path.empty')->setName(pht('Missing Repository Local Path'))->setSummary($summary)->setMessage($message)->addPhabricatorConfig('repository.default-local-path'); } }
protected function executeRepositoryOperations() { $args = $this->getArgs(); $path = head($args->getArg('dir')); $repository = $this->loadRepository($path); $command = csprintf('git-upload-pack -- %s', $repository->getLocalPath()); $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $future = id(new ExecFuture('%C', $command))->setEnv($this->getEnvironment()); $err = $this->newPassthruCommand()->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->execute(); if (!$err) { $this->waitForGitClient(); } return $err; }
public function getLocalCommitInformation() { if ($this->repositoryHasNoCommits) { // Zero commits. throw new Exception("You can't get local commit information for a repository with no " . "commits."); } else { if ($this->getBaseCommit() == self::GIT_MAGIC_ROOT_COMMIT) { // One commit. $against = 'HEAD'; } else { // 2..N commits. We include commits reachable from HEAD which are // not reachable from the relative commit; this is consistent with // user expectations even though it is not actually the diff range. // Particularly: // // | // D <----- master branch // | // C Y <- feature branch // | /| // B X // | / // A // | // // If "A, B, C, D" are master, and the user is at Y, when they run // "arc diff B" they want (and get) a diff of B vs Y, but they think about // this as being the commits X and Y. If we log "B..Y", we only show // Y. With "Y --not B", we show X and Y. $against = csprintf('%s --not %s', 'HEAD', $this->getBaseCommit()); } } // NOTE: Windows escaping of "%" symbols apparently is inherently broken; // when passed throuhgh escapeshellarg() they are replaced with spaces. // TODO: Learn how cmd.exe works and find some clever workaround? // NOTE: If we use "%x00", output is truncated in Windows. list($info) = $this->execxLocal(phutil_is_windows() ? 'log %C --format=%C --' : 'log %C --format=%s --', $against, '%H%x01%T%x01%P%x01%at%x01%an%x01%aE%x01%s%x01%s%n%n%b%x02'); $commits = array(); $info = trim($info, " \n"); if (!strlen($info)) { return array(); } $info = explode("", $info); foreach ($info as $line) { list($commit, $tree, $parents, $time, $author, $author_email, $title, $message) = explode("", trim($line), 8); $message = rtrim($message); $commits[$commit] = array('commit' => $commit, 'tree' => $tree, 'parents' => array_filter(explode(' ', $parents)), 'time' => $time, 'author' => $author, 'summary' => $title, 'message' => $message, 'authorEmail' => $author_email); } return $commits; }
public function execute(PhutilArgumentParser $args) { $api = $this->getSingleAPI(); list($host, $port) = $this->getBareHostAndPort($api->getHost()); $flag_port = $port ? csprintf('--port %d', $port) : ''; $flag_password = ''; $password = $api->getPassword(); if ($password) { if (strlen($password->openEnvelope())) { $flag_password = csprintf('--password=%P', $password); } } return phutil_passthru('mysql --protocol=TCP --default-character-set=utf8mb4 ' . '-u %s %C -h %s %C', $api->getUser(), $flag_password, $host, $flag_port); }
public function buildTestFuture($test_output, $cover_output) { $paths = $this->getPaths(); # We want to run the tests inside the VM. # `vagrant ssh -c` will return the exit code of whatever command you pass, # but we need it to always return 0. Hence the `|| true`. # Set SYNC_ENGINE_REPO_VAGRANT_PATH to the location of the sync engine repo # within your VM. By default this is /vagrant via stock setup.sh but you # may move it elsewhere! TODO just use a symlink for this later $se_path = getenv('SYNC_ENGINE_REPO_VAGRANT_PATH'); if (!$se_path) { $se_path = '/vagrant'; } $cmd_line = csprintf("vagrant ssh -c \"export NYLAS_ENV=test; cd {$se_path}; coverage run --source {$se_path}/inbox -m py.test --junitxml {$se_path}/tests/output {$se_path}/tests; coverage xml -i -o {$se_path}/tests/coverage; true\""); return new ExecFuture('%C', $cmd_line); }
protected function executeChecks() { $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); if (strpos(AphrontRequest::getHTTPHeader('Host'), '.') === false) { $summary = pht('The domain does not contain a dot. This is necessary for some web ' . 'browsers to be able to set cookies.'); $message = pht('The domain in the base URI must contain a dot ("."), e.g. ' . '"http://example.com", not just a bare name like "http://example/". ' . 'Some web browsers will not set cookies on domains with no TLD.'); $this->newIssue('config.phabricator.domain')->setShortName(pht('Dotless Domain'))->setName(pht('No Dot Character in Domain'))->setSummary($summary)->setMessage($message)->setIsFatal(true); } if ($base_uri) { return; } $base_uri_guess = PhabricatorEnv::getRequestBaseURI(); $summary = pht('The base URI for this install is not configured. Configuring it will ' . 'improve security and enable features.'); $message = pht('The base URI for this install is not configured. Configuring it will ' . 'improve security and allow background processes (like daemons and ' . 'scripts) to generate links.' . "\n\n" . 'You should set the base URI to the URI you will use to access ' . 'Phabricator, like "http://phabricator.example.com/".' . "\n\n" . 'Include the protocol (http or https), domain name, and port number if ' . 'you are using a port other than 80 (http) or 443 (https).' . "\n\n" . 'Based on this request, it appears that the correct setting is:' . "\n\n" . '%s' . "\n\n" . 'To configure the base URI, run the command shown below.', $base_uri_guess); $this->newIssue('config.phabricator.base-uri')->setShortName(pht('No Base URI'))->setName(pht('Base URI Not Configured'))->setSummary($summary)->setMessage($message)->addCommand(hsprintf('<tt>phabricator/ $</tt> %s', csprintf('./bin/config set phabricator.base-uri %s', $base_uri_guess))); }