1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-26 00:32:41 +01:00

Unify arguments for 'arc lint', 'arc unit'

Summary: See T645. These commands take inconsistent and overly-magical arguments
right now. Instead, make them behave consistently and allow them both to operate
on "arc <workflow> path path2 path3 ...", which is a generally useful workflow.

Test Plan: Ran "arc lint <path>", "arc unit <path>", "arc lint --rev
HEAD^^^^^^", "arc unit --rev HEAD^^^^^^^^^^^^", etc. Ran "arc diff --trace" and
verified --rev argument to child workflows.

Reviewers: btrahan, jungejason

Reviewed By: btrahan

CC: aran, epriestley, btrahan

Maniphest Tasks: T645

Differential Revision: https://secure.phabricator.com/D1348
This commit is contained in:
epriestley 2012-01-09 12:40:50 -08:00
parent 8cbfa612da
commit f3eccfbe81
15 changed files with 112 additions and 92 deletions

View file

@ -2,7 +2,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -936,4 +936,49 @@ class ArcanistBaseWorkflow {
return ($working_copy->getConfig('immutable_history') === true); return ($working_copy->getConfig('immutable_history') === true);
} }
/**
* Workflows like 'lint' and 'unit' operate on a list of working copy paths.
* The user can either specify the paths explicitly ("a.js b.php"), or by
* specfifying a revision ("--rev a3f10f1f") to select all paths modified
* since that revision, or by omitting both and letting arc choose the
* default relative revision.
*
* This method takes the user's selections and returns the paths that the
* workflow should act upon.
*
* @param list List of explicitly provided paths.
* @param string|null Revision name, if provided.
* @return list List of paths the workflow should act on.
*/
protected function selectPathsForWorkflow(array $paths, $rev) {
if ($paths) {
$working_copy = $this->getWorkingCopy();
foreach ($paths as $key => $path) {
$full_path = Filesystem::resolvePath($path);
if (!Filesystem::pathExists($full_path)) {
throw new ArcanistUsageException("Path '{$path}' does not exist!");
}
$relative_path = Filesystem::readablePath(
$full_path,
$working_copy->getProjectRoot());
$paths[$key] = $relative_path;
}
} else {
$repository_api = $this->getRepositoryAPI();
if ($rev) {
$repository_api->parseRelativeLocalCommit(array($rev));
}
$paths = $repository_api->getWorkingCopyStatus();
foreach ($paths as $path => $flags) {
if ($flags & ArcanistRepositoryAPI::FLAG_UNTRACKED) {
unset($paths[$path]);
}
}
$paths = array_keys($paths);
}
return array_values($paths);
}
} }

View file

@ -14,6 +14,7 @@ phutil_require_module('arcanist', 'parser/bundle');
phutil_require_module('arcanist', 'parser/diff'); phutil_require_module('arcanist', 'parser/diff');
phutil_require_module('arcanist', 'parser/diff/change'); phutil_require_module('arcanist', 'parser/diff/change');
phutil_require_module('arcanist', 'parser/diff/changetype'); phutil_require_module('arcanist', 'parser/diff/changetype');
phutil_require_module('arcanist', 'repository/api/base');
phutil_require_module('phutil', 'conduit/client'); phutil_require_module('phutil', 'conduit/client');
phutil_require_module('phutil', 'console'); phutil_require_module('phutil', 'console');

View file

