Add presubmit check for bzl/bazel file formatting using `buildifier` This only helps if you're using depot_tools. Nothing else currently seems to check this upstream. Incorrect formatting causes errors when importing into google3. Change-Id: I439c207b7ca571ed25030cea639b89d10c990175 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/83827 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 4f13a4e..393d7f0 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -21,33 +21,102 @@ USE_PYTHON3 = True +def _PassResult(stdout, stderr, retcode): + return [] # Check passed. + + +def _RunTool(input_api, + output_api, + command, + handle_result=_PassResult, + explain_error=None): + """ + Runs a command and processes its result. + handle_result(stdout, stderr, retcode) and explain_error(error) should each + return a list of output_api.PresubmitResult. + """ + try: + out, retcode = input_api.subprocess.communicate( + command, + stdout=input_api.subprocess.PIPE, + stderr=input_api.subprocess.PIPE, + encoding='utf-8') + return handle_result(out[0], out[1], retcode) + except input_api.subprocess.CalledProcessError as e: + errors = [] + if explain_error: + errors += explain_error(e) + errors += [ + output_api.PresubmitPromptOrNotify( + 'Command "%s" returned exit code %d. Output: \n\n%s' % + ' '.join(command), e.returncode, e.output) + ] + return errors + + def CheckPregeneratedFiles(input_api, output_api): """Checks that pregenerated files are properly updated.""" # TODO(chlily): Make this compatible with the util/bot environment for CI/CQ. - try: - # Check that `go` is available on the $PATH. - input_api.subprocess.check_call(['go', 'version'], - stdout=input_api.subprocess.PIPE, - stderr=input_api.subprocess.PIPE) - except input_api.subprocess.CalledProcessError as e: - return [ - output_api.PresubmitPromptOrNotify(f'Could not run `go`: {e}') - ] + # Check that `go` is available on the $PATH. + if error := _RunTool(input_api, output_api, ['go', 'version']): + return error - pregenerate_script_path = input_api.os_path.join( - input_api.change.RepositoryRoot(), 'util', 'pregenerate') - try: - out, retcode = input_api.subprocess.communicate( - ['go', 'run', pregenerate_script_path, '-check'], - stdout=input_api.subprocess.PIPE, - stderr=input_api.subprocess.PIPE) + def HandlePregenerateResult(stdout, stderr, retcode): if retcode: - bad = out[1].decode("utf-8").splitlines() return [ output_api.PresubmitError( ("Found out-of-date generated files. " - "Run `go run ./util/pregenerate` to update them."), bad) + "Run `go run ./util/pregenerate` to update them."), + stderr.splitlines()) ] - except input_api.subprocess.CalledProcessError as e: - return [output_api.PresubmitError(f'Could not run go script: {e}')] - return [] # Check passed. + return [] # Check passed. + + pregenerate_script_path = input_api.os_path.join( + input_api.change.RepositoryRoot(), 'util', 'pregenerate') + return _RunTool(input_api, + output_api, + ['go', 'run', pregenerate_script_path, '-check'], + handle_result=HandlePregenerateResult) + + +def CheckBuildifier(input_api, output_api): + """ + Runs Buildifier formatting check if the affected files include + *.bazel or *.bzl files. + """ + file_paths = [] + for affected_file in input_api.AffectedFiles(include_deletes=False): + affected_file_path = affected_file.LocalPath() + if not affected_file_path.endswith(('.bzl', '.bazel')): + continue + if "third_party" in affected_file_path or "gen" in affected_file_path: + continue + file_paths.append(affected_file_path) + if not file_paths: + return [] # Check passed. + + def ExplainBuildifierError(e): + return [ + output_api.PresubmitNotifyResult( + ('You can download buildifier from ' + 'https://github.com/bazelbuild/buildtools/releases')) + ] + + # Check that `buildifier` is available on the $PATH. + # TODO(chlily): Make this compatible with the util/bot environment for CI/CQ. + if error := _RunTool(input_api, + output_api, ['buildifier', '--version'], + explain_error=ExplainBuildifierError): + return error + + def HandleBuildifierResult(stdout, stderr, retcode): + if retcode == 4: # check mode failed (reformat is needed). + return [ + output_api.PresubmitError( + ("Found incorrectly formatted *.bzl or *.bazel files. " + "Run `buildifier` to update them."), stdout.splitlines()) + ] + + return _RunTool(input_api, + output_api, ['buildifier', '--mode=check'] + file_paths, + handle_result=HandleBuildifierResult)