[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DiffusionLintSaveRunner { 4 private $arc = 'arc'; 5 private $severity = ArcanistLintSeverity::SEVERITY_ADVICE; 6 private $all = false; 7 private $chunkSize = 256; 8 private $needsBlame = false; 9 10 private $svnRoot; 11 private $lintCommit; 12 private $branch; 13 private $conn; 14 private $deletes = array(); 15 private $inserts = array(); 16 private $blame = array(); 17 18 19 public function setArc($path) { 20 $this->arc = $path; 21 return $this; 22 } 23 24 public function setSeverity($string) { 25 $this->severity = $string; 26 return $this; 27 } 28 29 public function setAll($bool) { 30 $this->all = $bool; 31 return $this; 32 } 33 34 public function setChunkSize($number) { 35 $this->chunkSize = $number; 36 return $this; 37 } 38 39 public function setNeedsBlame($boolean) { 40 $this->needsBlame = $boolean; 41 return $this; 42 } 43 44 45 public function run($dir) { 46 $working_copy = ArcanistWorkingCopyIdentity::newFromPath($dir); 47 $configuration_manager = new ArcanistConfigurationManager(); 48 $configuration_manager->setWorkingCopyIdentity($working_copy); 49 $api = ArcanistRepositoryAPI::newAPIFromConfigurationManager( 50 $configuration_manager); 51 52 $this->svnRoot = id(new PhutilURI($api->getSourceControlPath()))->getPath(); 53 if ($api instanceof ArcanistGitAPI) { 54 $svn_fetch = $api->getGitConfig('svn-remote.svn.fetch'); 55 list($this->svnRoot) = explode(':', $svn_fetch); 56 if ($this->svnRoot != '') { 57 $this->svnRoot = '/'.$this->svnRoot; 58 } 59 } 60 61 $project_id = $working_copy->getProjectID(); 62 $project = id(new PhabricatorRepositoryArcanistProject()) 63 ->loadOneWhere('name = %s', $project_id); 64 if (!$project || !$project->getRepositoryID()) { 65 throw new Exception("Couldn't find repository for {$project_id}."); 66 } 67 68 $branch_name = $api->getBranchName(); 69 70 $this->branch = PhabricatorRepositoryBranch::loadOrCreateBranch( 71 $project->getRepositoryID(), 72 $branch_name); 73 $this->conn = $this->branch->establishConnection('w'); 74 75 $this->lintCommit = null; 76 if (!$this->all) { 77 $this->lintCommit = $this->branch->getLintCommit(); 78 } 79 80 if ($this->lintCommit) { 81 try { 82 $commit = $this->lintCommit; 83 if ($this->svnRoot) { 84 $commit = $api->getCanonicalRevisionName('@'.$commit); 85 } 86 $all_files = $api->getChangedFiles($commit); 87 } catch (ArcanistCapabilityNotSupportedException $ex) { 88 $this->lintCommit = null; 89 } 90 } 91 92 93 if (!$this->lintCommit) { 94 $where = ($this->svnRoot 95 ? qsprintf($this->conn, 'AND path LIKE %>', $this->svnRoot.'/') 96 : ''); 97 queryfx( 98 $this->conn, 99 'DELETE FROM %T WHERE branchID = %d %Q', 100 PhabricatorRepository::TABLE_LINTMESSAGE, 101 $this->branch->getID(), 102 $where); 103 $all_files = $api->getAllFiles(); 104 } 105 106 $count = 0; 107 108 $files = array(); 109 foreach ($all_files as $file => $val) { 110 $count++; 111 if (!$this->lintCommit) { 112 $file = $val; 113 } else { 114 $this->deletes[] = $this->svnRoot.'/'.$file; 115 if ($val & ArcanistRepositoryAPI::FLAG_DELETED) { 116 continue; 117 } 118 } 119 $files[$file] = $file; 120 121 if (count($files) >= $this->chunkSize) { 122 $this->runArcLint($files); 123 $files = array(); 124 } 125 } 126 127 $this->runArcLint($files); 128 $this->saveLintMessages(); 129 130 $this->lintCommit = $api->getUnderlyingWorkingCopyRevision(); 131 $this->branch->setLintCommit($this->lintCommit); 132 $this->branch->save(); 133 134 if ($this->blame) { 135 $this->blameAuthors(); 136 $this->blame = array(); 137 } 138 139 return $count; 140 } 141 142 143 private function runArcLint(array $files) { 144 if (!$files) { 145 return; 146 } 147 148 echo '.'; 149 try { 150 $future = new ExecFuture( 151 '%C lint --severity %s --output json %Ls', 152 $this->arc, 153 $this->severity, 154 $files); 155 156 foreach (new LinesOfALargeExecFuture($future) as $json) { 157 $paths = json_decode($json, true); 158 if (!is_array($paths)) { 159 fprintf(STDERR, "Invalid JSON: {$json}\n"); 160 continue; 161 } 162 163 foreach ($paths as $path => $messages) { 164 if (!isset($files[$path])) { 165 continue; 166 } 167 168 foreach ($messages as $message) { 169 $line = idx($message, 'line', 0); 170 171 $this->inserts[] = qsprintf( 172 $this->conn, 173 '(%d, %s, %d, %s, %s, %s, %s)', 174 $this->branch->getID(), 175 $this->svnRoot.'/'.$path, 176 $line, 177 idx($message, 'code', ''), 178 idx($message, 'severity', ''), 179 idx($message, 'name', ''), 180 idx($message, 'description', '')); 181 182 if ($line && $this->needsBlame) { 183 $this->blame[$path][$line] = true; 184 } 185 } 186 187 if (count($this->deletes) >= 1024 || count($this->inserts) >= 256) { 188 $this->saveLintMessages(); 189 } 190 } 191 } 192 193 } catch (Exception $ex) { 194 fprintf(STDERR, $ex->getMessage()."\n"); 195 } 196 } 197 198 199 private function saveLintMessages() { 200 $this->conn->openTransaction(); 201 202 foreach (array_chunk($this->deletes, 1024) as $paths) { 203 queryfx( 204 $this->conn, 205 'DELETE FROM %T WHERE branchID = %d AND path IN (%Ls)', 206 PhabricatorRepository::TABLE_LINTMESSAGE, 207 $this->branch->getID(), 208 $paths); 209 } 210 211 foreach (array_chunk($this->inserts, 256) as $values) { 212 queryfx( 213 $this->conn, 214 'INSERT INTO %T 215 (branchID, path, line, code, severity, name, description) 216 VALUES %Q', 217 PhabricatorRepository::TABLE_LINTMESSAGE, 218 implode(', ', $values)); 219 } 220 221 $this->conn->saveTransaction(); 222 223 $this->deletes = array(); 224 $this->inserts = array(); 225 } 226 227 228 private function blameAuthors() { 229 $repository = id(new PhabricatorRepositoryQuery()) 230 ->setViewer(PhabricatorUser::getOmnipotentUser()) 231 ->withIDs(array($this->branch->getRepositoryID())) 232 ->executeOne(); 233 234 $queries = array(); 235 $futures = array(); 236 foreach ($this->blame as $path => $lines) { 237 $drequest = DiffusionRequest::newFromDictionary(array( 238 'user' => PhabricatorUser::getOmnipotentUser(), 239 'initFromConduit' => false, 240 'repository' => $repository, 241 'branch' => $this->branch->getName(), 242 'path' => $path, 243 'commit' => $this->lintCommit, 244 )); 245 $query = DiffusionFileContentQuery::newFromDiffusionRequest($drequest) 246 ->setNeedsBlame(true); 247 $queries[$path] = $query; 248 $futures[$path] = $query->getFileContentFuture(); 249 } 250 251 $authors = array(); 252 253 foreach (Futures($futures)->limit(8) as $path => $future) { 254 $queries[$path]->loadFileContentFromFuture($future); 255 list(, $rev_list, $blame_dict) = $queries[$path]->getBlameData(); 256 foreach (array_keys($this->blame[$path]) as $line) { 257 $commit_identifier = $rev_list[$line - 1]; 258 $author = idx($blame_dict[$commit_identifier], 'authorPHID'); 259 if ($author) { 260 $authors[$author][$path][] = $line; 261 } 262 } 263 } 264 265 if ($authors) { 266 $this->conn->openTransaction(); 267 268 foreach ($authors as $author => $paths) { 269 $where = array(); 270 foreach ($paths as $path => $lines) { 271 $where[] = qsprintf( 272 $this->conn, 273 '(path = %s AND line IN (%Ld))', 274 $this->svnRoot.'/'.$path, 275 $lines); 276 } 277 queryfx( 278 $this->conn, 279 'UPDATE %T SET authorPHID = %s WHERE %Q', 280 PhabricatorRepository::TABLE_LINTMESSAGE, 281 $author, 282 implode(' OR ', $where)); 283 } 284 285 $this->conn->saveTransaction(); 286 } 287 } 288 289 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |