public static function loadOneSkinSpecification($name) { // Only allow skins which we know to exist to load. This prevents loading // skins like "../../secrets/evil/". $all = self::loadAllSkinSpecifications(); if (empty($all[$name])) { throw new Exception(pht('Blog skin "%s" is not a valid skin!', $name)); } $paths = PhabricatorEnv::getEnvConfig('phame.skins'); $base = dirname(phutil_get_library_root('phabricator')); foreach ($paths as $path) { $path = Filesystem::resolvePath($path, $base); $skin_path = $path . DIRECTORY_SEPARATOR . $name; if (is_dir($skin_path)) { // Double check that the skin really lives in the skin directory. if (!Filesystem::isDescendant($skin_path, $path)) { throw new Exception(pht('Blog skin "%s" is not located in path "%s"!', $name, $path)); } $spec = self::loadSkinSpecification($skin_path); if ($spec) { $spec->setName($name); return $spec; } } } return null; }
protected function deleteDocumentsByHash(array $hashes) { $root = $this->getConfig('root'); $cache = $this->getPublishCache(); foreach ($hashes as $hash) { $paths = $cache->getAtomPathsFromCache($hash); foreach ($paths as $path) { $abs = $root . DIRECTORY_SEPARATOR . $path; Filesystem::remove($abs); // If the parent directory is now empty, clean it up. $dir = dirname($abs); while (true) { if (!Filesystem::isDescendant($dir, $root)) { // Directory is outside of the root. break; } if (Filesystem::listDirectory($dir)) { // Directory is not empty. break; } Filesystem::remove($dir); $dir = dirname($dir); } } $cache->removeAtomPathsFromCache($hash); $cache->deleteAtomFromIndex($hash); } }
public function testisDescendant() { $test_cases = array(array(__FILE__, dirname(__FILE__), true), array(dirname(__FILE__), dirname(dirname(__FILE__)), true), array(dirname(__FILE__), phutil_get_library_root_for_path(__FILE__), true), array(dirname(dirname(__FILE__)), dirname(__FILE__), false), array(dirname(__FILE__) . '/quack', dirname(__FILE__), false)); foreach ($test_cases as $test_case) { list($path, $root, $expected) = $test_case; $this->assertEqual($expected, Filesystem::isDescendant($path, $root), sprintf('Filesystem::isDescendant(%s, %s)', phutil_var_export($path), phutil_var_export($root))); } }
public function render() { $user = $this->getUser(); $trace = $this->trace; $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P'); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'] . '::'; } if (isset($part['function'])) { $where .= $part['function'] . '()'; } if ($file) { if (isset($callsigns[$lib])) { $attrs = array('title' => $file); try { $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]); } catch (Exception $ex) { // The database can be inaccessible. } if (empty($attrs['href'])) { $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . '$' . $part['line']; $attrs['target'] = '_blank'; } $file_name = phutil_tag('a', $attrs, $relative); } else { $file_name = phutil_tag('span', array('title' => $file), $relative); } $file_name = hsprintf('%s : %d', $file_name, $part['line']); } else { $file_name = phutil_tag('em', array(), '(Internal)'); } $rows[] = array($depth--, $lib, $file_name, $where); } $table = new AphrontTableView($rows); $table->setHeaders(array(pht('Depth'), pht('Library'), pht('File'), pht('Where'))); $table->setColumnClasses(array('n', '', '', 'wide')); return phutil_tag('div', array('class' => 'exception-trace'), $table->render()); }
public function readCoverage($cover_file) { $coverage_dom = new DOMDocument(); $coverage_dom->loadXML(Filesystem::readFile($cover_file)); $modified = $this->getPaths(); $reports = array(); $classes = $coverage_dom->getElementsByTagName("class"); foreach ($classes as $class) { $path = $class->getAttribute("filename"); $root = $this->getWorkingCopy()->getProjectRoot(); if (in_array($path, $modified) === false) { continue; } if (!Filesystem::isDescendant($path, $root)) { continue; } // get total line count in file $line_count = count(phutil_split_lines(Filesystem::readFile($path))); $coverage = ""; $start_line = 1; $lines = $this->unitTestEngineGetLines($class); for ($ii = 0; $ii < count($lines); $ii++) { $line = $lines[$ii]; $next_line = $line["number"]; for ($start_line; $start_line < $next_line; $start_line++) { $coverage .= "N"; } if ($line["hits"] === 0) { $coverage .= "U"; } else { if ($line["hits"] > 0) { $coverage .= "C"; } } $start_line++; } if ($start_line < $line_count) { foreach (range($start_line, $line_count) as $line_num) { $coverage .= "N"; } } $reports[$path] = $coverage; } return $reports; }
/** * Returns true if an application is first-party (developed by Phacility) * and false otherwise. * * @return bool True if this application is developed by Phacility. */ public final function isFirstParty() { $where = id(new ReflectionClass($this))->getFileName(); $root = phutil_get_library_root('phabricator'); if (!Filesystem::isDescendant($where, $root)) { return false; } if (Filesystem::isDescendant($where, $root . '/extensions')) { return false; } return true; }
/** * Returns the paths in which we should look for tests to execute. * * @return list<string> A list of paths in which to search for test cases. */ public function getTestPaths() { $root = $this->getWorkingCopy()->getProjectRoot(); $paths = array(); foreach ($this->getPaths() as $path) { $library_root = phutil_get_library_root_for_path($path); if (!$library_root) { continue; } $library_name = phutil_get_library_name_for_root($library_root); if (!$library_name) { throw new Exception(pht("Attempting to run unit tests on a libphutil library which has " . "not been loaded, at:\n\n" . " %s\n\n" . "This probably means one of two things:\n\n" . " - You may need to add this library to %s.\n" . " - You may be running tests on a copy of libphutil or " . "arcanist using a different copy of libphutil or arcanist. " . "This operation is not supported.\n", $library_root, '.arcconfig.')); } $path = Filesystem::resolvePath($path, $root); $library_path = Filesystem::readablePath($path, $library_root); if (!Filesystem::isDescendant($path, $library_root)) { // We have encountered some kind of symlink maze -- for instance, $path // is some symlink living outside the library that links into some file // inside the library. Just ignore these cases, since the affected file // does not actually lie within the library. continue; } if (is_file($path) && preg_match('@(?:^|/)__tests__/@', $path)) { $paths[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path); continue; } foreach (Filesystem::walkToRoot($path, $library_root) as $subpath) { if ($subpath == $library_root) { $paths[$library_name . ':.'] = array('library' => $library_name, 'path' => '__tests__/'); } else { $library_subpath = Filesystem::readablePath($subpath, $library_root); $paths[$library_name . ':' . $library_subpath] = array('library' => $library_name, 'path' => $library_subpath . '/__tests__/'); } } } return $paths; }
/** * Search for test cases for a given file in a large number of "reasonable" * locations. See @{method:getSearchLocationsForTests} for specifics. * * TODO: Add support for finding tests in testsuite folders from * phpunit.xml configuration. * * @param string PHP file to locate test cases for. * @return string|null Path to test cases, or null. */ private function findTestFile($path) { $root = $this->projectRoot; $path = Filesystem::resolvePath($path, $root); $file = basename($path); $possible_files = array($file, substr($file, 0, -4) . 'Test.php'); $search = self::getSearchLocationsForTests($path); foreach ($search as $search_path) { foreach ($possible_files as $possible_file) { $full_path = $search_path . $possible_file; if (!Filesystem::pathExists($full_path)) { // If the file doesn't exist, it's clearly a miss. continue; } if (!Filesystem::isDescendant($full_path, $root)) { // Don't look above the project root. continue; } if (0 == strcasecmp(Filesystem::resolvePath($full_path), $path)) { // Don't return the original file. continue; } return $full_path; } } return null; }
private function getTestsForPaths() { $project_root = $this->getWorkingCopy()->getProjectRoot(); $look_here = array(); foreach ($this->getPaths() as $path) { $library_root = phutil_get_library_root_for_path($path); if (!$library_root) { continue; } $library_name = phutil_get_library_name_for_root($library_root); if (!$library_name) { throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . " {$library_root}\n\n" . "This probably means one of two things:\n\n" . " - You may need to add this library to .arcconfig.\n" . " - You may be running tests on a copy of libphutil or arcanist\n" . " using a different copy of libphutil or arcanist. This\n" . " operation is not supported."); } $path = Filesystem::resolvePath($path, $project_root); if (!is_dir($path)) { $path = dirname($path); } if ($path == $library_root) { $look_here[$library_name . ':.'] = array('library' => $library_name, 'path' => ''); } else { if (!Filesystem::isDescendant($path, $library_root)) { // We have encountered some kind of symlink maze -- for instance, $path // is some symlink living outside the library that links into some file // inside the library. Just ignore these cases, since the affected file // does not actually lie within the library. continue; } else { $library_path = Filesystem::readablePath($path, $library_root); do { $look_here[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path); $library_path = dirname($library_path); } while ($library_path != '.'); } } } // Look for any class that extends ArcanistPhutilTestCase inside a // __tests__ directory in any parent directory of every affected file. // // The idea is that "infrastructure/__tests__/" tests defines general tests // for all of "infrastructure/", and those tests run for any change in // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/" // defines more specific tests that run only when rebar/ (or some // subdirectory) changes. $run_tests = array(); foreach ($look_here as $path_info) { $library = $path_info['library']; $path = $path_info['path']; $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($library)->setPathPrefix(($path ? $path . '/' : '') . '__tests__/')->setAncestorClass('ArcanistPhutilTestCase')->setConcreteOnly(true)->selectAndLoadSymbols(); foreach ($symbols as $symbol) { $run_tests[$symbol['name']] = true; } } $run_tests = array_keys($run_tests); return $run_tests; }
public function runUnitTests() { // Build any unit tests $this->make('build-tests'); // Now find all the test programs $root = $this->getProjectRoot(); $futures = array(); $cg_files = array(); $obj_files = array(); $coverage = $this->getEnableCoverage(); $tests = $this->getTestsToRun(); foreach ($tests as $relname => $test) { if ($coverage) { $cg = new TempFile(); $cg_files[$relname] = $cg; $obj_files[] = $test; $test = 'valgrind --tool=callgrind --collect-jumps=yes ' . '--callgrind-out-file=' . $cg . ' ' . $test; } $futures[$relname] = new ExecFuture($test); } $results = array(); // Use a smaller limit for SunOS because my test VM has crappy // timing characteristics and that causes timer.t to fail $limit = PHP_OS == 'SunOS' ? 1 : 4; foreach (Futures($futures)->limit($limit) as $test => $future) { list($err, $stdout, $stderr) = $future->resolve(); $results[$test] = $this->parseTestResults($test, $err, $stdout, $stderr); } if ($coverage) { // Find all executables, not just the test executables. // We need to do this because the test executables may not // include all of the possible code (linker decides to omit // it from the image) so we see a skewed representation of // the source lines. $fp = popen("find {$root} -type f -name \\*.a -o -name \\*.so -o -name \\*.dylib", "r"); while (true) { $line = fgets($fp); if ($line === false) { break; } $obj_files[] = trim($line); } // Parse line information from the objects foreach ($obj_files as $object) { PhenomDwarfLineInfo::loadObject($object); } // Now process coverage data files foreach ($cg_files as $relname => $cg) { $CG = new PhenomCallgrindFile((string) $cg); $CG->parse(); $source_files = array(); foreach ($CG->getSourceFiles() as $filename) { if (Filesystem::isDescendant($filename, $root) && !preg_match("/(thirdparty|tests)/", $filename)) { $source_files[$filename] = $filename; } } $cov = array(); foreach ($source_files as $filename) { $relsrc = substr($filename, strlen($root) + 1); $cov[$relsrc] = PhenomCallgrindFile::mergeSourceLineData($filename, array($CG)); } // Update the coverage data for that test. // arcanist will merge the final results $results[$relname]->setCoverage($cov); } } return $results; }
/** * NOTE: SPOOKY BLACK MAGIC * * When arc is run in a copy of arcanist other than itself, or a copy of * libphutil other than the one we loaded, reenter the script and force it * to use the current working directory instead of the default. * * In the case of execution inside arcanist/, we force execution of the local * arc binary. * * In the case of execution inside libphutil/, we force the local copy to load * instead of the one selected by default rules. * * @param PhutilConsole Console. * @param ArcanistWorkingCopyIdentity The current working copy. * @param array Original arc arguments. * @return void */ function reenter_if_this_is_arcanist_or_libphutil(PhutilConsole $console, ArcanistWorkingCopyIdentity $working_copy, array $original_argv) { $project_id = $working_copy->getProjectID(); if ($project_id != 'arcanist' && $project_id != 'libphutil') { // We're not in a copy of arcanist or libphutil. return; } $library_names = array('arcanist' => 'arcanist', 'libphutil' => 'phutil'); $library_root = phutil_get_library_root($library_names[$project_id]); $project_root = $working_copy->getProjectRoot(); if (Filesystem::isDescendant($library_root, $project_root)) { // We're in a copy of arcanist or libphutil, but already loaded the correct // copy. Continue execution normally. return; } if ($project_id == 'libphutil') { $console->writeLog("This is libphutil! Forcing this copy to load...\n"); $original_argv[0] = dirname(phutil_get_library_root('arcanist')) . '/bin/arc'; $libphutil_path = $project_root; } else { $console->writeLog("This is arcanist! Forcing this copy to run...\n"); $original_argv[0] = $project_root . '/bin/arc'; $libphutil_path = dirname(phutil_get_library_root('phutil')); } if (phutil_is_windows()) { $err = phutil_passthru('set ARC_PHUTIL_PATH=%s & %Ls', $libphutil_path, $original_argv); } else { $err = phutil_passthru('ARC_PHUTIL_PATH=%s %Ls', $libphutil_path, $original_argv); } exit($err); }
protected function getCommitFileList(array $revision) { $repository_api = $this->getRepositoryAPI(); $revision_id = $revision['id']; $commit_paths = $this->getConduit()->callMethodSynchronous('differential.getcommitpaths', array('revision_id' => $revision_id)); $dir_paths = array(); foreach ($commit_paths as $path) { $path = dirname($path); while ($path != '.') { $dir_paths[$path] = true; $path = dirname($path); } } $commit_paths = array_fill_keys($commit_paths, true); $status = $repository_api->getSVNStatus(); $modified_but_not_included = array(); foreach ($status as $path => $mask) { if (!empty($dir_paths[$path])) { $commit_paths[$path] = true; } if (!empty($commit_paths[$path])) { continue; } foreach ($commit_paths as $will_commit => $ignored) { if (Filesystem::isDescendant($path, $will_commit)) { throw new ArcanistUsageException("This commit includes the directory '{$will_commit}', but " . "it contains a modified path ('{$path}') which is NOT included " . "in the commit. Subversion can not handle this operation and " . "will commit the path anyway. You need to sort out the working " . "copy changes to '{$path}' before you may proceed with the " . "commit."); } } $modified_but_not_included[] = $path; } if ($modified_but_not_included) { $prefix = pht('Locally modified path(s) are not included in this revision:', count($modified_but_not_included)); $prompt = pht('They will NOT be committed. Commit this revision anyway?', count($modified_but_not_included)); $this->promptFileWarning($prefix, $prompt, $modified_but_not_included); } $do_not_exist = array(); foreach ($commit_paths as $path => $ignored) { $disk_path = $repository_api->getPath($path); if (file_exists($disk_path)) { continue; } if (is_link($disk_path)) { continue; } if (idx($status, $path) & ArcanistRepositoryAPI::FLAG_DELETED) { continue; } $do_not_exist[] = $path; unset($commit_paths[$path]); } if ($do_not_exist) { $prefix = pht('Revision includes changes to path(s) that do not exist:', count($do_not_exist)); $prompt = "Commit this revision anyway?"; $this->promptFileWarning($prefix, $prompt, $do_not_exist); } $files = array_keys($commit_paths); if (empty($files)) { throw new ArcanistUsageException("There is nothing left to commit. None of the modified paths exist."); } return $files; }
private function renderStackTrace($trace, PhabricatorUser $user) { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); $version = PhabricatorEnv::getEnvConfig('phabricator.version'); if (preg_match('/[^a-f0-9]/i', $version)) { $version = ''; } // TODO: Make this configurable? $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P'); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'] . '::'; } if (isset($part['function'])) { $where .= $part['function'] . '()'; } if ($file) { if (isset($callsigns[$lib])) { $attrs = array('title' => $file); try { $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]); } catch (Exception $ex) { // The database can be inaccessible. } if (empty($attrs['href'])) { $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . ($version && $lib == 'phabricator' ? ';' . $version : '') . '$' . $part['line']; $attrs['target'] = '_blank'; } $file_name = phutil_render_tag('a', $attrs, phutil_escape_html($relative)); } else { $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative)); } $file_name = $file_name . ' : ' . (int) $part['line']; } else { $file_name = '<em>(Internal)</em>'; } $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where)); } $table = new AphrontTableView($rows); $table->setHeaders(array('Depth', 'Library', 'File', 'Where')); $table->setColumnClasses(array('n', '', '', 'wide')); return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>'; }
public function run() { $bootloader = PhutilBootloader::getInstance(); $project_root = $this->getWorkingCopy()->getProjectRoot(); $look_here = array(); foreach ($this->getPaths() as $path) { $library_root = phutil_get_library_root_for_path($path); if (!$library_root) { continue; } $library_name = phutil_get_library_name_for_root($library_root); if (!$library_name) { throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . " {$library_root}\n\n" . "This probably means one of two things:\n\n" . " - You may need to add this library to .arcconfig.\n" . " - You may be running tests on a copy of libphutil or arcanist\n" . " using a different copy of libphutil or arcanist. This\n" . " operation is not supported."); } $path = Filesystem::resolvePath($path, $project_root); if (!is_dir($path)) { $path = dirname($path); } if ($path == $library_root) { continue; } if (!Filesystem::isDescendant($path, $library_root)) { // We have encountered some kind of symlink maze -- for instance, $path // is some symlink living outside the library that links into some file // inside the library. Just ignore these cases, since the affected file // does not actually lie within the library. continue; } $library_path = Filesystem::readablePath($path, $library_root); do { $look_here[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path); $library_path = dirname($library_path); } while ($library_path != '.'); } // Look for any class that extends ArcanistPhutilTestCase inside a // __tests__ directory in any parent directory of every affected file. // // The idea is that "infrastructure/__tests__/" tests defines general tests // for all of "infrastructure/", and those tests run for any change in // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/" // defines more specific tests that run only when rebar/ (or some // subdirectory) changes. $run_tests = array(); foreach ($look_here as $path_info) { $library = $path_info['library']; $path = $path_info['path']; $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($library)->setPathPrefix($path . '/__tests__/')->setAncestorClass('ArcanistPhutilTestCase')->setConcreteOnly(true)->selectAndLoadSymbols(); foreach ($symbols as $symbol) { $run_tests[$symbol['name']] = true; } } $run_tests = array_keys($run_tests); if (!$run_tests) { throw new ArcanistNoEffectException("No tests to run."); } $enable_coverage = $this->getEnableCoverage(); if ($enable_coverage !== false) { if (!function_exists('xdebug_start_code_coverage')) { if ($enable_coverage === true) { throw new ArcanistUsageException("You specified --coverage but xdebug is not available, so " . "coverage can not be enabled for PhutilUnitTestEngine."); } } else { $enable_coverage = true; } } $results = array(); foreach ($run_tests as $test_class) { $test_case = newv($test_class, array()); $test_case->setEnableCoverage($enable_coverage); $test_case->setProjectRoot($project_root); $test_case->setPaths($this->getPaths()); $results[] = $test_case->run(); } if ($results) { $results = call_user_func_array('array_merge', $results); } return $results; }
public function readCoverage($cover_file, $source_path) { $coverage_dom = new DOMDocument(); $coverage_dom->loadXML(Filesystem::readFile($cover_file)); $reports = array(); $classes = $coverage_dom->getElementsByTagName('class'); foreach ($classes as $class) { $path = $class->getAttribute('filename'); $root = $this->getWorkingCopy()->getProjectRoot(); if (!Filesystem::isDescendant($path, $root)) { continue; } // get total line count in file $line_count = count(phutil_split_lines(Filesystem::readFile($path))); $coverage = ''; $start_line = 1; $lines = $class->getElementsByTagName('line'); for ($ii = 0; $ii < $lines->length; $ii++) { $line = $lines->item($ii); $next_line = intval($line->getAttribute('number')); for ($start_line; $start_line < $next_line; $start_line++) { $coverage .= 'N'; } if (intval($line->getAttribute('hits')) == 0) { $coverage .= 'U'; } else { if (intval($line->getAttribute('hits')) > 0) { $coverage .= 'C'; } } $start_line++; } if ($start_line < $line_count) { foreach (range($start_line, $line_count) as $line_num) { $coverage .= 'N'; } } $reports[$path] = $coverage; } return $reports; }
private function buildRepositoryStatus(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $is_cluster = $repository->getAlmanacServicePHID(); $view = new PHUIStatusListView(); $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere('repositoryID = %d', $repository->getID()); $messages = mpull($messages, null, 'getStatusType'); if ($repository->isTracked()) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Repository Active'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')->setTarget(pht('Repository Inactive'))->setNote(pht('Activate this repository to begin or resume import.'))); return $view; } $binaries = array(); $svnlook_check = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } if ($repository->isHosted()) { if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-http-backend'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-receive-pack'; $binaries[] = 'git-upload-pack'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } } $binaries = array_unique($binaries); if (!$is_cluster) { // We're only checking for binaries if we aren't running with a cluster // configuration. In theory, we could check for binaries on the // repository host machine, but we'd need to make this more complicated // to do that. foreach ($binaries as $binary) { $where = Filesystem::resolveBinary($binary); if (!$where) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht("Unable to find this binary in the webserver's PATH. You may " . "need to configure %s.", $this->getEnvConfigLink()))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Found Binary %s', phutil_tag('tt', array(), $binary)))->setNote(phutil_tag('tt', array(), $where))); } } // This gets checked generically above. However, for svn commit hooks, we // need this to be in environment.append-paths because subversion strips // PATH. if ($svnlook_check) { $where = Filesystem::resolveBinary('svnlook'); if ($where) { $path = substr($where, 0, strlen($where) - strlen('svnlook')); $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths'); $in_path = false; foreach ($dirs as $dir) { if (Filesystem::isDescendant($path, $dir)) { $in_path = true; break; } } if (!$in_path) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht('Unable to find this binary in `%s`. ' . 'You need to configure %s and include %s.', 'environment.append-paths', $this->getEnvConfigLink(), $path))); } } } } $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $daemon_instructions = pht('Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href), pht('Managing Daemons with phd'))); $pull_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))->setLimit(1)->execute(); if ($pull_daemon) { // TODO: In a cluster environment, we need a daemon on this repository's // host, specifically, and we aren't checking for that right now. This // is a reasonable proxy for things being more-or-less correctly set up, // though. $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Pull Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Pull Daemon Not Running'))->setNote($daemon_instructions)); } $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if ($task_daemon) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Task Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Task Daemon Not Running'))->setNote($daemon_instructions)); } if ($is_cluster) { // Just omit this status check for now in cluster environments. We // could make a service call and pull it from the repository host // eventually. } else { if ($repository->usesLocalWorkingCopy()) { $local_parent = dirname($repository->getLocalPath()); if (Filesystem::pathExists($local_parent)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Storage Directory OK'))->setNote(phutil_tag('tt', array(), $local_parent))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('No Storage Directory'))->setNote(pht('Storage directory %s does not exist, or is not readable by ' . 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent)))); return $view; } $local_path = $repository->getLocalPath(); $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Initialization Error'))->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: if (Filesystem::pathExists($local_path)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Working Copy OK'))->setNote(phutil_tag('tt', array(), $local_path))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Working Copy Error'))->setNote(pht('Working copy %s has been deleted, or is not ' . 'readable by the webserver. Make this directory ' . 'readable. If it has been deleted, the daemons should ' . 'restore it automatically.', phutil_tag('tt', array(), $local_path)))); return $view; } break; case PhabricatorRepositoryStatusMessage::CODE_WORKING: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Initializing Working Copy'))->setNote(pht('Daemons are initializing the working copy.'))); return $view; default: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Unknown Init Status'))->setNote($message->getStatusCode())); return $view; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('No Working Copy Yet'))->setNote(pht('Waiting for daemons to build a working copy.'))); return $view; } } } $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $message = $message->getParameter('message'); $suggestion = null; if (preg_match('/Permission denied \\(publickey\\)./', $message)) { $suggestion = pht('Public Key Error: This error usually indicates that the ' . 'keypair you have configured does not have permission to ' . 'access the repository.'); } $message = phutil_escape_html_newlines($message); if ($suggestion !== null) { $message = array(phutil_tag('strong', array(), $suggestion), phutil_tag('br'), phutil_tag('br'), phutil_tag('em', array(), pht('Raw Error')), phutil_tag('br'), $message); } $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Update Error'))->setNote($message)); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: $ago = PhabricatorTime::getNow() - $message->getEpoch(); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Updates OK'))->setNote(pht('Last updated %s (%s ago).', phabricator_datetime($message->getEpoch(), $viewer), phutil_format_relative_time_detailed($ago)))); break; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('Waiting For Update'))->setNote(pht('Waiting for daemons to read updates.'))); } if ($repository->isImporting()) { $progress = queryfx_all($repository->establishConnection('r'), 'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d GROUP BY importStatus', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID()); $done = 0; $total = 0; foreach ($progress as $row) { $total += $row['N'] * 4; $status = $row['importStatus']; if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) { $done += $row['N']; } } if ($total) { $percentage = 100 * ($done / $total); } else { $percentage = 0; } // Cap this at "99.99%", because it's confusing to users when the actual // fraction is "99.996%" and it rounds up to "100.00%". if ($percentage > 99.98999999999999) { $percentage = 99.98999999999999; } $percentage = sprintf('%.2f%%', $percentage); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Importing'))->setNote(pht('%s Complete', $percentage))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Fully Imported'))); } if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')->setTarget(pht('Prioritized'))->setNote(pht('This repository will be updated soon!'))); } return $view; }
private function renderStackTrace($trace) { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? $host = 'https://secure.phabricator.com'; $browse = array('arcanist' => $host . '/diffusion/ARC/browse/origin:master/src/', 'phutil' => $host . '/diffusion/PHU/browse/origin:master/src/', 'phabricator' => $host . '/diffusion/P/browse/origin:master/src/'); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'] . '::'; } if (isset($part['function'])) { $where .= $part['function'] . '()'; } if ($file) { if (isset($browse[$lib])) { $file_name = phutil_render_tag('a', array('href' => $browse[$lib] . $relative . '$' . $part['line'], 'title' => $file, 'target' => '_blank'), phutil_escape_html($relative)); } else { $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative)); } $file_name = $file_name . ' : ' . (int) $part['line']; } else { $file_name = '<em>(Internal)</em>'; } $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where)); } $table = new AphrontTableView($rows); $table->setHeaders(array('Depth', 'Library', 'File', 'Where')); $table->setColumnClasses(array('n', '', '', 'wide')); return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>'; }
public function deleteKeys(array $keys) { $this->validateKeys($keys); $this->lockCache(15); foreach ($keys as $key) { $path = $this->getKeyFile($key); Filesystem::remove($path); // If removing this key leaves the directory empty, clean it up. Then // clean up any empty parent directories. $path = dirname($path); do { if (!Filesystem::isDescendant($path, $this->getCacheDirectory())) { break; } if (Filesystem::listDirectory($path, true)) { break; } Filesystem::remove($path); $path = dirname($path); } while (true); } $this->unlockCache(); return $this; }
function generateCoverageResults() { // Find all executables, not just the test executables. // We need to do this because the test executables may not // include all of the possible code (linker decides to omit // it from the image) so we see a skewed representation of // the source lines. $fp = popen("find {$this->repo_root} -type f -name watchman -o " . "-name \\*.a -o -name \\*.so -o -name \\*.dylib", "r"); while (true) { $line = fgets($fp); if ($line === false) { break; } $obj_files[] = trim($line); } // Parse line information from the objects foreach ($obj_files as $object) { DwarfLineInfo::loadObject($object); } $CG = new CallgrindFile((string) $this->cg_file); $CG->parse(); $source_files = array(); foreach ($CG->getSourceFiles() as $filename) { if (Filesystem::isDescendant($filename, $this->repo_root) && !preg_match("/(thirdparty|tests)/", $filename)) { $source_files[$filename] = $filename; } } $cov = array(); foreach ($source_files as $filename) { $relsrc = substr($filename, strlen($this->repo_root) + 1); $cov[$relsrc] = CallgrindFile::mergeSourceLineData($filename, array($CG)); } $res = new ArcanistUnitTestResult(); $res->setName('coverage'); $res->setUserData("Collected"); $res->setResult(ArcanistUnitTestResult::RESULT_PASS); $res->setCoverage($cov); // Stash it for review with our `arc cov` command $wc = ArcanistWorkingCopyIdentity::newFromPath($this->repo_root); $api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($wc); $api->writeScratchFile('wman-cov.json', json_encode($cov)); return array($res); }
public function canDestroyWorkingCopy() { if ($this->isHosted()) { // Never destroy hosted working copies. return false; } $default_path = PhabricatorEnv::getEnvConfig('repository.default-local-path'); return Filesystem::isDescendant($this->getLocalPath(), $default_path); }
/** * Returns the EditorConfig files which affect the specified path. * * Find and parse all `.editorconfig` files between the specified path and * the root directory. The results are returned in the same order that they * should be matched. * * return list<pair<string, map>> */ private function getEditorConfigs($path) { $configs = array(); $found_root = false; $root = $this->root; do { $path = dirname($path); $file = $path . '/.editorconfig'; if (!Filesystem::pathExists($file)) { continue; } $contents = Filesystem::readFile($file); $config = phutil_ini_decode($contents); if (idx($config, 'root') === true) { $found_root = true; } unset($config['root']); array_unshift($configs, array($path, $config)); if ($found_root) { break; } } while ($path != $root && Filesystem::isDescendant($path, $root)); return $configs; }