private function runTests()
 {
     $root = $this->getWorkingCopy()->getProjectRoot();
     $script = $this->getConfiguredScript();
     $path = $this->getConfiguredTestResultPath();
     foreach (glob($root . DIRECTORY_SEPARATOR . $path . "/*.xml") as $filename) {
         // Remove existing files so we cannot report old results
         $this->unlink($filename);
     }
     // Provide changed paths to process
     putenv("ARCANIST_DIFF_PATHS=" . implode(PATH_SEPARATOR, $this->getPaths()));
     $future = new ExecFuture('%C %s', $script, $path);
     $future->setCWD($root);
     $err = null;
     try {
         $future->resolvex();
     } catch (CommandException $exc) {
         $err = $exc;
     }
     $results = $this->parseTestResults($root . DIRECTORY_SEPARATOR . $path);
     if ($err) {
         $result = new ArcanistUnitTestResult();
         $result->setName('Unit Test Script');
         $result->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
         $result->setUserData("ERROR: Command failed with code {$err->getError()}\nCOMMAND: `{$err->getCommand()}`");
         $results[] = $result;
     }
     return $results;
 }
Exemplo n.º 2
0
 /**
  * Returns an array of the full canonical paths to all the Maven directories
  * (directories containing pom.xml files) in the project.
  */
 private function findMavenDirectories()
 {
     if (file_exists($this->project_root . "/.git")) {
         // The fastest way to find all the pom.xml files is to let git scan
         // its index.
         $future = new ExecFuture('git ls-files \\*/pom.xml');
     } else {
         // Not a git repo. Do it the old-fashioned way.
         $future = new ExecFuture('find . -name pom.xml -print');
     }
     // TODO: This will find *all* the pom.xml files in the working copy.
     // Need to obey the optional paths argument to "arc unit" to let users
     // run just a subset of tests.
     $future->setCWD($this->project_root);
     list($stdout) = $future->resolvex();
     $poms = explode("\n", trim($stdout));
     if (!$poms) {
         throw new Exception("No pom.xml files found");
     }
     $maven_dirs = array_map(function ($pom) {
         $maven_dir = dirname($pom);
         return realpath($this->project_root . '/' . $maven_dir);
     }, $poms);
     return $maven_dirs;
 }
 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 run()
 {
     $working_copy = $this->getWorkingCopy();
     $this->projectRoot = $working_copy->getProjectRoot();
     $junit_tmp = new TempFile();
     $cover_tmp = new TempFile();
     $future = $this->buildTestFuture($junit_tmp, $cover_tmp);
     $future->resolvex();
     $future = new ExecFuture('coverage xml -o %s', $cover_tmp);
     $future->setCWD($this->projectRoot);
     $future->resolvex();
     return $this->parseTestResults($junit_tmp, $cover_tmp);
 }
Exemplo n.º 5
0
 public function __construct($sockname, $repo_root, $args, array $command)
 {
     $this->command = json_encode($command);
     $console = PhutilConsole::getConsole();
     $console->writeLog("cli query: %s\n", $this->command);
     $future = new ExecFuture("%C {$args} -U %s %s %s %s %s", "{$repo_root}/watchman", $sockname, '--no-pretty', '--no-spawn', '--no-local', '-j');
     $cwd = getcwd();
     if (!$cwd) {
         throw new Exception("can't figure out my cwd!?");
     }
     $future->setCWD($cwd);
     $future->write($this->command . "\n");
     parent::__construct($future);
 }
 public function willLintPaths(array $paths)
 {
     $script = $this->getConfiguredScript();
     $root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
     $futures = array();
     foreach ($paths as $path) {
         $future = new ExecFuture('%C %s', $script, $path);
         $future->setCWD($root);
         $futures[$path] = $future;
     }
     foreach (Futures($futures)->limit(4) as $path => $future) {
         list($stdout) = $future->resolvex();
         $this->output[$path] = $stdout;
     }
 }
 private function checkRequirements()
 {
     $working_copy = $this->getWorkingCopy();
     $project_root = $working_copy->getProjectRoot();
     $piplint_files = $this->getConfigurationManager()->getConfigFromAnySource('unit.piplint.files');
     if (empty($piplint_files)) {
         return;
     }
     $args = array('piplint');
     $args = array_merge($args, $piplint_files);
     $cmd = implode(' ', $args);
     $future = new ExecFuture($cmd);
     $future->setCWD($project_root);
     $future->resolvex();
 }
Exemplo n.º 8
0
 public function run()
 {
     $working_copy = $this->getWorkingCopy();
     $this->projectRoot = $working_copy->getProjectRoot();
     $junit_tmp = new TempFile();
     $cover_tmp = new TempFile();
     $future = $this->buildTestFuture($junit_tmp, $cover_tmp);
     list($err, $stdout, $stderr) = $future->resolve();
     if (!Filesystem::pathExists($junit_tmp)) {
         throw new CommandException(pht('Command failed with error #%s!', $err), $future->getCommand(), $err, $stdout, $stderr);
     }
     $future = new ExecFuture('coverage xml -o %s', $cover_tmp);
     $future->setCWD($this->projectRoot);
     $future->resolvex();
     return $this->parseTestResults($junit_tmp, $cover_tmp);
 }
Exemplo n.º 9
0
 public function run()
 {
     $working_copy = $this->getWorkingCopy();
     $this->projectRoot = $working_copy->getProjectRoot();
     $future = new ExecFuture('npm run coverage');
     $future->setCWD($this->projectRoot);
     list($err, $stdout, $stderr) = $future->resolve();
     $result = new ArcanistUnitTestResult();
     $result->setName("Node test engine");
     $result->setUserData($stdout);
     if ($err) {
         $result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
     } else {
         $result->setResult(ArcanistUnitTestResult::RESULT_PASS);
     }
     return array($result);
 }
 private function runTests()
 {
     $root = $this->getWorkingCopy()->getProjectRoot();
     $script = $this->getConfiguredScript();
     $path = $this->getConfiguredTestResultPath();
     foreach (glob($path . "/*.xml") as $filename) {
         // Remove existing files so we cannot report old results
         $this->unlink($filename);
     }
     $future = new ExecFuture('%C %s', $script, $path);
     $future->setCWD($root);
     try {
         $future->resolvex();
     } catch (CommandException $exc) {
         if ($exc->getError() != 0) {
             throw $exc;
         }
     }
     return $this->parseTestResults($path);
 }
Exemplo n.º 11
0
 /**
  * Build the future for running a unit test. This can be overridden to enable
  * support for code coverage via another tool.
  *
  * @param  string  Name of the test assembly.
  * @return array   The future, output filename and coverage filename
  *                 stored in an array.
  */
 protected function buildTestFuture($test_assembly)
 {
     // FIXME: Can't use TempFile here as xUnit doesn't like
     // UNIX-style full paths. It sees the leading / as the
     // start of an option flag, even when quoted.
     $xunit_temp = Filesystem::readRandomCharacters(10) . '.results.xml';
     if (file_exists($xunit_temp)) {
         unlink($xunit_temp);
     }
     $future = new ExecFuture('%C %s /xml %s', trim($this->runtimeEngine . ' ' . $this->testEngine), $test_assembly, $xunit_temp);
     $folder = Filesystem::resolvePath($this->projectRoot);
     $future->setCWD($folder);
     $combined = $folder . '/' . $xunit_temp;
     if (phutil_is_windows()) {
         $combined = $folder . '\\' . $xunit_temp;
     }
     return array($future, $combined, null);
 }
 protected final function launchDaemon($class, array $argv, $debug)
 {
     $daemon = $this->findDaemonClass($class);
     $console = PhutilConsole::getConsole();
     if ($debug) {
         if ($argv) {
             $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized) " . "with arguments %s.\n", $daemon, csprintf('%LR', $argv)));
         } else {
             $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized).\n", $daemon));
         }
     } else {
         if ($argv) {
             $console->writeOut(pht("Launching daemon \"%s\" with arguments %s.\n", $daemon, csprintf('%LR', $argv)));
         } else {
             $console->writeOut(pht("Launching daemon \"%s\".\n", $daemon));
         }
     }
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     if (!$debug) {
         $flags[] = '--daemonize';
     }
     if (!$debug) {
         $log_file = $this->getLogDirectory() . '/daemons.log';
         $flags[] = csprintf('--log=%s', $log_file);
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $flags[] = csprintf('--phd=%s', $pid_dir);
     $command = csprintf('./phd-daemon %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv));
     $phabricator_root = dirname(phutil_get_library_root('phabricator'));
     $daemon_script_dir = $phabricator_root . '/scripts/daemon/';
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array(__CLASS__, 'ignoreSignal'));
         echo "\n    phabricator/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $daemon_script_dir, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($daemon_script_dir);
         $future->resolvex();
     }
 }
 private function buildAtomizerFutures(array $file_atomizers)
 {
     $atomizers = array();
     foreach ($file_atomizers as $file => $atomizer) {
         $atomizers[$atomizer][] = $file;
     }
     $root = dirname(phutil_get_library_root('phabricator'));
     $config_root = $this->getConfig('root');
     $bar = id(new PhutilConsoleProgressBar())->setTotal(count($file_atomizers));
     $futures = array();
     foreach ($atomizers as $class => $files) {
         foreach (array_chunk($files, 32) as $chunk) {
             $future = new ExecFuture('%s atomize --ugly --book %s --atomizer %s -- %Ls', $root . '/bin/diviner', $this->getBookConfigPath(), $class, $chunk);
             $future->setCWD($config_root);
             $futures[] = $future;
             $bar->update(count($chunk));
         }
     }
     $bar->done();
     return $futures;
 }
