protected function getPathArgumentForLinterFuture($path) { $full_path = Filesystem::resolvePath($path); $ret = array($full_path); // The |path| we get fed needs to be made relative to the project_root, // otherwise the |engine| won't recognise it. $relative_path = Filesystem::readablePath($full_path, $this->getProjectRoot()); $changed = $this->getEngine()->getPathChangedLines($relative_path); if ($changed !== null) { // Convert the ordered set of changed lines to a list of ranges. $changed_lines = array_keys(array_filter($changed)); $ranges = array(array($changed_lines[0], $changed_lines[0])); foreach (array_slice($changed_lines, 1) as $line) { $range = last($ranges); if ($range[1] + 1 === $line) { ++$range[1]; $ranges[last_key($ranges)] = $range; } else { $ranges[] = array($line, $line); } } foreach ($ranges as $range) { $ret[] = sprintf('--lines=%d:%d', $range[0], $range[1]); } } return csprintf('%Ls', $ret); }
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 buildFileContentHashes() { $files = array(); $root = $this->getConfiguration()->getProjectRoot(); $finder = new FileFinder($root . '/src'); $finder->excludePath('*/.*')->withSuffix('js')->withType('f')->setGenerateChecksums(true); foreach ($finder->find() as $path => $hash) { $path = Filesystem::readablePath($path, $root); $files[$path] = $hash; } return $files; }
private function findResourcesWithSuffixes(array $suffixes) { $root = $this->getPathToResources(); $finder = id(new FileFinder($root))->withType('f')->withFollowSymlinks(true)->setGenerateChecksums(true); foreach ($suffixes as $suffix) { $finder->withSuffix($suffix); } $raw_files = $finder->find(); $results = array(); foreach ($raw_files as $path => $hash) { $readable = Filesystem::readablePath($path, $root); $results[$readable] = $hash; } return $results; }
/** * Rebuild the resource map for a resource source. * * @param CelerityPhysicalResources Resource source to rebuild. * @return void */ private function rebuildResources(CelerityPhysicalResources $resources) { $this->log(pht('Rebuilding resource source "%s" (%s)...', $resources->getName(), get_class($resources))); $binary_map = $this->rebuildBinaryResources($resources); $this->log(pht('Found %d binary resources.', new PhutilNumber(count($binary_map)))); $xformer = id(new CelerityResourceTransformer())->setMinify(false)->setRawURIMap(ipull($binary_map, 'uri')); $text_map = $this->rebuildTextResources($resources, $xformer); $this->log(pht('Found %d text resources.', new PhutilNumber(count($text_map)))); $resource_graph = array(); $requires_map = array(); $symbol_map = array(); foreach ($text_map as $name => $info) { if (isset($info['provides'])) { $symbol_map[$info['provides']] = $info['hash']; // We only need to check for cycles and add this to the requires map // if it actually requires anything. if (!empty($info['requires'])) { $resource_graph[$info['provides']] = $info['requires']; $requires_map[$info['hash']] = $info['requires']; } } } $this->detectGraphCycles($resource_graph); $name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); $hash_map = array_flip($name_map); $package_map = $this->rebuildPackages($resources, $symbol_map, $hash_map); $this->log(pht('Found %d packages.', new PhutilNumber(count($package_map)))); $component_map = array(); foreach ($package_map as $package_name => $package_info) { foreach ($package_info['symbols'] as $symbol) { $component_map[$symbol] = $package_name; } } $name_map = $this->mergeNameMaps(array(array(pht('Binary'), ipull($binary_map, 'hash')), array(pht('Text'), ipull($text_map, 'hash')), array(pht('Package'), ipull($package_map, 'hash')))); $package_map = ipull($package_map, 'symbols'); ksort($name_map); ksort($symbol_map); ksort($requires_map); ksort($package_map); $map_content = $this->formatMapContent(array('names' => $name_map, 'symbols' => $symbol_map, 'requires' => $requires_map, 'packages' => $package_map)); $map_path = $resources->getPathToMap(); $this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path))); Filesystem::writeFile($map_path, $map_content); }
public function run() { $repository_api = $this->getRepositoryAPI(); $project_root = $this->getWorkingCopy()->getProjectRoot(); $in_paths = $this->getArgument('paths'); $paths = array(); foreach ($in_paths as $key => $path) { $full_path = Filesystem::resolvePath($path); $paths[$key] = Filesystem::readablePath($full_path, $project_root); } if (!$paths) { throw new ArcanistUsageException("Specify a path to browse"); } $base_uri = $this->getBaseURI(); $browser = $this->getBrowserCommand(); foreach ($paths as $path) { $ret_code = phutil_passthru("%s %s", $browser, $base_uri . $path); if ($ret_code) { throw new ArcanistUsageException("It seems we failed to open the browser; Perhaps you should try to " . "set the 'browser' config option. The command we tried to use was: " . $browser); } } return 0; }
protected final function addLintMessage(ArcanistLintMessage $message) { $root = $this->getProjectRoot(); $path = Filesystem::resolvePath($message->getPath(), $root); $message->setPath(Filesystem::readablePath($path, $root)); $this->messages[] = $message; return $message; }
/** * Build a map of all source files in a library to hashes of their content. * Returns an array like this: * * array( * 'src/parser/ExampleParser.php' => '60b725f10c9c85c70d97880dfe8191b3', * // ... * ); * * @return dict Map of library-relative paths to content hashes. * @task source */ private function loadSourceFileMap() { $root = $this->getPath(); $init = $this->getPathForLibraryInit(); if (!Filesystem::pathExists($init)) { throw new Exception(pht("Provided path '%s' is not a %s library.", $root, 'phutil')); } $files = id(new FileFinder($root))->withType('f')->withSuffix('php')->excludePath('*/.*')->setGenerateChecksums(true)->find(); $map = array(); foreach ($files as $file => $hash) { $file = Filesystem::readablePath($file, $root); $file = ltrim($file, '/'); if (dirname($file) == '.') { // We don't permit normal source files at the root level, so just ignore // them; they're special library files. continue; } if (dirname($file) == 'extensions') { // Ignore files in the extensions/ directory. continue; } // We include also filename in the hash to handle cases when the file is // moved without modifying its content. $map[$file] = md5($hash . $file); } return $map; }
/** * 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; }
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>'; }
/** * Workflows like 'lint' and 'unit' operate on a list of working copy paths. * The user can either specify the paths explicitly ("a.js b.php"), or by * specfifying a revision ("--rev a3f10f1f") to select all paths modified * since that revision, or by omitting both and letting arc choose the * default relative revision. * * This method takes the user's selections and returns the paths that the * workflow should act upon. * * @param list List of explicitly provided paths. * @param string|null Revision name, if provided. * @param mask Mask of ArcanistRepositoryAPI flags to exclude. * Defaults to ArcanistRepositoryAPI::FLAG_UNTRACKED. * @return list List of paths the workflow should act on. */ protected function selectPathsForWorkflow(array $paths, $rev, $omit_mask = null) { if ($omit_mask === null) { $omit_mask = ArcanistRepositoryAPI::FLAG_UNTRACKED; } if ($paths) { $working_copy = $this->getWorkingCopy(); foreach ($paths as $key => $path) { $full_path = Filesystem::resolvePath($path); if (!Filesystem::pathExists($full_path)) { throw new ArcanistUsageException("Path '{$path}' does not exist!"); } $relative_path = Filesystem::readablePath($full_path, $working_copy->getProjectRoot()); $paths[$key] = $relative_path; } } else { $repository_api = $this->getRepositoryAPI(); if ($rev) { $this->parseBaseCommitArgument(array($rev)); } $paths = $repository_api->getWorkingCopyStatus(); foreach ($paths as $path => $flags) { if ($flags & $omit_mask) { unset($paths[$path]); } } $paths = array_keys($paths); } return array_values($paths); }
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>'; }
protected function addLintMessage(ArcanistLintMessage $message) { if (!$this->getEngine()->getCommitHookMode()) { $root = $this->getEngine()->getWorkingCopy()->getProjectRoot(); $path = Filesystem::resolvePath($message->getPath(), $root); $message->setPath(Filesystem::readablePath($path, $root)); } $this->messages[] = $message; return $message; }
$uri = '/res/' . substr($hash, 0, 8) . $path; $runtime_map[$path] = array('hash' => $hash, 'uri' => $uri, 'disk' => $path, 'type' => $type); } echo "\n"; $xformer = id(new CelerityResourceTransformer())->setMinify(false)->setRawResourceMap($runtime_map); echo "Finding transformable static resources...\n"; $finder = id(new FileFinder($root))->withType('f')->withSuffix('js')->withSuffix('css')->withFollowSymlinks(true)->setGenerateChecksums(true); if (!$with_custom) { $finder->excludePath('./rsrc/custom'); } $files = $finder->find(); echo "Processing " . count($files) . " files"; $file_map = array(); foreach ($files as $path => $raw_hash) { echo "."; $path = '/' . Filesystem::readablePath($path, $root); $data = Filesystem::readFile($root . $path); $data = $xformer->transformResource($path, $data); $hash = md5($data); $hash = md5($hash . $path . $resource_hash); $file_map[$path] = array('hash' => $hash, 'disk' => $path); } echo "\n"; $resource_graph = array(); $hash_map = array(); $parser = new PhutilDocblockParser(); foreach ($file_map as $path => $info) { $type = CelerityResourceTransformer::getResourceType($path); $data = Filesystem::readFile($root . $info['disk']); $matches = array(); $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
/** * Build a map of all source files in a library to hashes of their content. * Returns an array like this: * * array( * 'src/parser/ExampleParser.php' => '60b725f10c9c85c70d97880dfe8191b3', * // ... * ); * * @return dict Map of library-relative paths to content hashes. * @task source */ private function loadSourceFileMap() { $root = $this->getPath(); $init = $this->getPathForLibraryInit(); if (!Filesystem::pathExists($init)) { throw new Exception("Provided path '{$root}' is not a phutil library."); } $files = id(new FileFinder($root))->withType('f')->withSuffix('php')->excludePath('*/.*')->setGenerateChecksums(true)->find(); $map = array(); foreach ($files as $file => $hash) { if (basename($file) == '__init__.php') { // TODO: Remove this once we kill __init__.php. This just makes the // script run faster until we do, so testing and development is less // annoying. continue; } $file = Filesystem::readablePath($file, $root); $file = ltrim($file, '/'); if (dirname($file) == '.') { // We don't permit normal source files at the root level, so just ignore // them; they're special library files. continue; } // We include also filename in the hash to handle cases when the file is // moved without modifying its content. $map[$file] = md5($hash . $file); } return $map; }
public function testGetTestPaths() { $tests = array('empty' => array(array(), array()), 'test file' => array(array(__FILE__), array(__FILE__)), 'test directory' => array(array(dirname(__FILE__)), array(dirname(dirname(__FILE__)) . '/__tests__/__tests__/', dirname(dirname(__FILE__)) . '/__tests__/', dirname(dirname(dirname(__FILE__))) . '/__tests__/', phutil_get_library_root('arcanist') . '/__tests__/')), 'normal directory' => array(array(dirname(dirname(__FILE__))), array(dirname(dirname(__FILE__)) . '/__tests__/', dirname(dirname(dirname(__FILE__))) . '/__tests__/', phutil_get_library_root('arcanist') . '/__tests__/')), 'library root' => array(array(phutil_get_library_root('arcanist')), array(phutil_get_library_root('arcanist') . '/__tests__/'))); $test_engine = id(new PhutilUnitTestEngine())->setWorkingCopy($this->getWorkingCopy()); $library = phutil_get_current_library_name(); $library_root = phutil_get_library_root($library); foreach ($tests as $name => $test) { list($paths, $test_paths) = $test; $expected = array(); foreach ($test_paths as $path) { $expected[] = array('library' => $library, 'path' => Filesystem::readablePath($path, $library_root)); } $test_engine->setPaths($paths); $this->assertEqual($expected, array_values($test_engine->getTestPaths()), pht('Test paths for: "%s"', $name)); } }
private function loadSourceHash(PhutilSprite $sprite) { $inputs = array(); foreach ($this->scales as $scale) { $file = $sprite->getSourceFile($scale); // If two users have a project in different places, like: // // /home/alincoln/project // /home/htaft/project // // ...we want to ignore the `/home/alincoln` part when hashing the sheet, // since the sprites don't change when the project directory moves. If // the base path is set, build the hashes using paths relative to the // base path. $file_key = $file; if ($this->basePath) { $file_key = Filesystem::readablePath($file, $this->basePath); } if (empty($this->hashes[$file_key])) { $this->hashes[$file_key] = md5(Filesystem::readFile($file)); } $inputs[] = $file_key; $inputs[] = $this->hashes[$file_key]; } $inputs[] = $sprite->getSourceX(); $inputs[] = $sprite->getSourceY(); $inputs[] = $sprite->getSourceW(); $inputs[] = $sprite->getSourceH(); return md5(implode(':', $inputs)); }
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; }
function phutil_fail_on_unsupported_feature(XHPASTNode $node, $file, $what) { $line = $node->getLineNumber(); $message = phutil_console_wrap(pht('`%s` has limited support for features introduced after PHP 5.2.3. ' . 'This library uses an unsupported feature (%s) on line %d of %s.', 'arc liberate', $what, $line, Filesystem::readablePath($file))); $result = array('error' => $message, 'line' => $line, 'file' => $file); $json = new PhutilJSON(); echo $json->encodeFormatted($result); exit(0); }
private function loadLibraryFiles($root) { $files = id(new FileFinder($root))->withType('f')->withSuffix('php')->excludePath('*/.*')->setGenerateChecksums(true)->find(); $map = array(); foreach ($files as $file => $hash) { $file = Filesystem::readablePath($file, $root); $file = ltrim($file, '/'); if (dirname($file) == '.') { continue; } if (dirname($file) == 'extensions') { continue; } $map[$file] = md5($hash . $file); } return $map; }
/** * Get a human-readable description of the scratch file location. * * @param string Scratch file name. * @return mixed String, or false on failure. * @task scratch */ public function getReadableScratchFilePath($path) { $full_path = $this->getScratchFilePath($path); if ($full_path) { return Filesystem::readablePath($full_path, $this->getPath()); } else { return false; } }
public function raiseLintInModule($key, $code, $desc, $places, $text = null) { if ($places) { foreach ($places as $place) { list($file, $offset) = explode(':', $place); $this->willLintPath(Filesystem::readablePath($this->getModulePathOnDisk($key) . '/' . $file, $this->getEngine()->getWorkingCopy()->getProjectRoot())); return $this->raiseLintAtOffset($offset, $code, $desc, $text); } } else { $this->willLintPath($this->getModuleDisplayName($key)); return $this->raiseLintAtPath($code, $desc); } }
public function run() { $bootloader = PhutilBootloader::getInstance(); $affected_modules = 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); if (!is_dir($path)) { $path = dirname($path); } if ($path == $library_root) { continue; } $library_path = Filesystem::readablePath($path, $library_root); do { // Add the module and all parent modules as affected modules, which // means we'll look for __tests__ to run here and in any containing // module. $affected_modules[$library_name . ':' . $library_path] = array('name' => $library_name, 'root' => $library_root, 'path' => $library_path); $library_path = dirname($library_path); } while ($library_path != '.'); } $tests = array(); foreach ($affected_modules as $library_info) { $library_name = $library_info['name']; $library_root = $library_info['root']; $module = $library_info['path']; if (basename($module) == '__tests__') { // Okay, this is a __tests__ module. } else { $exists = $bootloader->moduleExists($library_name, $module . '/__tests__'); if ($exists) { // This is a module which has a __tests__ module in it. $module .= '/__tests__'; } else { // Look for a parent named __tests__. $rpos = strrpos($module, '/__tests__'); if ($rpos === false) { // No tests to run since there is no child or parent module named // __tests__. continue; } // Select the parent named __tests__. $module = substr($module, 0, $rpos + strlen('/__tests__')); } } $module_key = $library_name . ':' . $module; $tests[$module_key] = array('library' => $library_name, 'root' => $library_root, 'module' => $module); } if (!$tests) { throw new ArcanistNoEffectException("No tests to run."); } $run_tests = array(); foreach ($tests as $test) { $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($test['library'])->setModule($test['module'])->setAncestorClass('ArcanistPhutilTestCase')->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. You may need to rebuild the phutil library map."); } $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) { PhutilSymbolLoader::loadClass($test_class); $test_case = newv($test_class, array()); $test_case->setEnableCoverage($enable_coverage); $test_case->setProjectRoot($this->getWorkingCopy()->getProjectRoot()); $test_case->setPaths($this->getPaths()); $results[] = $test_case->run(); } if ($results) { $results = call_user_func_array('array_merge', $results); } return $results; }
public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $device_name = $args->getArg('device'); if (!strlen($device_name)) { throw new PhutilArgumentUsageException(pht('Specify a device with --device.')); } $device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($device_name))->executeOne(); if (!$device) { throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $device_name)); } $identify_as = $args->getArg('identify-as'); $raw_device = $device_name; if (strlen($identify_as)) { $raw_device = $identify_as; } $identity_device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($raw_device))->executeOne(); if (!$identity_device) { throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $raw_device)); } $private_key_path = $args->getArg('private-key'); if (!strlen($private_key_path)) { throw new PhutilArgumentUsageException(pht('Specify a private key with --private-key.')); } if (!Filesystem::pathExists($private_key_path)) { throw new PhutilArgumentUsageException(pht('No private key exists at path "%s"!', $private_key_path)); } $raw_private_key = Filesystem::readFile($private_key_path); $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); if (!$phd_user) { throw new PhutilArgumentUsageException(pht('Config option "phd.user" is not set. You must set this option ' . 'so the private key can be stored with the correct permissions.')); } $tmp = new TempFile(); list($err) = exec_manual('chown %s %s', $phd_user, $tmp); if ($err) { throw new PhutilArgumentUsageException(pht('Unable to change ownership of an identity file to daemon user ' . '"%s". Run this command as %s or root.', $phd_user, $phd_user)); } $stored_public_path = AlmanacKeys::getKeyPath('device.pub'); $stored_private_path = AlmanacKeys::getKeyPath('device.key'); $stored_device_path = AlmanacKeys::getKeyPath('device.id'); if (!$args->getArg('force')) { if (Filesystem::pathExists($stored_public_path)) { throw new PhutilArgumentUsageException(pht('This host already has a registered public key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_public_path))); } if (Filesystem::pathExists($stored_private_path)) { throw new PhutilArgumentUsageException(pht('This host already has a registered private key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_private_path))); } } // NOTE: We're writing the private key here so we can change permissions // on it without causing weird side effects to the file specified with // the `--private-key` flag. The file needs to have restrictive permissions // before `ssh-keygen` will willingly operate on it. $tmp_private = new TempFile(); Filesystem::changePermissions($tmp_private, 0600); execx('chown %s %s', $phd_user, $tmp_private); Filesystem::writeFile($tmp_private, $raw_private_key); list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private); $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key); $public_key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withKeys(array($key_object))->withIsActive(true)->executeOne(); if (!$public_key) { throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is not ' . 'yet known to Phabricator. Associate the public key with an ' . 'Almanac device in the web interface before registering hosts ' . 'with it.')); } if ($public_key->getObjectPHID() !== $device->getPHID()) { $public_phid = $public_key->getObjectPHID(); $public_handles = $viewer->loadHandles(array($public_phid)); $public_handle = $public_handles[$public_phid]; throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is already ' . 'associated with an object ("%s") other than the specified ' . 'device ("%s"). You can not use a single private key to identify ' . 'multiple devices or users.', $public_handle->getFullName(), $device->getName())); } if (!$public_key->getIsTrusted()) { throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'properly associated with the device, but is not yet trusted. ' . 'Trust this key before registering devices with it.')); } echo tsprintf("%s\n", pht('Installing public key...')); $tmp_public = new TempFile(); Filesystem::changePermissions($tmp_public, 0600); execx('chown %s %s', $phd_user, $tmp_public); Filesystem::writeFile($tmp_public, $raw_public_key); execx('mv -f %s %s', $tmp_public, $stored_public_path); echo tsprintf("%s\n", pht('Installing private key...')); execx('mv -f %s %s', $tmp_private, $stored_private_path); echo tsprintf("%s\n", pht('Installing device %s...', $raw_device)); // The permissions on this file are more open because the webserver also // needs to read it. $tmp_device = new TempFile(); Filesystem::changePermissions($tmp_device, 0644); execx('chown %s %s', $phd_user, $tmp_device); Filesystem::writeFile($tmp_device, $raw_device); execx('mv -f %s %s', $tmp_device, $stored_device_path); echo tsprintf("**<bg:green> %s </bg>** %s\n", pht('HOST REGISTERED'), pht('This host has been registered as "%s" and a trusted keypair ' . 'has been installed.', $raw_device)); }
* See the License for the specific language governing permissions and * limitations under the License. */ $root = dirname(dirname(dirname(__FILE__))); require_once $root . '/scripts/__init_script__.php'; if ($argc !== 1 || posix_isatty(STDIN)) { echo phutil_console_format("usage: find . -type f -name '*.php' | ./generate_php_symbols.php\n"); exit(1); } $input = file_get_contents('php://stdin'); $input = trim($input); $input = explode("\n", $input); $data = array(); $futures = array(); foreach ($input as $file) { $file = Filesystem::readablePath($file); $data[$file] = Filesystem::readFile($file); $futures[$file] = xhpast_get_parser_future($data[$file]); } foreach (Futures($futures)->limit(8) as $file => $future) { $tree = XHPASTTree::newFromDataAndResolvedExecFuture($data[$file], $future->resolve()); $root = $tree->getRootNode(); $scopes = array(); $functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION'); foreach ($functions as $function) { $name = $function->getChildByIndex(2); print_symbol($file, 'function', $name); } $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $class_name = $class->getChildByIndex(1);
private function findFilesInProject() { $raw_hashes = id(new FileFinder($this->getConfig('root')))->excludePath('*/.*')->withType('f')->setGenerateChecksums(true)->find(); $version = $this->getDivinerAtomWorldVersion(); $file_hashes = array(); foreach ($raw_hashes as $file => $md5_hash) { $rel_file = Filesystem::readablePath($file, $this->getConfig('root')); // We want the hash to change if the file moves or Diviner gets updated, // not just if the file content changes. Derive a hash from everything // we care about. $file_hashes[$rel_file] = md5("{$rel_file}{$md5_hash}{$version}") . 'F'; } return $file_hashes; }
$args = new PhutilArgumentParser($argv); $args->setTagline('regenerate CSS sprite sheets'); $args->setSynopsis(<<<EOHELP **sprites** Rebuild CSS sprite sheets. EOHELP ); $args->parseStandardArguments(); $args->parse(array(array('name' => 'source', 'param' => 'directory', 'help' => 'Directory with sprite sources.'))); $srcroot = $args->getArg('source'); if (!$srcroot) { throw new Exception("You must specify a source directory with '--source'."); } $webroot = dirname(phutil_get_library_root('phabricator')) . '/webroot/rsrc'; $webroot = Filesystem::readablePath($webroot); function glx($x) { return 60 + 48 * $x; } function gly($y) { return 110 + 48 * $y; } $sheet = new PhutilSpriteSheet(); $at = '@'; $sheet->setCSSHeader(<<<EOCSS /** * @provides autosprite-css * {$at}generated */
public function run() { $console = PhutilConsole::getConsole(); $is_force = $this->getArgument('force'); $things = $this->getArgument('paths'); if (!$things) { throw new ArcanistUsageException(pht('Specify one or more paths or objects to browse. Use the command ' . '"%s" if you want to browse this directory.', 'arc browse .')); } $things = array_fuse($things); $objects = $this->getConduit()->callMethodSynchronous('phid.lookup', array('names' => array_keys($things))); $uris = array(); foreach ($objects as $name => $object) { $uris[] = $object['uri']; $console->writeOut(pht('Opening **%s** as an object.', $name) . "\n"); unset($things[$name]); } if ($this->hasRepositoryAPI()) { $repository_api = $this->getRepositoryAPI(); $project_root = $this->getWorkingCopy()->getProjectRoot(); // First, try to resolve arguments as symbolic commits. $commits = array(); foreach ($things as $key => $thing) { if ($thing == '.') { // Git resolves '.' like HEAD, but it should be interpreted to mean // "the current directory". Just skip resolution and fall through. continue; } try { $commit = $repository_api->getCanonicalRevisionName($thing); if ($commit) { $commits[$commit] = $key; } } catch (Exception $ex) { // Ignore. } } if ($commits) { $commit_info = $this->getConduit()->callMethodSynchronous('diffusion.querycommits', array('repositoryPHID' => $this->getRepositoryPHID(), 'names' => array_keys($commits))); foreach ($commit_info['identifierMap'] as $ckey => $cphid) { $thing = $commits[$ckey]; unset($things[$thing]); $uris[] = $commit_info['data'][$cphid]['uri']; $console->writeOut(pht('Opening **%s** as a commit.', $thing) . "\n"); } } // If we fail, try to resolve them as paths. foreach ($things as $key => $path) { $lines = null; $parts = explode(':', $path); if (count($parts) > 1) { $lines = array_pop($parts); } $path = implode(':', $parts); $full_path = Filesystem::resolvePath($path); if (!$is_force && !Filesystem::pathExists($full_path)) { continue; } $console->writeOut(pht('Opening **%s** as a repository path.', $key) . "\n"); unset($things[$key]); if ($full_path == $project_root) { $path = ''; } else { $path = Filesystem::readablePath($full_path, $project_root); } $base_uri = $this->getBaseURI(); $uri = $base_uri . $path; if ($lines) { $uri = $uri . '$' . $lines; } $uris[] = $uri; } } else { if ($things) { $console->writeOut("%s\n", pht("The current working directory is not a repository working " . "copy, so remaining arguments can not be resolved as paths or " . "commits. To browse paths or symbolic commits in Diffusion, run " . "'%s' from inside a working copy.", 'arc browse')); } } foreach ($things as $thing) { $console->writeOut("%s\n", pht('Unable to find an object named **%s**, no such commit exists in ' . 'the remote, and no such path exists in the working copy. Use ' . '__%s__ to treat this as a path anyway.', $thing, '--force')); } if ($uris) { $this->openURIsInBrowser($uris); } return 0; }
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; }
private function renderFindResults(array $results) { $drequest = $this->getDiffusionRequest(); $rows = array(); foreach ($results as $result) { $href = $drequest->generateURI(array('action' => 'browse', 'path' => $result)); $readable = Filesystem::readablePath($result, $drequest->getPath()); $rows[] = array(phutil_tag('a', array('href' => $href), $readable)); } $table = id(new AphrontTableView($rows))->setHeaders(array(pht('Path')))->setColumnClasses(array('wide'))->setNoDataString(pht('The pattern you searched for did not match the names of any ' . 'files.')); return $table; }