[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/diffusion/ -> DiffusionLintSaveRunner.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1