Exemplo n.º 14
0
 protected final function buildFutures(array $paths)
 {
     $executable = $this->getExecutableCommand();
     $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags());
     $futures = array();
     foreach ($paths as $path) {
         $disk_path = $this->getEngine()->getFilePathOnDisk($path);
         $path_argument = $this->getPathArgumentForLinterFuture($disk_path);
         $future = new ExecFuture('%C %C', $bin, $path_argument);
         $future->setCWD($this->getProjectRoot());
         $futures[$path] = $future;
     }
     return $futures;
 }
 /**
  * Handle resolving "arc:*" rules.
  */
 private function resolveArcRule($rule, $name, $source)
 {
     switch ($name) {
         case 'verbose':
             $this->verbose = true;
             $this->log("Enabled verbose mode.");
             break;
         case 'prompt':
             $reason = "it is what you typed when prompted.";
             $this->api->setBaseCommitExplanation($reason);
             return phutil_console_prompt('Against which commit?');
         case 'local':
         case 'global':
         case 'project':
         case 'args':
         case 'system':
             // Push the other source on top of the list.
             array_unshift($this->try, $name);
             $this->log("Switching to source '{$name}'.");
             return false;
         case 'yield':
             // Cycle this source to the end of the list.
             $this->try[] = array_shift($this->try);
             $this->log("Yielding processing of rules from '{$source}'.");
             return false;
         case 'halt':
             // Dump the whole stack.
             $this->try = array();
             $this->log("Halting all rule processing.");
             return false;
         case 'skip':
             return null;
         case 'empty':
         case 'upstream':
         case 'outgoing':
         case 'bookmark':
         case 'amended':
         case 'this':
             return $this->api->resolveBaseCommitRule($rule, $source);
         default:
             $matches = null;
             if (preg_match('/^exec\\((.*)\\)$/', $name, $matches)) {
                 $root = $this->api->getWorkingCopyIdentity()->getProjectRoot();
                 $future = new ExecFuture('%C', $matches[1]);
                 $future->setCWD($root);
                 list($err, $stdout) = $future->resolve();
                 if (!$err) {
                     return trim($stdout);
                 } else {
                     return null;
                 }
             } else {
                 if (preg_match('/^nodiff\\((.*)\\)$/', $name, $matches)) {
                     return $this->api->resolveBaseCommitRule($rule, $source);
                 }
             }
             throw new ArcanistUsageException("Base commit rule '{$rule}' (from source '{$source}') " . "is not a recognized rule.");
     }
 }
 private function executeDaemonLaunchCommand($command, $daemon_script_dir, array $config, $run_as_user = null)
 {
     $is_sudo = false;
     if ($run_as_user) {
         // If anything else besides sudo should be
         // supported then insert it here (runuser, su, ...)
         $command = csprintf('sudo -En -u %s -- %C', $run_as_user, $command);
         $is_sudo = true;
     }
     $future = new ExecFuture('exec %C', $command);
     // Play games to keep 'ps' looking reasonable.
     $future->setCWD($daemon_script_dir);
     $future->write(json_encode($config));
     list($stdout, $stderr) = $future->resolvex();
     if ($is_sudo) {
         // On OSX, `sudo -n` exits 0 when the user does not have permission to
         // switch accounts without a password. This is not consistent with
         // sudo on Linux, and seems buggy/broken. Check for this by string
         // matching the output.
         if (preg_match('/sudo: a password is required/', $stderr)) {
             throw new Exception(pht('sudo exited with a zero exit code, but emitted output ' . 'consistent with failure under OSX.'));
         }
     }
 }
Exemplo n.º 17
0
 private function parseTestResultFile($target)
 {
     $path = "bazel-testlogs/" . str_replace(":", "/", substr($target, 2)) . "/test.cache_status";
     $future = new ExecFuture("bazel-bin/kythe/go/util/tools/print_test_status %s", $path);
     $future->setCWD($this->project_root);
     return json_decode($future->resolvex()[0]);
 }
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not known.");
     }
     $pid_dir = $this->getControlDirectory('pid');
     $log_dir = $this->getControlDirectory('log') . '/daemons.log';
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $bootloader = PhutilBootloader::getInstance();
     $all_libraries = $bootloader->getAllLibraries();
     $non_default_libraries = array_diff($all_libraries, array('phutil', 'phabricator'));
     $extra_libraries = array();
     foreach ($non_default_libraries as $library) {
         $extra_libraries[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $command = csprintf("./launch_daemon.php " . "%s " . "--load-phutil-library=%s " . "%C " . "--conduit-uri=%s " . "--phd=%s " . ($debug ? '--trace ' : '--daemonize '), $daemon, phutil_get_library_root('phabricator'), implode(' ', $extra_libraries), PhabricatorEnv::getURI('/api/'), $pid_dir);
     if (!$debug) {
         // If we're running "phd debug", send output straight to the console
         // instead of to a logfile.
         $command = csprintf("%C --log=%s", $command, $log_dir);
     }
     // Append the daemon's argv.
     $command = csprintf("%C %C", $command, implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }
Exemplo n.º 19
0
 private static function discoverGitBaseDirectory($root)
 {
     try {
         // NOTE: This awkward construction is to make sure things work on Windows.
         $future = new ExecFuture('git rev-parse --show-cdup');
         $future->setCWD($root);
         list($stdout) = $future->resolvex();
         return Filesystem::resolvePath(rtrim($stdout, "\n"), $root);
     } catch (CommandException $ex) {
         if (preg_match('/^fatal: Not a git repository/', $ex->getStdErr())) {
             return null;
         }
         throw $ex;
     }
 }
Exemplo n.º 20
0
 /**
  * Overridden version of `buildTestFuture` so that the unit test can be run
  * via `cscover`, which instruments assemblies and reports on code coverage.
  *
  * @param  string  Name of the test assembly.
  * @return array   The future, output filename and coverage filename
  *                 stored in an array.
  */
 protected function buildTestFuture($test_assembly)
 {
     if ($this->getEnableCoverage() === false) {
         return parent::buildTestFuture($test_assembly);
     }
     // FIXME: Can't use TempFile here as xUnit doesn't like
     // UNIX-style full paths. It sees the leading / as the
     // start of an option flag, even when quoted.
     $xunit_temp = Filesystem::readRandomCharacters(10) . '.results.xml';
     if (file_exists($xunit_temp)) {
         unlink($xunit_temp);
     }
     $cover_temp = new TempFile();
     $cover_temp->setPreserveFile(true);
     $xunit_cmd = $this->runtimeEngine;
     $xunit_args = null;
     if ($xunit_cmd === '') {
         $xunit_cmd = $this->testEngine;
         $xunit_args = csprintf('%s /xml %s', $test_assembly, $xunit_temp);
     } else {
         $xunit_args = csprintf('%s %s /xml %s', $this->testEngine, $test_assembly, $xunit_temp);
     }
     $assembly_dir = dirname($test_assembly);
     $assemblies_to_instrument = array();
     foreach (Filesystem::listDirectory($assembly_dir) as $file) {
         if (substr($file, -4) == '.dll' || substr($file, -4) == '.exe') {
             if ($this->assemblyShouldBeInstrumented($file)) {
                 $assemblies_to_instrument[] = $assembly_dir . DIRECTORY_SEPARATOR . $file;
             }
         }
     }
     if (count($assemblies_to_instrument) === 0) {
         return parent::buildTestFuture($test_assembly);
     }
     $future = new ExecFuture('%C -o %s -c %s -a %s -w %s %Ls', trim($this->runtimeEngine . ' ' . $this->coverEngine), $cover_temp, $xunit_cmd, $xunit_args, $assembly_dir, $assemblies_to_instrument);
     $future->setCWD(Filesystem::resolvePath($this->projectRoot));
     return array($future, $assembly_dir . DIRECTORY_SEPARATOR . $xunit_temp, $cover_temp);
 }
 /**
  * Run the script on each file to be linted.
  *
  * @task lint
  */
 public function willLintPaths(array $paths)
 {
     $root = $this->getProjectRoot();
     $futures = array();
     foreach ($paths as $path) {
         $future = new ExecFuture('%C %s', $this->script, $path);
         $future->setCWD($root);
         $futures[$path] = $future;
     }
     $futures = id(new FutureIterator($futures))->limit(4);
     foreach ($futures as $path => $future) {
         list($stdout) = $future->resolvex();
         $this->output[$path] = $stdout;
     }
 }
Exemplo n.º 22
0
 public function run()
 {
     if ($this->shouldRunSilently()) {
         echo "Running daemon '{$this->daemon}' silently. Use '--trace' or " . "'--verbose' to produce debugging output.\n";
     }
     $root = phutil_get_library_root('phutil');
     $root = dirname($root);
     $exec_dir = $root . '/scripts/daemon/exec/';
     // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this
     // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you
     // get one new process (the command you ran). When you proc_open() using
     // dash, you get two new processes: the command you ran and a parent
     // "dash -c" (or "sh -c") process. This means that the child process's PID
     // is actually the 'dash' PID, not the command's PID. To avoid this, use
     // 'exec' to replace the shell process with the real process; without this,
     // the child will call posix_getppid(), be given the pid of the 'sh -c'
     // process, and send it SIGUSR1 to keepalive which will terminate it
     // immediately. We also won't be able to do process group management because
     // the shell process won't properly posix_setsid() so the pgid of the child
     // won't be meaningful.
     // Format the exec command, which looks something like:
     //
     //   exec ./exec_daemon DaemonName --trace -- --no-discovery
     $argv = array();
     $argv[] = csprintf('exec ./exec_daemon.php %s', $this->daemon);
     foreach ($this->argv as $k => $arg) {
         $argv[] = csprintf('%s', $arg);
     }
     $argv[] = '--';
     foreach ($this->moreArgs as $k => $arg) {
         $argv[] = csprintf('%s', $arg);
     }
     $command = implode(' ', $argv);
     while (true) {
         $this->logMessage('INIT', 'Starting process.');
         $future = new ExecFuture('%C', $command);
         $future->setCWD($exec_dir);
         $future->setStdoutSizeLimit($this->captureBufferSize);
         $future->setStderrSizeLimit($this->captureBufferSize);
         $this->deadline = time() + $this->deadlineTimeout;
         $this->heartbeat = time() + self::HEARTBEAT_WAIT;
         $future->isReady();
         $this->childPID = $future->getPID();
         do {
             do {
                 if ($this->traceMemory) {
                     $memuse = number_format(memory_get_usage() / 1024, 1);
                     $this->logMessage('RAMS', 'Overseer Memory Usage: ' . $memuse . ' KB');
                 }
                 // We need a shortish timeout here so we can run the tick handler
                 // frequently in order to process signals.
                 $result = $future->resolve(1);
                 list($stdout, $stderr) = $future->read();
                 $stdout = trim($stdout);
                 $stderr = trim($stderr);
                 if (strlen($stdout)) {
                     $this->logMessage('STDO', $stdout);
                 }
                 if (strlen($stderr)) {
                     $this->logMessage('STDE', $stderr);
                 }
                 $future->discardBuffers();
                 if ($result !== null) {
                     list($err) = $result;
                     if ($err) {
                         $this->logMessage('FAIL', 'Process exited with error ' . $err . '.', $err);
                     } else {
                         $this->logMessage('DONE', 'Process exited successfully.');
                     }
                     break 2;
                 }
                 if ($this->heartbeat < time()) {
                     $this->heartbeat = time() + self::HEARTBEAT_WAIT;
                     $this->dispatchEvent(self::EVENT_DID_HEARTBEAT);
                 }
             } while (time() < $this->deadline);
             $this->logMessage('HANG', 'Hang detected. Restarting process.');
             $this->annihilateProcessGroup();
         } while (false);
         if ($this->inGracefulShutdown) {
             // If we just exited because of a graceful shutdown, break now.
             break;
         }
         $this->logMessage('WAIT', 'Waiting to restart process.');
         sleep(self::RESTART_WAIT);
         if ($this->inGracefulShutdown) {
             // If we were awakend by a graceful shutdown, break now.
             break;
         }
     }
     // This is a clean exit after a graceful shutdown.
     $this->dispatchEvent(self::EVENT_WILL_EXIT);
     exit(0);
 }
Exemplo n.º 23
0
 public function launchDaemon($daemon, array $argv, $debug = false)
 {
     $symbols = $this->loadAvailableDaemonClasses();
     $symbols = ipull($symbols, 'name', 'name');
     if (empty($symbols[$daemon])) {
         throw new Exception("Daemon '{$daemon}' is not loaded, misspelled or abstract.");
     }
     $libphutil_root = dirname(phutil_get_library_root('phutil'));
     $launch_daemon = $libphutil_root . '/scripts/daemon/';
     foreach ($argv as $key => $arg) {
         $argv[$key] = escapeshellarg($arg);
     }
     $flags = array();
     if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) {
         $flags[] = '--trace';
     }
     if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
         $flags[] = '--verbose';
     }
     if (!$debug) {
         $flags[] = '--daemonize';
     }
     $bootloader = PhutilBootloader::getInstance();
     foreach ($bootloader->getAllLibraries() as $library) {
         if ($library == 'phutil') {
             // No need to load libphutil, it's necessarily loaded implicitly by the
             // daemon itself.
             continue;
         }
         $flags[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library));
     }
     $flags[] = csprintf('--conduit-uri=%s', PhabricatorEnv::getURI('/api/'));
     if (!$debug) {
         $log_file = $this->getLogDirectory() . '/daemons.log';
         $flags[] = csprintf('--log=%s', $log_file);
     }
     $pid_dir = $this->getPIDDirectory();
     // TODO: This should be a much better user experience.
     Filesystem::assertExists($pid_dir);
     Filesystem::assertIsDirectory($pid_dir);
     Filesystem::assertWritable($pid_dir);
     $flags[] = csprintf('--phd=%s', $pid_dir);
     $command = csprintf('./launch_daemon.php %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv));
     if ($debug) {
         // Don't terminate when the user sends ^C; it will be sent to the
         // subprocess which will terminate normally.
         pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal'));
         echo "\n    libphutil/scripts/daemon/ \$ {$command}\n\n";
         phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command);
     } else {
         $future = new ExecFuture('exec %C', $command);
         // Play games to keep 'ps' looking reasonable.
         $future->setCWD($launch_daemon);
         $future->resolvex();
     }
 }
 private static function discoverGitBaseDirectory($root)
 {
     try {
         // NOTE: This awkward construction is to make sure things work on Windows.
         $future = new ExecFuture('git rev-parse --show-cdup');
         $future->setCWD($root);
         list($stdout) = $future->resolvex();
         return Filesystem::resolvePath(rtrim($stdout, "\n"), $root);
     } catch (CommandException $ex) {
         // This might be because the $root isn't a Git working copy, or the user
         // might not have Git installed at all so the `git` command fails. Assume
         // that users trying to work with git working copies will have a working
         // `git` binary.
         return null;
     }
 }
 private function processCoverageResults($project_root, $results)
 {
     $time_start = microtime_float();
     // generate annotated source files to find out which lines have
     // coverage
     // limit files to only those "*.py" files in getPaths()
     $pythonPathsStr = join(" ", $this->getPythonPaths());
     $future = new ExecFuture("coverage annotate {$pythonPathsStr}");
     $future->setCWD($project_root);
     try {
         $future->resolvex();
     } catch (CommandException $exc) {
         if ($exc->getError() > 1) {
             // 'nose' returns 1 when tests are failing/broken.
             throw $exc;
         }
     }
     // store all the coverage results for this project
     $coverageArray = array();
     $lines_total = 0;
     $lines_executable = 0;
     $lines_not_executable = 0;
     $lines_covered = 0;
     $lines_not_covered = 0;
     // walk through project directory, searching for all ",cover" files
     // that coverage.py left behind
     foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(".")) as $path) {
         // paths are given as "./path/to/file.py,cover", so match the
         // "path/to/file.py" part
         if (!preg_match(":^\\./(.*),cover\$:", $path, $matches)) {
             continue;
         }
         $srcFilePath = $matches[1];
         $coverageStr = "";
         foreach (file($path) as $coverLine) {
             /*
                 python coverage
                 > executed
                 ! missing(not executed)
                 - excluded
             
                 phab coverage
                 N Not executable. This is a comment or whitespace which should be ignored when computing test coverage.
                 C Covered. This line has test coverage.
                 U Uncovered. This line is executable but has no test coverage.
                 X Unreachable. If your coverage analysis can detect unreachable code, you can report it here.
             */
             $lines_total++;
             switch ($coverLine[0]) {
                 case '>':
                     $lines_covered++;
                     $lines_executable++;
                     $coverageStr .= 'C';
                     break;
                 case '!':
                     $lines_not_covered++;
                     $lines_executable++;
                     $coverageStr .= 'U';
                     break;
                 case ' ':
                     $lines_not_executable++;
                     $coverageStr .= 'N';
                     break;
                 case '-':
                     $coverageStr .= 'X';
                     break;
                 default:
                     break;
             }
         }
         // delete the ,cover file
         unlink($path);
         // only add to coverage report if the path was originally
         // specified by arc
         if (in_array($srcFilePath, $this->getPaths())) {
             $coverageArray[$srcFilePath] = $coverageStr;
         }
     }
     $lines_percentage = bcdiv($lines_covered, $lines_executable, 4) * 100;
     $time_end = microtime_float();
     $time = $time_end - $time_start;
     // python does not support per-test coverage results so just put all the coverage
     // in a single 'coverage test'
     $result = new ArcanistUnitTestResult();
     $result->setNamespace('coverage');
     $result->setName('coverage');
     $result->setResult('pass');
     $result->setDuration($time);
     $result->setUserData("coverage: {$lines_percentage}% executable: {$lines_executable} / covered: {$lines_covered}");
     $result->setCoverage($coverageArray);
     $results[] = $result;
     return $results;
 }
 protected function executeRepositoryOperations()
 {
     $repository = $this->getRepository();
     $args = $this->getArgs();
     if (!$args->getArg('tunnel')) {
         throw new Exception(pht('Expected `%s`!', 'svnserve -t'));
     }
     if ($this->shouldProxy()) {
         $command = $this->getProxyCommand();
         $this->isProxying = true;
         $cwd = null;
     } else {
         $command = csprintf('svnserve -t --tunnel-user=%s', $this->getUser()->getUsername());
         $cwd = PhabricatorEnv::getEmptyCWD();
     }
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     $future = new ExecFuture('%C', $command);
     // If we're receiving a commit, svnserve will fail to execute the commit
     // hook with an unhelpful error if the CWD isn't readable by the user we
     // are sudoing to. Switch to a readable, empty CWD before running
     // svnserve. See T10941.
     if ($cwd !== null) {
         $future->setCWD($cwd);
     }
     $this->inProtocol = new DiffusionSubversionWireProtocol();
     $this->outProtocol = new DiffusionSubversionWireProtocol();
     $this->command = id($this->newPassthruCommand())->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->setWillReadCallback(array($this, 'willReadMessageCallback'));
     $this->command->setPauseIOReads(true);
     $err = $this->command->execute();
     if (!$err && $this->didSeeWrite) {
         $this->getRepository()->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY);
     }
     return $err;
 }
