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);
  }
示例#3
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");
}
 /**
  * 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());
 }
示例#7
0
 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();
 }
示例#13
0
 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);
 }
示例#15
0
 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);
 }
示例#16
0
 /**
  * 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);
 }
示例#17
0
 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);
 }
示例#18
0
 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);
 }
示例#21
0
 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;
 }
示例#27
0
 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);
 }
示例#29
0
 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)));
 }