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; }
public function run() { $paths = $this->getPaths(); $affected_tests = array(); foreach ($paths as $path) { $absolute_path = Filesystem::resolvePath($path); if (is_dir($absolute_path)) { $absolute_test_path = Filesystem::resolvePath('tests/' . $path); if (is_readable($absolute_test_path)) { $affected_tests[] = $absolute_test_path; } } if (is_readable($absolute_path)) { $filename = basename($path); $directory = dirname($path); // assumes directory layout: tests/<package>/test_<module>.py $relative_test_path = 'tests/' . $directory . '/test_' . $filename; $absolute_test_path = Filesystem::resolvePath($relative_test_path); if (is_readable($absolute_test_path)) { $affected_tests[] = $absolute_test_path; } } } return $this->runTests($affected_tests, './'); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $paths = $args->getArg('paths'); if (!$paths) { $paths = array(getcwd()); } $targets = array(); foreach ($paths as $path) { $root = Filesystem::resolvePath($path); if (!Filesystem::pathExists($root) || !is_dir($root)) { throw new PhutilArgumentUsageException(pht('Path "%s" does not exist, or is not a directory.', $path)); } $libraries = id(new FileFinder($path))->withPath('*/__phutil_library_init__.php')->find(); if (!$libraries) { throw new PhutilArgumentUsageException(pht('Path "%s" contains no libphutil libraries.', $path)); } foreach ($libraries as $library) { $targets[] = Filesystem::resolvePath(dirname($library)) . '/'; } } $targets = array_unique($targets); foreach ($targets as $library) { echo tsprintf("**<bg:blue> %s </bg>** %s\n", pht('EXTRACT'), pht('Extracting "%s"...', Filesystem::readablePath($library))); $this->extractLibrary($library); } return 0; }
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); }
protected function readBookConfiguration($book_path) { if ($book_path === null) { throw new PhutilArgumentUsageException('Specify a Diviner book configuration file with --book.'); } $book_data = Filesystem::readFile($book_path); $book = json_decode($book_data, true); if (!is_array($book)) { throw new PhutilArgumentUsageException("Book configuration '{$book_path}' is not in JSON format."); } PhutilTypeSpec::checkMap($book, array('name' => 'string', 'title' => 'optional string', 'short' => 'optional string', 'preface' => 'optional string', 'root' => 'optional string', 'uri.source' => 'optional string', 'rules' => 'optional map<regex, string>', 'exclude' => 'optional regex|list<regex>', 'groups' => 'optional map<string, map<string, wild>>')); // If the book specifies a "root", resolve it; otherwise, use the directory // the book configuration file lives in. $full_path = dirname(Filesystem::resolvePath($book_path)); if (empty($book['root'])) { $book['root'] = '.'; } $book['root'] = Filesystem::resolvePath($book['root'], $full_path); if (!preg_match('/^[a-z][a-z-]*\\z/', $book['name'])) { $name = $book['name']; throw new PhutilArgumentUsageException("Book configuration '{$book_path}' has name '{$name}', but book names " . "must include only lowercase letters and hyphens."); } foreach (idx($book, 'groups', array()) as $group) { PhutilTypeSpec::checkmap($group, array('name' => 'string', 'include' => 'optional regex|list<regex>')); } $this->bookConfigPath = $book_path; $this->config = $book; }
public function run() { $revisions = $this->getConduit()->callMethodSynchronous('differential.query', array('authors' => array($this->getUserPHID()), 'status' => 'status-open')); if (!$revisions) { echo "You have no open Differential revisions.\n"; return 0; } $repository_api = $this->getRepositoryAPI(); $info = array(); $status_len = 0; foreach ($revisions as $key => $revision) { $revision_path = Filesystem::resolvePath($revision['sourcePath']); $current_path = Filesystem::resolvePath($repository_api->getPath()); if ($revision_path == $current_path) { $info[$key]['here'] = 1; } else { $info[$key]['here'] = 0; } $info[$key]['sort'] = sprintf('%d%04d%08d', $info[$key]['here'], $revision['status'], $revision['id']); $info[$key]['statusColorized'] = BranchInfo::renderColorizedRevisionStatus($revision['statusName']); $status_len = max($status_len, strlen($info[$key]['statusColorized'])); } $info = isort($info, 'sort'); foreach ($info as $key => $spec) { $revision = $revisions[$key]; printf("%s %-" . ($status_len + 4) . "s D%d: %s\n", $spec['here'] ? phutil_console_format('**%s**', '*') : ' ', $spec['statusColorized'], $revision['id'], $revision['title']); } return 0; }
public function run() { if ($this->getRunAllTests()) { $root = $this->getWorkingCopy()->getProjectRoot(); $all_tests = glob(Filesystem::resolvePath("{$root}/tests/**/test_*.py")); return $this->runTests($all_tests, $root); } $paths = $this->getPaths(); $affected_tests = array(); foreach ($paths as $path) { $absolute_path = Filesystem::resolvePath($path); if (is_dir($absolute_path)) { $absolute_test_path = Filesystem::resolvePath('tests/' . $path); if (is_readable($absolute_test_path)) { $affected_tests[] = $absolute_test_path; } } if (is_readable($absolute_path)) { $filename = basename($path); $directory = dirname($path); // assumes directory layout: tests/<package>/test_<module>.py $relative_test_path = 'tests/' . $directory . '/test_' . $filename; $absolute_test_path = Filesystem::resolvePath($relative_test_path); if (is_readable($absolute_test_path)) { $affected_tests[] = $absolute_test_path; } } } return $this->runTests($affected_tests, './'); }
public function run() { static $color_map = array('Closed' => 'cyan', 'Needs Review' => 'magenta', 'Needs Revision' => 'red', 'Changes Planned' => 'red', 'Accepted' => 'green', 'No Revision' => 'blue', 'Abandoned' => 'default'); $revisions = $this->getConduit()->callMethodSynchronous('differential.query', array('authors' => array($this->getUserPHID()), 'status' => 'status-open')); if (!$revisions) { echo pht('You have no open Differential revisions.') . "\n"; return 0; } $repository_api = $this->getRepositoryAPI(); $info = array(); foreach ($revisions as $key => $revision) { $revision_path = Filesystem::resolvePath($revision['sourcePath']); $current_path = Filesystem::resolvePath($repository_api->getPath()); if ($revision_path == $current_path) { $info[$key]['exists'] = 1; } else { $info[$key]['exists'] = 0; } $info[$key]['sort'] = sprintf('%d%04d%08d', $info[$key]['exists'], $revision['status'], $revision['id']); $info[$key]['statusName'] = $revision['statusName']; $info[$key]['color'] = idx($color_map, $revision['statusName'], 'default'); } $table = id(new PhutilConsoleTable())->setShowHeader(false)->addColumn('exists', array('title' => ''))->addColumn('status', array('title' => pht('Status')))->addColumn('title', array('title' => pht('Title'))); $info = isort($info, 'sort'); foreach ($info as $key => $spec) { $revision = $revisions[$key]; $table->addRow(array('exists' => $spec['exists'] ? phutil_console_format('**%s**', '*') : '', 'status' => phutil_console_format("<fg:{$spec['color']}>%s</fg>", $spec['statusName']), 'title' => phutil_console_format('**D%d:** %s', $revision['id'], $revision['title']))); } $table->draw(); return 0; }
public function run() { $argv = $this->getArgument('argv'); if (count($argv) > 1) { throw new ArcanistUsageException("Provide only one path to 'arc liberate'. The path should be a " . "directory where you want to create or update a libphutil library."); } else { if (count($argv) == 0) { $path = getcwd(); } else { $path = reset($argv); } } $is_remap = $this->getArgument('remap'); $is_verify = $this->getArgument('verify'); $path = Filesystem::resolvePath($path); if (Filesystem::pathExists($path) && is_dir($path)) { $init = id(new FileFinder($path))->withPath('*/__phutil_library_init__.php')->find(); } else { $init = null; } if ($init) { if (count($init) > 1) { throw new ArcanistUsageException("Specified directory contains more than one libphutil library. Use " . "a more specific path."); } $path = Filesystem::resolvePath(dirname(reset($init)), $path); } else { $found = false; foreach (Filesystem::walkToRoot($path) as $dir) { if (Filesystem::pathExists($dir . '/__phutil_library_init__.php')) { $path = $dir; $found = true; break; } } if (!$found) { echo "No library currently exists at that path...\n"; $this->liberateCreateDirectory($path); $this->liberateCreateLibrary($path); return; } } $version = $this->getLibraryFormatVersion($path); switch ($version) { case 1: if ($this->getArgument('upgrade')) { return $this->upgradeLibrary($path); } throw new ArcanistUsageException("This library is using libphutil v1, which is no longer supported. " . "Run 'arc liberate --upgrade' to upgrade to v2."); case 2: if ($this->getArgument('upgrade')) { throw new ArcanistUsageException("Can't upgrade a v2 library!"); } return $this->liberateVersion2($path); default: throw new ArcanistUsageException("Unknown library version '{$version}'!"); } }
public function saveToArchive($path) { $tmp = new TempFile(); execx('tar -C %s -czvvf %s .', $this->getPath(), $tmp); $ok = rename($tmp, Filesystem::resolvePath($path)); if (!$ok) { throw new FilesystemException($path, 'Failed to overwrite file.'); } return $this; }
public function loadDictionary($path) { $root = $this->getProjectRoot(); $path = Filesystem::resolvePath($path, $root); $dict = phutil_json_decode(Filesystem::readFile($path)); PhutilTypeSpec::checkMap($dict, array('rules' => 'map<string, map<string, string>>')); $rules = $dict['rules']; $this->dictionaries[] = $path; $this->exactWordRules = array_merge($this->exactWordRules, idx($rules, 'exact', array())); $this->partialWordRules = array_merge($this->partialWordRules, idx($rules, 'partial', array())); }
/** * @group xhpast */ function xhpast_get_build_instructions() { $root = phutil_get_library_root('phutil'); $make = $root . '/../scripts/build_xhpast.sh'; $make = Filesystem::resolvePath($make); return <<<EOHELP Your version of 'xhpast' is unbuilt or out of date. Run this script to build it: \$ {$make} EOHELP; }
/** * Create a new lock on a lockfile. The file need not exist yet. * * @param string The lockfile to use. * @return PhutilFileLock New lock object. * * @task construct */ public static function newForPath($lockfile) { $lockfile = Filesystem::resolvePath($lockfile); $name = 'file:' . $lockfile; $lock = self::getLock($name); if (!$lock) { $lock = new PhutilFileLock($name); $lock->lockfile = $lockfile; self::registerLock($lock); } return $lock; }
function phutil_get_library_name_for_root($path) { $path = rtrim(Filesystem::resolvePath($path), '/'); $bootloader = PhutilBootloader::getInstance(); $libraries = $bootloader->getAllLibraries(); foreach ($libraries as $library) { $root = $bootloader->getLibraryRoot($library); if (rtrim(Filesystem::resolvePath($root), '/') == $path) { return $library; } } return null; }
public function run() { $this->projectRoot = $this->getWorkingCopy()->getProjectRoot(); $this->affectedTests = array(); foreach ($this->getPaths() as $path) { $path = Filesystem::resolvePath($path); // TODO: add support for directories // Users can call phpunit on the directory themselves if (is_dir($path)) { continue; } // Not sure if it would make sense to go further if // it is not a .php file if (substr($path, -4) != '.php') { continue; } if (substr($path, -8) == 'Test.php') { // Looks like a valid test file name. $this->affectedTests[$path] = $path; continue; } if ($test = $this->findTestFile($path)) { $this->affectedTests[$path] = $test; } } if (empty($this->affectedTests)) { throw new ArcanistNoEffectException('No tests to run.'); } $this->prepareConfigFile(); $futures = array(); $tmpfiles = array(); foreach ($this->affectedTests as $class_path => $test_path) { $json_tmp = new TempFile(); $clover_tmp = null; $clover = null; if ($this->getEnableCoverage() !== false) { $clover_tmp = new TempFile(); $clover = csprintf('--coverage-clover %s', $clover_tmp); } $config = $this->configFile ? csprintf('-c %s', $this->configFile) : null; $futures[$test_path] = new ExecFuture('phpunit %C --log-json %s %C %s', $config, $json_tmp, $clover, $test_path); $tmpfiles[$test_path] = array('json' => $json_tmp, 'clover' => $clover_tmp); } $results = array(); foreach (Futures($futures)->limit(4) as $test => $future) { list($err, $stdout, $stderr) = $future->resolve(); $results[] = $this->parseTestResults($test_path, $tmpfiles[$test]['json'], $tmpfiles[$test]['clover']); } return array_mergev($results); }
public function getJSHintOptions() { $working_copy = $this->getEngine()->getWorkingCopy(); $options = '--reporter ' . dirname(realpath(__FILE__)) . '/reporter.js'; $config = $working_copy->getConfig('lint.jshint.config'); if ($config !== null) { $config = Filesystem::resolvePath($config, $working_copy->getProjectRoot()); if (!Filesystem::pathExists($config)) { throw new ArcanistUsageException("Unable to find custom options file defined by 'lint.jshint.config'. " . "Make sure that the path is correct."); } $options .= ' --config ' . $config; } return $options; }
private function hasEmoji($name) { $path = Filesystem::resolvePath('rsrc/image/emoji'); if (Filesystem::pathExists($path)) { $files = Filesystem::listDirectory('rsrc/image/emoji', $include_hidden = false); foreach ($files as $file) { $info = pathinfo($file); $file_name = basename($file, '.' . $info['extension']); if ($file_name == $name) { return true; } } } return false; }
protected function parseLinterOutput($path, $err, $stdout, $stderr) { $ok = $err == 0; if (!$ok) { return false; } $root = $this->getProjectRoot(); $path = Filesystem::resolvePath($path, $root); $orig = file_get_contents($path); if ($orig == $stdout) { return array(); } $message = id(new ArcanistLintMessage())->setPath($path)->setLine(1)->setChar(1)->setGranularity(ArcanistLinter::GRANULARITY_FILE)->setCode('CFMT')->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX)->setName('Code style violation')->setDescription("'{$path}' has code style errors.")->setOriginalText($orig)->setReplacementText($stdout); return array($message); }
/** * Main entry point for the test engine. Determines what files changed * and test based on the files that have changed. * @return array Array of test results. * @throws ArcanistNoEffectException * @throws CommandException */ public function run() { /* * Determine whether it shall run all the test or more precisely the * modified file * */ if (!$this->getRunAllTests()) { $pattern_tests = $this->getAllTestsPattern(); } else { $pattern_tests = $this->getGranularTestsPattern(); } /* * Throw an error if there is no test that can be run */ if (!$pattern_tests) { throw new ArcanistNoEffectException(pht('No tests to run.')); } /* * Retrieve working copy and project root path */ $workingCopy = $this->getWorkingCopy(); $projectRoot = $workingCopy->getProjectRoot(); /* * Create new temp-file paths to receive junit test result * and lcov coverage result only if coverage is activated. */ $junit_tmp = new TempFile(); $cover_tmp = null; if ($this->getEnableCoverage() !== false) { $cover_tmp = Filesystem::resolvePath('./coverage/cobertura-coverage.xml', $projectRoot); } $future = $this->buildTestFuture($pattern_tests, $junit_tmp); list($err, $stdout, $stderr) = $future->resolve(); /* * Check that the future output desired files */ if (!Filesystem::pathExists($junit_tmp)) { throw new CommandException(pht('Testing Command failed with error #%s!', $err), $future->getCommand(), $err, $stdout, $stderr); } if ($this->getEnableCoverage() !== false) { if (!Filesystem::pathExists($cover_tmp)) { throw new CommandException(pht('Coverage Command failed with error #%s!', $err), $future->getCommand(), $err, $stdout, $stderr); } } return $this->parseTestResults($junit_tmp, $cover_tmp); }
public static function loadOneSkinSpecification($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)) { $spec = self::loadSkinSpecification($skin_path); if ($spec) { $spec->setName($name); return $spec; } } } return null; }
protected function executeChecks() { $max_allowed_packet = self::loadRawConfigValue('max_allowed_packet'); $recommended_minimum = 1024 * 1024; if ($max_allowed_packet < $recommended_minimum) { $message = pht("MySQL is configured with a very small 'max_allowed_packet' (%d), " . "which may cause some large writes to fail. Strongly consider raising " . "this to at least %d in your MySQL configuration.", $max_allowed_packet, $recommended_minimum); $this->newIssue('mysql.max_allowed_packet')->setName(pht('Small MySQL "max_allowed_packet"'))->setMessage($message)->addMySQLConfig('max_allowed_packet'); } $modes = self::loadRawConfigValue('sql_mode'); $modes = explode(',', $modes); if (!in_array('STRICT_ALL_TABLES', $modes)) { $summary = pht('MySQL is not in strict mode, but using strict mode is strongly ' . 'encouraged.'); $message = pht("On your MySQL instance, the global %s is not set to %s. " . "It is strongly encouraged that you enable this mode when running " . "Phabricator.\n\n" . "By default MySQL will silently ignore some types of errors, which " . "can cause data loss and raise security concerns. Enabling strict " . "mode makes MySQL raise an explicit error instead, and prevents this " . "entire class of problems from doing any damage.\n\n" . "You can find more information about this mode (and how to configure " . "it) in the MySQL manual. Usually, it is sufficient to add this to " . "your %s file (in the %s section) and then restart %s:\n\n" . "%s\n" . "(Note that if you run other applications against the same database, " . "they may not work in strict mode. Be careful about enabling it in " . "these cases.)", phutil_tag('tt', array(), 'sql_mode'), phutil_tag('tt', array(), 'STRICT_ALL_TABLES'), phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES')); $this->newIssue('mysql.mode')->setName(pht('MySQL STRICT_ALL_TABLES Mode Not Set'))->setSummary($summary)->setMessage($message)->addMySQLConfig('sql_mode'); } $stopword_file = self::loadRawConfigValue('ft_stopword_file'); if (!PhabricatorDefaultSearchEngineSelector::shouldUseElasticSearch()) { if ($stopword_file === null) { $summary = pht('Your version of MySQL does not support configuration of a ' . 'stopword file. You will not be able to find search results for ' . 'common words.'); $message = pht("Your MySQL instance does not support the %s option. You will not " . "be able to find search results for common words. You can gain " . "access to this option by upgrading MySQL to a more recent " . "version.\n\n" . "You can ignore this warning if you plan to configure ElasticSearch " . "later, or aren't concerned about searching for common words.", phutil_tag('tt', array(), 'ft_stopword_file')); $this->newIssue('mysql.ft_stopword_file')->setName(pht('MySQL ft_stopword_file Not Supported'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_stopword_file'); } else { if ($stopword_file == '(built-in)') { $root = dirname(phutil_get_library_root('phabricator')); $stopword_path = $root . '/resources/sql/stopwords.txt'; $stopword_path = Filesystem::resolvePath($stopword_path); $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); $summary = pht('MySQL is using a default stopword file, which will prevent ' . 'searching for many common words.'); $message = pht("Your MySQL instance is using the builtin stopword file for " . "building search indexes. This can make Phabricator's search " . "feature less useful.\n\n" . "Stopwords are common words which are not indexed and thus can not " . "be searched for. The default stopword file has about 500 words, " . "including various words which you are likely to wish to search " . "for, such as 'various', 'likely', 'wish', and 'zero'.\n\n" . "To make search more useful, you can use an alternate stopword " . "file with fewer words. Alternatively, if you aren't concerned " . "about searching for common words, you can ignore this warning. " . "If you later plan to configure ElasticSearch, you can also ignore " . "this warning: this stopword file only affects MySQL fulltext " . "indexes.\n\n" . "To choose a different stopword file, add this to your %s file " . "(in the %s section) and then restart %s:\n\n" . "%s\n" . "(You can also use a different file if you prefer. The file " . "suggested above has about 50 of the most common English words.)\n\n" . "Finally, run this command to rebuild indexes using the new " . "rules:\n\n" . "%s", phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'ft_stopword_file=' . $stopword_path), phutil_tag('pre', array(), "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); $this->newIssue('mysql.ft_stopword_file')->setName(pht('MySQL is Using Default Stopword File'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_stopword_file'); } } } $min_len = self::loadRawConfigValue('ft_min_word_len'); if ($min_len >= 4) { if (!PhabricatorDefaultSearchEngineSelector::shouldUseElasticSearch()) { $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); $summary = pht('MySQL is configured to only index words with at least %d ' . 'characters.', $min_len); $message = pht("Your MySQL instance is configured to use the default minimum word " . "length when building search indexes, which is 4. This means words " . "which are only 3 characters long will not be indexed and can not " . "be searched for.\n\n" . "For example, you will not be able to find search results for words " . "like 'SMS', 'web', or 'DOS'.\n\n" . "You can change this setting to 3 to allow these words to be " . "indexed. Alternatively, you can ignore this warning if you are " . "not concerned about searching for 3-letter words. If you later " . "plan to configure ElasticSearch, you can also ignore this warning: " . "only MySQL fulltext search is affected.\n\n" . "To reduce the minimum word length to 3, add this to your %s file " . "(in the %s section) and then restart %s:\n\n" . "%s\n" . "Finally, run this command to rebuild indexes using the new " . "rules:\n\n" . "%s", phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'ft_min_word_len=3'), phutil_tag('pre', array(), "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); $this->newIssue('mysql.ft_min_word_len')->setName(pht('MySQL is Using Default Minimum Word Length'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_min_word_len'); } } }
/** * Determines what executables and lint paths to use. Between platforms * this also changes whether the lint engine is run under .NET or Mono. It * also ensures that all of the required binaries are available for the lint * to run successfully. * * @return void */ private function loadEnvironment() { if ($this->loaded) { return; } // Determine runtime engine (.NET or Mono). if (phutil_is_windows()) { $this->runtimeEngine = ''; } else { if (Filesystem::binaryExists('mono')) { $this->runtimeEngine = 'mono '; } else { throw new Exception(pht('Unable to find Mono and you are not on Windows!')); } } // Determine cslint path. $cslint = $this->cslintHintPath; if ($cslint !== null && file_exists($cslint)) { $this->cslintEngine = Filesystem::resolvePath($cslint); } else { if (Filesystem::binaryExists('cslint.exe')) { $this->cslintEngine = 'cslint.exe'; } else { throw new Exception(pht('Unable to locate %s.', 'cslint')); } } // Determine cslint version. $ver_future = new ExecFuture('%C -v', $this->runtimeEngine . $this->cslintEngine); list($err, $stdout, $stderr) = $ver_future->resolve(); if ($err !== 0) { throw new Exception(pht('You are running an old version of %s. Please ' . 'upgrade to version %s.', 'cslint', self::SUPPORTED_VERSION)); } $ver = (int) $stdout; if ($ver < self::SUPPORTED_VERSION) { throw new Exception(pht('You are running an old version of %s. Please ' . 'upgrade to version %s.', 'cslint', self::SUPPORTED_VERSION)); } else { if ($ver > self::SUPPORTED_VERSION) { throw new Exception(pht('Arcanist does not support this version of %s (it is newer). ' . 'You can try upgrading Arcanist with `%s`.', 'cslint', 'arc upgrade')); } } $this->loaded = true; }
public function setLinterConfigurationValue($key, $value) { switch ($key) { case 'config': $working_copy = $this->getEngine()->getWorkingCopy(); $root = $working_copy->getProjectRoot(); $path = $value; if (Filesystem::pathExists($path)) { $this->configPath = $path; return; } $path = Filesystem::resolvePath($path, $root); if (Filesystem::pathExists($path)) { $this->configPath = $path; return; } throw new ArcanistUsageException(pht('None of the configured Scalafmt configs can be located.')); } return parent::setLinterConfigurationValue($key, $value); }
/** * Determine if a path is one of the paths in the list. Note that an empty * file list is considered to contain every file. * * @param string Relative or absolute system file path. * @param bool If true, consider the path to be contained in the list if * the list contains a parent directory. If false, require * that the path be part of the list explicitly. * @return bool If true, the file is in the list. * @task test */ public function contains($path, $allow_parent_directory = true) { if ($this->isEmpty()) { return true; } $path = Filesystem::resolvePath($path); if (is_dir($path)) { $path .= DIRECTORY_SEPARATOR; } foreach ($this->files as $file) { if ($file == $path) { return true; } if ($allow_parent_directory) { $len = strlen($file); if (isset($this->dirs[$file]) && !strncmp($file, $path, $len)) { return true; } } } return false; }
private function findTestForFile($file_path) { // Follow the hard rails convention: replace app/path/to/class.rb with // spec/path/to/class_spec.rb $relative_path = substr($file_path, strlen($this->projectRoot) + 1); $accpetable_directories = ['app/', 'lib/']; $found = false; foreach ($accpetable_directories as $dir) { if (0 === strpos($relative_path, $dir)) { $found = true; } } if (!$found) { return false; } $expected_loc = 'spec/' . substr($relative_path, 4, -3) . '_spec.rb'; $expected_loc = Filesystem::resolvePath($expected_loc, $this->projectRoot); if (Filesystem::pathExists($expected_loc)) { return $expected_loc; } return false; }
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; }
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 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; }
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ $package_spec = array('javelin.pkg.js' => array('javelin-util', 'javelin-install', 'javelin-event', 'javelin-stratcom', 'javelin-behavior', 'javelin-request', 'javelin-vector', 'javelin-dom', 'javelin-json', 'javelin-uri'), 'typeahead.pkg.js' => array('javelin-typeahead', 'javelin-typeahead-normalizer', 'javelin-typeahead-source', 'javelin-typeahead-preloaded-source', 'javelin-typeahead-ondemand-source', 'javelin-tokenizer', 'javelin-behavior-aphront-basic-tokenizer'), 'core.pkg.js' => array('javelin-mask', 'javelin-workflow', 'javelin-behavior-workflow', 'javelin-behavior-aphront-form-disable-on-submit', 'phabricator-keyboard-shortcut-manager', 'phabricator-keyboard-shortcut', 'javelin-behavior-phabricator-keyboard-shortcuts', 'javelin-behavior-refresh-csrf', 'javelin-behavior-phabricator-watch-anchor', 'javelin-behavior-phabricator-autofocus', 'phabricator-paste-file-upload', 'phabricator-menu-item', 'phabricator-dropdown-menu', 'javelin-behavior-phabricator-oncopy', 'phabricator-tooltip', 'javelin-behavior-phabricator-tooltips', 'phabricator-prefab'), 'core.pkg.css' => array('phabricator-core-css', 'phabricator-core-buttons-css', 'phabricator-standard-page-view', 'aphront-dialog-view-css', 'aphront-form-view-css', 'aphront-panel-view-css', 'aphront-side-nav-view-css', 'aphront-table-view-css', 'aphront-crumbs-view-css', 'aphront-tokenizer-control-css', 'aphront-typeahead-control-css', 'aphront-list-filter-view-css', 'phabricator-directory-css', 'phabricator-jump-nav', 'phabricator-app-buttons-css', 'phabricator-remarkup-css', 'syntax-highlighting-css', 'aphront-pager-view-css', 'phabricator-transaction-view-css', 'aphront-tooltip-css', 'aphront-headsup-view-css', 'phabricator-flag-css', 'aphront-error-view-css'), 'differential.pkg.css' => array('differential-core-view-css', 'differential-changeset-view-css', 'differential-results-table-css', 'differential-revision-history-css', 'differential-table-of-contents-css', 'differential-revision-comment-css', 'differential-revision-add-comment-css', 'differential-revision-comment-list-css', 'phabricator-object-selector-css', 'aphront-headsup-action-list-view-css', 'phabricator-content-source-view-css', 'differential-local-commits-view-css', 'inline-comment-summary-css'), 'differential.pkg.js' => array('phabricator-drag-and-drop-file-upload', 'phabricator-shaped-request', 'javelin-behavior-differential-feedback-preview', 'javelin-behavior-differential-edit-inline-comments', 'javelin-behavior-differential-populate', 'javelin-behavior-differential-show-more', 'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-accept-with-errors', 'javelin-behavior-differential-comment-jump', 'javelin-behavior-differential-add-reviewers-and-ccs', 'javelin-behavior-differential-keyboard-navigation', 'javelin-behavior-aphront-drag-and-drop', 'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-phabricator-object-selector', 'javelin-behavior-repository-crossreference', 'differential-inline-comment-editor', 'javelin-behavior-differential-dropdown-menus', 'javelin-behavior-buoyant'), 'diffusion.pkg.css' => array('diffusion-commit-view-css', 'diffusion-icons-css'), 'diffusion.pkg.js' => array('javelin-behavior-diffusion-pull-lastmodified', 'javelin-behavior-diffusion-commit-graph', 'javelin-behavior-audit-preview'), 'maniphest.pkg.css' => array('maniphest-task-summary-css', 'maniphest-transaction-detail-css', 'aphront-attached-file-view-css', 'phabricator-project-tag-css'), 'maniphest.pkg.js' => array('javelin-behavior-maniphest-batch-selector', 'javelin-behavior-maniphest-transaction-controls', 'javelin-behavior-maniphest-transaction-preview', 'javelin-behavior-maniphest-transaction-expand', 'javelin-behavior-maniphest-subpriority-editor')); require_once dirname(__FILE__) . '/__init_script__.php'; $args = new PhutilArgumentParser($argv); $args->setTagline('map static resources'); $args->setSynopsis("**celerity_mapper.php** [--output __path__] [--with-custom] <webroot>"); $args->parse(array(array('name' => 'output', 'param' => 'path', 'default' => '../src/__celerity_resource_map__.php', 'help' => "Set the path for resource map. It is usually useful for " . "'celerity.resource-path' configuration."), array('name' => 'with-custom', 'help' => 'Include resources in <webroot>/rsrc/custom/.'), array('name' => 'webroot', 'wildcard' => true))); $root = $args->getArg('webroot'); if (count($root) != 1 || !is_dir(reset($root))) { $args->printHelpAndExit(); } $root = Filesystem::resolvePath(reset($root)); $celerity_path = Filesystem::resolvePath($args->getArg('output'), $root); $with_custom = $args->getArg('with-custom'); $resource_hash = PhabricatorEnv::getEnvConfig('celerity.resource-hash'); $runtime_map = array(); echo "Finding raw static resources...\n"; $finder = id(new FileFinder($root))->withType('f')->withSuffix('png')->withSuffix('jpg')->withSuffix('gif')->withSuffix('swf')->withFollowSymlinks(true)->setGenerateChecksums(true); if (!$with_custom) { $finder->excludePath('./rsrc/custom'); } $raw_files = $finder->find(); echo "Processing " . count($raw_files) . " files"; foreach ($raw_files as $path => $hash) { echo "."; $path = '/' . Filesystem::readablePath($path, $root); $type = CelerityResourceTransformer::getResourceType($path); $hash = md5($hash . $path . $resource_hash);
/** * Build a new server. This server is bound to a working copy. The server * is inactive until you @{method:start} it. * * @param string Path to a Mercurial working copy. * * @task construct */ public function __construct($working_copy) { $this->workingCopy = Filesystem::resolvePath($working_copy); }