Exemplo n.º 27
0
 /**
  * Starts a Mercurial process which can actually handle requests.
  *
  * @return ArcanistHgServerChannel  Channel to the Mercurial server.
  * @task hg
  */
 private function startMercurialProcess()
 {
     // NOTE: "cmdserver.log=-" makes Mercurial use the 'd'ebug channel for
     // log messages.
     $future = new ExecFuture('HGPLAIN=1 hg --config cmdserver.log=- serve --cmdserver pipe');
     $future->setCWD($this->workingCopy);
     $channel = new PhutilExecChannel($future);
     $hg = new ArcanistHgServerChannel($channel);
     // The server sends a "hello" message with capability and encoding
     // information. Save it and forward it to clients when they connect.
     $this->hello = $hg->waitForMessage();
     return $hg;
 }
Exemplo n.º 28
0
 public function run()
 {
     if ($this->shouldRunSilently()) {
         echo "Running daemon '{$this->daemon}' silently. Use '--trace' or " . "'--verbose' to produce debugging output.\n";
     }
     $root = phutil_get_library_root('phutil');
     $root = dirname($root);
     $exec_dir = $root . '/scripts/daemon/exec/';
     // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this
     // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you
     // get one new process (the command you ran). When you proc_open() using
     // dash, you get two new processes: the command you ran and a parent
     // "dash -c" (or "sh -c") process. This means that the child process's PID
     // is actually the 'dash' PID, not the command's PID. To avoid this, use
     // 'exec' to replace the shell process with the real process; without this,
     // the child will call posix_getppid(), be given the pid of the 'sh -c'
     // process, and send it SIGUSR1 to keepalive which will terminate it
     // immediately. We also won't be able to do process group management because
     // the shell process won't properly posix_setsid() so the pgid of the child
     // won't be meaningful.
     $exec_daemon = './exec_daemon.php';
     $argv = $this->argv;
     array_unshift($argv, 'exec', $exec_daemon, $this->daemon);
     foreach ($argv as $k => $arg) {
         $argv[$k] = escapeshellarg($arg);
     }
     $command = implode(' ', $argv);
     while (true) {
         $this->logMessage('INIT', 'Starting process.');
         $future = new ExecFuture($command);
         $future->setCWD($exec_dir);
         $future->setStdoutSizeLimit($this->captureBufferSize);
         $future->setStderrSizeLimit($this->captureBufferSize);
         $this->deadline = time() + $this->deadlineTimeout;
         $this->heartbeat = time() + self::HEARTBEAT_WAIT;
         $future->isReady();
         $this->childPID = $future->getPID();
         do {
             do {
                 if ($this->traceMemory) {
                     $memuse = number_format(memory_get_usage() / 1024, 1);
                     $this->logMessage('RAMS', 'Overseer Memory Usage: ' . $memuse . ' KB');
                 }
                 // We need a shortish timeout here so we can run the tick handler
                 // frequently in order to process signals.
                 $result = $future->resolve(1);
                 list($stdout, $stderr) = $future->read();
                 $stdout = trim($stdout);
                 $stderr = trim($stderr);
                 if (strlen($stdout)) {
                     $this->logMessage('STDO', $stdout, $stdout);
                 }
                 if (strlen($stderr)) {
                     $this->logMessage('STDE', $stderr, $stderr);
                 }
                 $future->discardBuffers();
                 if ($result !== null) {
                     list($err) = $result;
                     if ($err) {
                         $this->logMessage('FAIL', 'Process exited with error ' . $err . '.', $err);
                     } else {
                         $this->logMessage('DONE', 'Process exited successfully.');
                     }
                     break 2;
                 }
                 if ($this->heartbeat < time()) {
                     $this->heartbeat = time() + self::HEARTBEAT_WAIT;
                     if ($this->conduitURI) {
                         try {
                             $this->conduit = new ConduitClient($this->conduitURI);
                             $this->conduit->callMethodSynchronous('daemon.setstatus', array('daemonLogID' => $this->daemonLogID, 'status' => 'run'));
                         } catch (Exception $ex) {
                         }
                     }
                 }
             } while (time() < $this->deadline);
             $this->logMessage('HANG', 'Hang detected. Restarting process.');
             $this->annihilateProcessGroup();
         } while (false);
         $this->logMessage('WAIT', 'Waiting to restart process.');
         sleep($this->restartDelay);
     }
 }
 protected final function buildFutures(array $paths)
 {
     $this->getAnalyzeCommands();
     $futures = array();
     foreach ($paths as $path) {
         $disk_path = $this->getEngine()->getFilePathOnDisk($path);
         if (!array_key_exists($disk_path, $this->analyzeCommands)) {
             throw new Exception(sprintf("%s\n\nNo command for path %s. Is it included in the project?\n", pht('Linter failed to parse xcodebuild commands.'), $path));
         }
         $future = new ExecFuture('%C', $this->analyzeCommands[$disk_path]);
         $future->setCWD($this->getProjectRoot());
         $futures[$path] = $future;
     }
     return $futures;
 }
Exemplo n.º 30
0
 protected final function buildFutures(array $paths)
 {
     $executable = $this->getExecutableCommand();
     $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags());
     $futures = array();
     foreach ($paths as $path) {
         if ($this->supportsReadDataFromStdin()) {
             $future = new ExecFuture('%C %C', $bin, $this->getReadDataFromStdinFilename());
             $future->write($this->getEngine()->loadData($path));
         } else {
             // TODO: In commit hook mode, we need to do more handling here.
             $disk_path = $this->getEngine()->getFilePathOnDisk($path);
             $path_argument = $this->getPathArgumentForLinterFuture($disk_path);
             $future = new ExecFuture('%C %C', $bin, $path_argument);
         }
         $future->setCWD($this->getEngine()->getWorkingCopy()->getProjectRoot());
         $futures[$path] = $future;
     }
     return $futures;
 }