Something went wrong. Try again.
@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
Something went wrong. Try again.
8.3 kB · 309 lines
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310<?php
final class DiffusionLintSaveRunner extends Phobject { private $arc = 'arc'; private $severity = ArcanistLintSeverity::SEVERITY_ADVICE; private $all = false; private $chunkSize = 256; private $needsBlame = false;
private $svnRoot; private $lintCommit; private $branch; private $conn; private $deletes = array(); private $inserts = array(); private $blame = array();
public function setArc($path) { $this->arc = $path; return $this; }
public function setSeverity($string) { $this->severity = $string; return $this; }
public function setAll($bool) { $this->all = $bool; return $this; }
public function setChunkSize($number) { $this->chunkSize = $number; return $this; }
public function setNeedsBlame($boolean) { $this->needsBlame = $boolean; return $this; }
public function run($dir) { $working_copy = ArcanistWorkingCopyIdentity::newFromPath($dir); $configuration_manager = new ArcanistConfigurationManager(); $configuration_manager->setWorkingCopyIdentity($working_copy); $api = ArcanistRepositoryAPI::newAPIFromConfigurationManager( $configuration_manager);
$this->svnRoot = id(new PhutilURI($api->getSourceControlPath()))->getPath(); if ($api instanceof ArcanistGitAPI) { $svn_fetch = $api->getGitConfig('svn-remote.svn.fetch'); list($this->svnRoot) = explode(':', $svn_fetch); if ($this->svnRoot != '') { $this->svnRoot = '/'.$this->svnRoot; } }
$callsign = $configuration_manager->getConfigFromAnySource( 'repository.callsign'); $uuid = $api->getRepositoryUUID(); $remote_uri = $api->getRemoteURI();
$repository_query = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser());
if ($callsign) { $repository_query->withCallsigns(array($callsign)); } else if ($uuid) { $repository_query->withUUIDs(array($uuid)); } else if ($remote_uri) { $repository_query->withURIs(array($remote_uri)); }
$repository = $repository_query->executeOne(); $branch_name = $api->getBranchName();
if (!$repository) { throw new Exception(pht('No repository was found.')); }
$this->branch = PhabricatorRepositoryBranch::loadOrCreateBranch( $repository->getID(), $branch_name); $this->conn = $this->branch->establishConnection('w');
$this->lintCommit = null; if (!$this->all) { $this->lintCommit = $this->branch->getLintCommit(); }
$all_files = array(); if ($this->lintCommit) { try { $commit = $this->lintCommit; if ($this->svnRoot) { $commit = $api->getCanonicalRevisionName('@'.$commit); } $all_files = $api->getChangedFiles($commit); } catch (ArcanistCapabilityNotSupportedException $ex) { $this->lintCommit = null; } }
if (!$this->lintCommit) { $where = ($this->svnRoot ? qsprintf($this->conn, 'AND path LIKE %>', $this->svnRoot.'/') : ''); queryfx( $this->conn, 'DELETE FROM %T WHERE branchID = %d %Q', PhabricatorRepository::TABLE_LINTMESSAGE, $this->branch->getID(), $where); $all_files = $api->getAllFiles(); }
$count = 0;
$files = array(); foreach ($all_files as $file => $val) { $count++; if (!$this->lintCommit) { $file = $val; } else { $this->deletes[] = $this->svnRoot.'/'.$file; if ($val & ArcanistRepositoryAPI::FLAG_DELETED) { continue; } } $files[$file] = $file;
if (count($files) >= $this->chunkSize) { $this->runArcLint($files); $files = array(); } }
$this->runArcLint($files); $this->saveLintMessages();
$this->lintCommit = $api->getUnderlyingWorkingCopyRevision(); $this->branch->setLintCommit($this->lintCommit); $this->branch->save();
if ($this->blame) { $this->blameAuthors(); $this->blame = array(); }
return $count; }
private function runArcLint(array $files) { if (!$files) { return; }
echo '.'; try { $future = new ExecFuture( '%C lint --severity %s --output json %Ls', $this->arc, $this->severity, $files);
foreach (new LinesOfALargeExecFuture($future) as $json) { $paths = null; try { $paths = phutil_json_decode($json); } catch (PhutilJSONParserException $ex) { fprintf(STDERR, pht('Invalid JSON: %s', $json)."\n"); continue; }
foreach ($paths as $path => $messages) { if (!isset($files[$path])) { continue; }
foreach ($messages as $message) { $line = idx($message, 'line', 0);
$this->inserts[] = qsprintf( $this->conn, '(%d, %s, %d, %s, %s, %s, %s)', $this->branch->getID(), $this->svnRoot.'/'.$path, $line, idx($message, 'code', ''), idx($message, 'severity', ''), idx($message, 'name', ''), idx($message, 'description', ''));
if ($line && $this->needsBlame) { $this->blame[$path][$line] = true; } }
if (count($this->deletes) >= 1024 || count($this->inserts) >= 256) { $this->saveLintMessages(); } } }
} catch (Exception $ex) { fprintf(STDERR, $ex->getMessage()."\n"); } }
private function saveLintMessages() { $this->conn->openTransaction();
foreach (array_chunk($this->deletes, 1024) as $paths) { queryfx( $this->conn, 'DELETE FROM %T WHERE branchID = %d AND path IN (%Ls)', PhabricatorRepository::TABLE_LINTMESSAGE, $this->branch->getID(), $paths); }
foreach (array_chunk($this->inserts, 256) as $values) { queryfx( $this->conn, 'INSERT INTO %T (branchID, path, line, code, severity, name, description) VALUES %LQ', PhabricatorRepository::TABLE_LINTMESSAGE, $values); }
$this->conn->saveTransaction();
$this->deletes = array(); $this->inserts = array(); }
private function blameAuthors() { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withIDs(array($this->branch->getRepositoryID())) ->executeOne();
$queries = array(); $futures = array(); foreach ($this->blame as $path => $lines) { $drequest = DiffusionRequest::newFromDictionary(array( 'user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $repository, 'branch' => $this->branch->getName(), 'path' => $path, 'commit' => $this->lintCommit, ));
// TODO: Restore blame information / generally fix this workflow.
$query = DiffusionFileContentQuery::newFromDiffusionRequest($drequest); $queries[$path] = $query; $futures[$path] = new ImmediateFuture($query->executeInline()); }
$authors = array();
$futures = id(new FutureIterator($futures)) ->limit(8); foreach ($futures as $path => $future) { $queries[$path]->loadFileContentFromFuture($future); list(, $rev_list, $blame_dict) = $queries[$path]->getBlameData(); foreach (array_keys($this->blame[$path]) as $line) { $commit_identifier = $rev_list[$line - 1]; $author = idx($blame_dict[$commit_identifier], 'authorPHID'); if ($author) { $authors[$author][$path][] = $line; } } }
if ($authors) { $this->conn->openTransaction();
foreach ($authors as $author => $paths) { $where = array(); foreach ($paths as $path => $lines) { $where[] = qsprintf( $this->conn, '(path = %s AND line IN (%Ld))', $this->svnRoot.'/'.$path, $lines); } queryfx( $this->conn, 'UPDATE %T SET authorPHID = %s WHERE %LO', PhabricatorRepository::TABLE_LINTMESSAGE, $author, $where); }
$this->conn->saveTransaction(); } }
}