Commit b2c0d072 authored by Vihang Mehta's avatar Vihang Mehta
Browse files

Covert proto break check detector into a linter

Summary:
We currently run prototool as a "unit test" for arcanist and run it
over the entire repo. Instead, let's run it as a linter. This means the checker
will run only on `.proto` files and one at a time instead of the entire repo.
This will also make lint messages show up inline in the diff.

Downgrade from error to warnings since we do sometimes make what would be
considered a breaking change (such as renaming a field) or removing an unused
field. So these are warnings and should be treated as such.

Test Plan:
Made some breaking changes and ran `arc lint`
{F127032}

Reviewers: zasgar, michelle

Reviewed By: michelle

Differential Revision: https://phab.corp.pixielabs.ai/D8128

GitOrigin-RevId: 70c4a66a0f233f39a14b2a8b177236dc157f5818
parent 9c491f7e
Showing with 78 additions and 53 deletions
+78 -53
......@@ -138,6 +138,15 @@
"(^arc_addons/.*\\.php$)"
]
},
"proto-break-check": {
"type": "proto-break-check",
"include": [
"(.*\\.proto$)"
],
"exclude": [
"(^src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/ir/physicalpb/physical\\.proto$)"
]
},
"spelling": {
"type": "spelling",
"exclude": [
......
......@@ -15,6 +15,7 @@ phutil_register_library_map(array(
'ArcanistGoImportsLinter' => 'pixielabs/lint/ArcanistGoImportsLinter.php',
'ArcanistGoVetLinter' => 'golang/lint/ArcanistGoVetLinter.php',
'ArcanistGolangCiLinter' => 'pixielabs/lint/ArcanistGolangCiLinter.php',
'ArcanistProtoBreakCheckLinter' => 'pixielabs/lint/ArcanistProtoBreakCheckLinter.php',
'ArcanistShellCheckLinter' => 'shellcheck/lint/ArcanistShellCheckLinter.php',
'ArcanistShellCheckLinterTestCase' => 'shellcheck/lint/__tests__/ArcanistShellCheckLinterTestCase.php',
'ExpCheckerTestEngine' => 'pixielabs/unit/ExpCheckerTestEngine.php',
......@@ -22,7 +23,6 @@ phutil_register_library_map(array(
'GazelleCheckerTestEngine' => 'pixielabs/unit/GazelleCheckerTestEngine.php',
'GoGenerateCheckerTestEngine' => 'pixielabs/unit/GoGenerateCheckerTestEngine.php',
'PLTestEngine' => 'pixielabs/unit/PLTestEngine.php',
'PrototoolCheckerTestEngine' => 'pixielabs/unit/PrototoolCheckerTestEngine.php',
),
'function' => array(),
'xmap' => array(
......@@ -32,6 +32,7 @@ phutil_register_library_map(array(
'ArcanistGoImportsLinter' => 'ArcanistExternalLinter',
'ArcanistGoVetLinter' => 'ArcanistExternalLinter',
'ArcanistGolangCiLinter' => 'ArcanistExternalLinter',
'ArcanistProtoBreakCheckLinter' => 'ArcanistExternalLinter',
'ArcanistShellCheckLinter' => 'ArcanistExternalLinter',
'ArcanistShellCheckLinterTestCase' => 'PhutilTestCase',
'PLTestEngine' => 'ArcanistUnitTestEngine',
......
......@@ -11,17 +11,18 @@ phutil_register_library_map(array(
'class' => array(
'ArcanistGoImportsLinter' => 'lint/ArcanistGoImportsLinter.php',
'ArcanistGolangCiLinter' => 'lint/ArcanistGolangCiLinter.php',
'ArcanistProtoBreakCheckLinter' => 'lint/ArcanistProtoBreakCheckLinter.php',
'ExpCheckerTestEngine' => 'unit/ExpCheckerTestEngine.php',
'FileCheckerTestEngine' => 'unit/FileCheckerTestEngine.php',
'GazelleCheckerTestEngine' => 'unit/GazelleCheckerTestEngine.php',
'GoGenerateCheckerTestEngine' => 'unit/GoGenerateCheckerTestEngine.php',
'PLTestEngine' => 'unit/PLTestEngine.php',
'PrototoolCheckerTestEngine' => 'unit/PrototoolCheckerTestEngine.php',
),
'function' => array(),
'xmap' => array(
'ArcanistGoImportsLinter' => 'ArcanistExternalLinter',
'ArcanistGolangCiLinter' => 'ArcanistExternalLinter',
'ArcanistProtoBreakCheckLinter' => 'ArcanistExternalLinter',
'PLTestEngine' => 'ArcanistUnitTestEngine',
),
));
<?php
final class ArcanistProtoBreakCheckLinter extends ArcanistExternalLinter {
public function getInfoName() {
return 'proto-break-check';
}
public function getInfoURI() {
return 'https://github.com/uber/prototool#prototool-break-check';
}
public function getInfoDescription() {
return 'proto-break-check looks for backward incompatible protobuf changes';
}
public function getLinterName() {
return 'proto-break-check';
}
public function getLinterConfigurationName() {
return 'proto-break-check';
}
public function getDefaultBinary() {
return 'prototool';
}
public function getInstallInstructions() {
return 'Install from github releases '.
'https://github.com/uber/prototool/releases';
}
protected function getMandatoryFlags() {
return array('--git-branch=main');
}
protected function buildFutures(array $paths) {
$executable = $this->getExecutableCommand();
$flags = $this->getCommandFlags();
$futures = array();
foreach ($paths as $path) {
$future = new ExecFuture('%C break check %Ls %s', $executable, $flags, $path);
$futures[$path] = $future;
}
return $futures;
}
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
$messages = array();
if ($err !== 0) {
$message = id(new ArcanistLintMessage())
->setPath($path)
->setCode('breaking-change')
->setName('prototool')
->setDescription($stdout)
->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
$messages[] = $message;
}
return $messages;
}
}
......@@ -4,7 +4,6 @@ include 'FileCheckerTestEngine.php';
include 'GazelleCheckerTestEngine.php';
include 'ExpCheckerTestEngine.php';
include 'GoGenerateCheckerTestEngine.php';
include 'PrototoolCheckerTestEngine.php';
final class PLTestEngine extends ArcanistUnitTestEngine {
private $project_root;
......@@ -30,9 +29,6 @@ final class PLTestEngine extends ArcanistUnitTestEngine {
// $go_generate_checker = new GoGenerateCheckerTestEngine($this->project_root, $this->files);
// $test_results = array_merge($test_results, $go_generate_checker->run());
$prototool_checker = new PrototoolCheckerTestEngine($this->project_root, $this->files);
$test_results = array_merge($test_results, $prototool_checker->run());
return $test_results;
}
}
<?php
final class PrototoolCheckerTestEngine {
private $project_root;
private $files;
public function __construct($project_root, $files) {
$this->project_root = $project_root;
$this->files = $files;
}
public function run() {
$test_results = array();
chdir($this->project_root);
// Skip prototool if no .proto files are present, since prototool is time-consuming.
$protoFiles = array_filter($this->files, function($f) {
return substr($f, -6) == '.proto';
});
if (count($protoFiles) == 0) {
return $test_results;
}
$output = array();
exec('prototool break check . --git-branch main --walk-timeout 10s', $output, $return_var);
$updatedRes = new ArcanistUnitTestResult();
$updatedRes->setName('Protobuf Breaking API check');
if ($return_var == 0) {
$updatedRes->setResult(ArcanistUnitTestResult::RESULT_PASS);
} else {
$updatedRes->setUserData(implode("\n", $output));
$updatedRes->setResult(ArcanistUnitTestResult::RESULT_FAIL);
}
$test_results[] = $updatedRes;
return $test_results;
}
}
---
excludes:
- vendor
- third_party
- chef
- bazel-bin
- bazel-out
- bazel-pixielabs
- bazel-testlogs
- docs
- src/ui
# Exclude stirling's dynamic tracing physical IR, since it is not a public proto.
- src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/ir/physicalpb
- third_party
protoc:
version: 3.8.0
includes:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment