Skip to content
Permalink
Browse files

Add ability to specify GitHub code owners

  • Loading branch information...
muglug committed Apr 26, 2019
1 parent 59e53ac commit 29588c9fd8d1a12fa3542f7e3cefa042b4808acd
Showing with 134 additions and 13 deletions.
  1. +20 −1 src/Psalm/Internal/Codebase/Analyzer.php
  2. +114 −12 src/psalter.php
@@ -106,6 +106,13 @@ class Analyzer
*/
private $files_to_analyze = [];
/**
* We may update fewer files than we analyse (i.e. for dead code detection)
*
* @var array<string>|null
*/
private $files_to_update = null;
/**
* @var array<string, array<string, int>>
*/
@@ -151,6 +158,16 @@ public function addFiles(array $files_to_analyze)
$this->files_to_analyze += $files_to_analyze;
}
/**
* @param array<string> $files_to_update
*
* @return void
*/
public function setFilesToUpdate(array $files_to_update)
{
$this->files_to_update = $files_to_update;
}
/**
* @param string $file_path
*
@@ -386,7 +403,9 @@ function () {
}
if ($alter_code) {
foreach ($this->files_to_analyze as $file_path) {
$files_to_update = $this->files_to_update !== null ? $this->files_to_update : $this->files_to_analyze;
foreach ($files_to_update as $file_path) {
$this->updateFile($file_path, $project_analyzer->dry_run, true);
}
}
@@ -20,7 +20,7 @@
$valid_long_options = [
'help', 'debug', 'debug-by-line', 'config:', 'file:', 'root:',
'plugin:', 'issues:', 'php-version:', 'dry-run', 'safe-types',
'find-unused-code', 'threads:',
'find-unused-code', 'threads:', 'codeowner:',
];
// get options from command line
@@ -114,6 +114,9 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
--threads=INT
If greater than one, Psalm will run analysis on multiple threads, speeding things up.
--codeowner=[codeowner]
You can specify a GitHub code ownership group, and only that owner's code will be updated.
HELP;
exit;
@@ -149,10 +152,6 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
$paths_to_check = getPathsToCheck(isset($options['f']) ? $options['f'] : null);
if ($paths_to_check && count($paths_to_check) > 1) {
die('Psalter can currently only be run on one path at a time' . PHP_EOL);
}
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
@@ -171,14 +170,16 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
$threads = isset($options['threads']) ? (int)$options['threads'] : 1;
$providers = new Psalm\Internal\Provider\Providers(
new Psalm\Internal\Provider\FileProvider(),
new Psalm\Internal\Provider\ParserCacheProvider($config),
new Psalm\Internal\Provider\FileStorageCacheProvider($config),
new Psalm\Internal\Provider\ClassLikeStorageCacheProvider($config)
);
$project_analyzer = new ProjectAnalyzer(
$config,
new Psalm\Internal\Provider\Providers(
new Psalm\Internal\Provider\FileProvider(),
new Psalm\Internal\Provider\ParserCacheProvider($config),
new Psalm\Internal\Provider\FileStorageCacheProvider($config),
new Psalm\Internal\Provider\ClassLikeStorageCacheProvider($config)
),
$providers,
!array_key_exists('m', $options),
false,
ProjectAnalyzer::TYPE_CONSOLE,
@@ -216,6 +217,91 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
$project_analyzer->setPhpVersion($options['php-version']);
}
if (isset($options['codeowner'])) {
if (file_exists('CODEOWNERS')) {
$codeowners_file_path = realpath('CODEOWNERS');
} elseif (file_exists('.github/CODEOWNERS')) {
$codeowners_file_path = realpath('.github/CODEOWNERS');
} elseif (file_exists('docs/CODEOWNERS')) {
$codeowners_file_path = realpath('docs/CODEOWNERS');
} else {
die('Cannot use --codeowner without a CODEOWNERS file' . PHP_EOL);
}
$codeowners_file = file_get_contents($codeowners_file_path);
$codeowner_lines = array_map(
function (string $line) : array {
$line_parts = preg_split('/\s+/', $line);
$file_selector = substr(array_shift($line_parts), 1);
return [$file_selector, $line_parts];
},
array_filter(
explode("\n", $codeowners_file),
function (string $line) : bool {
$line = trim($line);
// currently we don’t match wildcard files or files that could appear anywhere
// in the repo
return $line && $line[0] === '/' && strpos($line, '*') === false;
}
)
);
$codeowner_files = [];
foreach ($codeowner_lines as list($path, $owners)) {
if (!file_exists($path)) {
continue;
}
foreach ($owners as $i => $owner) {
$owners[$i] = strtolower($owner);
}
if (!is_dir($path)) {
if (pathinfo($path, PATHINFO_EXTENSION) === 'php') {
$codeowner_files[$path] = $owners;
}
} else {
foreach ($providers->file_provider->getFilesInDir($path, ['php']) as $php_file_path) {
$codeowner_files[$php_file_path] = $owners;
}
}
}
if (!$codeowner_files) {
die('Could not find any available entries in CODEOWNERS' . PHP_EOL);
}
$desired_codeowners = is_array($options['codeowner']) ? $options['codeowner'] : [$options['codeowner']];
/** @psalm-suppress MixedAssignment */
foreach ($desired_codeowners as $desired_codeowner) {
if (!is_string($desired_codeowner)) {
die('Invalid --codeowner ' . (string)$desired_codeowner . PHP_EOL);
}
if ($desired_codeowner[0] !== '@') {
die('--codeowner option must start with @' . PHP_EOL);
}
$matched_file = false;
foreach ($codeowner_files as $file_path => $owners) {
if (in_array(strtolower($desired_codeowner), $owners)) {
$paths_to_check[] = $file_path;
$matched_file = true;
}
}
if (!$matched_file) {
die('User/group ' . $desired_codeowner . ' does not own any PHP files' . PHP_EOL);
}
}
}
$plugins = [];
if (isset($options['plugin'])) {
@@ -249,7 +335,23 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
$start_time = microtime(true);
if ($paths_to_check === null) {
if ($paths_to_check === null || count($paths_to_check) > 1 || $find_unused_code) {
if ($paths_to_check) {
$files_to_update = [];
foreach ($paths_to_check as $path_to_check) {
if (!is_dir($path_to_check)) {
$files_to_update[] = (string) realpath($path_to_check);
} else {
foreach ($providers->file_provider->getFilesInDir($path_to_check, ['php']) as $php_file_path) {
$files_to_update[] = $php_file_path;
}
}
}
$project_analyzer->getCodebase()->analyzer->setFilesToUpdate($files_to_update);
}
$project_analyzer->check($current_dir);
} elseif ($paths_to_check) {
foreach ($paths_to_check as $path_to_check) {

0 comments on commit 29588c9

Please sign in to comment.
You can’t perform that action at this time.