@ -956,9 +956,8 @@ EOTEXT
echo "Linting...\n"; echo "Linting...\n";
try { try {
$argv = $this->getPassthruArgumentsAsArgv('lint'); $argv = $this->getPassthruArgumentsAsArgv('lint');
if ($repository_api instanceof ArcanistSubversionAPI) { if ($repository_api->supportsRelativeLocalCommits()) {
$argv = array_merge($argv, array_keys($paths)); $argv[] = '--rev';
} else {
$argv[] = $repository_api->getRelativeCommit(); $argv[] = $repository_api->getRelativeCommit();
} }
$lint_workflow = $this->buildChildWorkflow('lint', $argv); $lint_workflow = $this->buildChildWorkflow('lint', $argv);
@ -1021,8 +1020,9 @@ EOTEXT
echo "Running unit tests...\n"; echo "Running unit tests...\n";
try { try {
$argv = $this->getPassthruArgumentsAsArgv('unit'); $argv = $this->getPassthruArgumentsAsArgv('unit');
if ($repository_api instanceof ArcanistSubversionAPI) { if ($repository_api->supportsRelativeLocalCommits()) {
$argv = array_merge($argv, array_keys($paths)); $argv[] = '--rev';
$argv[] = $repository_api->getRelativeCommit();
} }
$this->unitWorkflow = $this->buildChildWorkflow('unit', $argv); $this->unitWorkflow = $this->buildChildWorkflow('unit', $argv);
$unit_result = $this->unitWorkflow->run(); $unit_result = $this->unitWorkflow->run();

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,9 +38,9 @@ class ArcanistLintWorkflow extends ArcanistBaseWorkflow {
public function getCommandHelp() { public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT return phutil_console_format(<<<EOTEXT
**lint** [__options__] [__paths__] (svn) **lint** [__options__] [__paths__]
**lint** [__options__] [__commit_range__] (git) **lint** [__options__] --rev [__rev__]
Supports: git, svn Supports: git, svn, hg
Run static analysis on changes to check for mistakes. If no files Run static analysis on changes to check for mistakes. If no files
are specified, lint will be run on all files which have been modified. are specified, lint will be run on all files which have been modified.
EOTEXT EOTEXT
@ -53,6 +53,17 @@ EOTEXT
'help' => 'help' =>
"Show all lint warnings, not just those on changed lines." "Show all lint warnings, not just those on changed lines."
), ),
'rev' => array(
'param' => 'revision',
'help' => "Lint changes since a specific revision.",
'supports' => array(
'git',
'hg',
),
'nosupport' => array(
'svn' => "Lint does not currently support --rev in SVN.",
),
),
'output' => array( 'output' => array(
'param' => 'format', 'param' => 'format',
'help' => 'help' =>
@ -90,6 +101,10 @@ EOTEXT
return true; return true;
} }
public function requiresRepositoryAPI() {
return true;
}
public function run() { public function run() {
$working_copy = $this->getWorkingCopy(); $working_copy = $this->getWorkingCopy();
@ -103,61 +118,23 @@ EOTEXT
} }
} }
$should_lint_all = $this->getArgument('lintall'); $rev = $this->getArgument('rev');
$paths = $this->getArgument('paths');
$repository_api = null; if ($rev && $paths) {
if (!$should_lint_all) { throw new ArcanistUsageException("Specify either --rev or paths.");
try {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$this->setRepositoryAPI($repository_api);
} catch (ArcanistUsageException $ex) {
throw new ArcanistUsageException(
$ex->getMessage()."\n\n".
"Use '--lintall' to ignore working copy changes when running lint.");
}
if ($repository_api instanceof ArcanistSubversionAPI) {
$paths = $repository_api->getWorkingCopyStatus();
$list = new FileList($this->getArgument('paths'));
foreach ($paths as $path => $flags) {
if (!$list->contains($path)) {
unset($paths[$path]);
}
}
} else if ($repository_api->supportsRelativeLocalCommits()) {
$repository_api->parseRelativeLocalCommit(
$this->getArgument('paths', array()));
$paths = $repository_api->getWorkingCopyStatus();
} else {
throw new Exception("Unknown VCS!");
}
foreach ($paths as $path => $flags) {
if ($flags & ArcanistRepositoryAPI::FLAG_UNTRACKED) {
unset($paths[$path]);
}
}
$paths = array_keys($paths);
} else {
$paths = $this->getArgument('paths');
if (empty($paths)) {
throw new ArcanistUsageException(
"You must specify one or more files to lint when using '--lintall'.");
}
foreach ($paths as $key => $path) {
$full_path = Filesystem::resolvePath($path);
if (!Filesystem::pathExists($full_path)) {
throw new ArcanistUsageException("Path '{$path}' does not exist!");
}
$relative_path = Filesystem::readablePath(
$full_path,
$working_copy->getProjectRoot());
$paths[$key] = $relative_path;
}
} }
$should_lint_all = $this->getArgument('lintall');
if ($paths) {
// NOTE: When the user specifies paths, we imply --lintall and show all
// warnings for the paths in question. This is easier to deal with for
// us and less confusing for users.
$should_lint_all = true;
}
$paths = $this->selectPathsForWorkflow($paths, $rev);
PhutilSymbolLoader::loadClass($engine); PhutilSymbolLoader::loadClass($engine);
if (!is_subclass_of($engine, 'ArcanistLintEngine')) { if (!is_subclass_of($engine, 'ArcanistLintEngine')) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
@ -262,6 +239,7 @@ EOTEXT
} }
} }
$repository_api = $this->getRepositoryAPI();
if ($wrote_to_disk && if ($wrote_to_disk &&
($repository_api instanceof ArcanistGitAPI) && ($repository_api instanceof ArcanistGitAPI) &&
$this->shouldAmendChanges) { $this->shouldAmendChanges) {

View file

@ -11,12 +11,10 @@ phutil_require_module('arcanist', 'exception/usage/noengine');
phutil_require_module('arcanist', 'lint/patcher'); phutil_require_module('arcanist', 'lint/patcher');
phutil_require_module('arcanist', 'lint/renderer'); phutil_require_module('arcanist', 'lint/renderer');
phutil_require_module('arcanist', 'lint/severity'); phutil_require_module('arcanist', 'lint/severity');
phutil_require_module('arcanist', 'repository/api/base');
phutil_require_module('arcanist', 'workflow/base'); phutil_require_module('arcanist', 'workflow/base');
phutil_require_module('phutil', 'console'); phutil_require_module('phutil', 'console');
phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'filesystem/filelist');
phutil_require_module('phutil', 'filesystem/tempfile'); phutil_require_module('phutil', 'filesystem/tempfile');
phutil_require_module('phutil', 'future/exec'); phutil_require_module('phutil', 'future/exec');
phutil_require_module('phutil', 'symbols'); phutil_require_module('phutil', 'symbols');

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -34,8 +34,9 @@ class ArcanistUnitWorkflow extends ArcanistBaseWorkflow {
public function getCommandHelp() { public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT return phutil_console_format(<<<EOTEXT
**unit** [__paths__] **unit** [__options__] [__paths__]
Supports: git, svn **unit** [__options__] --rev [__rev__]
Supports: git, svn, hg
Run unit tests that cover specified paths. If no paths are specified, Run unit tests that cover specified paths. If no paths are specified,
unit tests covering all modified files will be run. unit tests covering all modified files will be run.
EOTEXT EOTEXT
@ -44,6 +45,17 @@ EOTEXT
public function getArguments() { public function getArguments() {
return array( return array(
'rev' => array(
'param' => 'revision',
'help' => "Run unit tests covering changes since a specific revision.",
'supports' => array(
'git',
'hg',
),
'nosupport' => array(
'svn' => "Arc unit does not currently support --rev in SVN.",
),
),
'engine' => array( 'engine' => array(
'param' => 'classname', 'param' => 'classname',
'help' => 'help' =>
@ -79,23 +91,10 @@ EOTEXT
"to specify a unit test engine."); "to specify a unit test engine.");
} }
$repository_api = $this->getRepositoryAPI(); $paths = $this->getArgument('paths');
$rev = $this->getArgument('rev');
if ($this->getArgument('paths')) { $paths = $this->selectPathsForWorkflow($paths, $rev);
// TODO: deal with git stuff
$paths = $this->getArgument('paths');
} else {
$paths = $repository_api->getWorkingCopyStatus();
// TODO: clean this up
foreach ($paths as $path => $mask) {
if ($mask & ArcanistRepositoryAPI::FLAG_UNTRACKED) {
unset($paths[$path]);
}
}
$paths = array_keys($paths);
}
PhutilSymbolLoader::loadClass($engine_class); PhutilSymbolLoader::loadClass($engine_class);
if (!is_subclass_of($engine_class, 'ArcanistBaseUnitTestEngine')) { if (!is_subclass_of($engine_class, 'ArcanistBaseUnitTestEngine')) {

View file

@ -8,7 +8,6 @@
phutil_require_module('arcanist', 'exception/usage'); phutil_require_module('arcanist', 'exception/usage');
phutil_require_module('arcanist', 'exception/usage/noengine'); phutil_require_module('arcanist', 'exception/usage/noengine');
phutil_require_module('arcanist', 'repository/api/base');
phutil_require_module('arcanist', 'unit/result'); phutil_require_module('arcanist', 'unit/result');
phutil_require_module('arcanist', 'workflow/base'); phutil_require_module('arcanist', 'workflow/base');