Add support for upstream presubmit logic on npm run lint.

This commit is contained in:
Aleksey Khoroshilov
2022-06-20 17:30:24 +07:00
parent 7ab5ffed44
commit ffd10a5576
16 changed files with 390 additions and 63 deletions
+17
View File
@@ -0,0 +1,17 @@
# Copyright (c) 2022 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */
USE_PYTHON3 = True
PRESUBMIT_VERSION = '2.0.0'
def CheckChangeLintsClean(input_api, output_api):
return input_api.canned_checks.CheckChangeLintsClean(input_api, output_api)
def CheckPylint(input_api, output_api):
return input_api.canned_checks.RunPylint(input_api,
output_api,
version='2.7')
+21 -9
View File
@@ -5,6 +5,7 @@
const path = require('path')
const fs = require('fs')
const os = require('os')
const config = require('../lib/config')
const util = require('../lib/util')
const {EOL} = require('os');
@@ -65,14 +66,14 @@ const getAllFiles = (check_folders) => {
return files.split('\n')
}
const getChangedFiles = (base_branch, check_folders) => {
if (!base_branch) {
const getChangedFiles = (options, check_folders) => {
if (options.all) {
return getAllFiles(check_folders)
}
const merge_base = runGit(['merge-base', 'origin/' + base_branch, 'HEAD'])
const merge_base = runGit(['merge-base', options.base, 'HEAD'])
if (!merge_base) {
console.error('Could not determine merge-base for branch ' + base_branch)
console.error('Could not determine merge-base for branch ' + options.base)
process.exit(1)
}
const changed_files = runGit(['diff', '--name-only', '--diff-filter', 'drt',
@@ -114,26 +115,31 @@ const runPylintLoop = (options, args, paths, report_file) => {
result &= doPylint(loop_args)
currentLen = 0
loop_args = [...args]
}
}
loop_args.push(pyPath)
currentLen += pyPath.length
}
if (currentLen > 0) {
result &= doPylint(loop_args)
result &= doPylint(loop_args)
}
return result
}
const pylint = (options = {}) => {
if (!options.base) {
options.base = 'origin/master'
} else if (!options.base.startsWith('origin/')) {
options.base = 'origin/' + options.base
}
const check_folders = ['build', 'components', 'installer', 'script', 'tools']
const report_file = 'pylint-report.txt'
const description = getDescription(options.base, check_folders)
// Get changed or all python files
const paths = getChangedFiles(options.base, check_folders)
const paths = getChangedFiles(options, check_folders)
if (!paths.length) {
console.log('No ' + description)
if (options.report) {
@@ -142,8 +148,14 @@ const pylint = (options = {}) => {
return
}
// Prepare pylint args
let args = ['-j0', '-rn']
var j_value = 0
if (process.platform === 'win32') {
// Windows can't handle unrestricted -j0 value, limit it to sane value.
j_value = Math.min(32, os.cpus().length)
}
// Prepare pylint args.
let args = [ '-j' + j_value.toString(), '-rn']
if (options.report) {
args.push('-fparseable')
// Clean previous report as we will be appending to it
+34 -3
View File
@@ -587,7 +587,7 @@ const util = {
util.run('goma_ctl', ['showflags'], options)
util.run('goma_ctl', ['stat'], options)
}
// Setting `AUTONINJA_BUILD_ID` allows tracing Goma remote execution which helps with
// debugging issues (e.g., slowness or remote-failures).
options.env.AUTONINJA_BUILD_ID = buildId
@@ -627,11 +627,42 @@ const util = {
'--base_branch=' + options.base], cmd_options)
},
format: (options = {}) => {
presubmit: (options = {}) => {
if (!options.base) {
options.base = 'origin/master'
}
// Temporary cleanup call, should be removed when everyone will remove
// 'gerrit.host' from their brave checkout.
util.runGit(
config.braveCoreDir, ['config', '--unset-all', 'gerrit.host'], true)
let cmd_options = config.defaultOptions
cmd_options.cwd = config.braveCoreDir
cmd_options = mergeWithDefault(cmd_options)
util.run('vpython', [path.join(config.braveCoreDir, 'build', 'commands', 'scripts', 'format.py'), options.full ? '--full' : ''], cmd_options)
cmd = 'git'
args = ['cl', 'presubmit', options.base, '--force']
if (options.all)
args.append('--all')
util.run(cmd, args, cmd_options)
},
format: (options = {}) => {
if (!options.base) {
options.base = 'origin/master'
}
let cmd_options = config.defaultOptions
cmd_options.cwd = config.braveCoreDir
cmd_options = mergeWithDefault(cmd_options)
cmd = 'git'
args = ['cl', 'format', '--upstream=' + options.base]
if (options.full)
args.append('--full')
if (options.js)
args.append('--js')
if (options.rust)
args.append('--rust-fmt')
if (options.swift)
args.append('--swift-format')
util.run(cmd, args, cmd_options)
},
+12
View File
@@ -249,15 +249,27 @@ program
.option('--base <base branch>', 'set the destination branch for the PR')
.action(util.lint)
program
.command('presubmit')
.option('--base <base branch>', 'set the destination branch for the PR')
.option('--all', 'run presubmit on all files')
.action(util.presubmit)
program
.command('pylint')
.option('--base <base_branch>', 'only analyse files changed relative to base_branch')
.option('--all', 'run pylint on all files')
.option('--report', 'produce a parseable report file')
.action(pylint)
program
.command('format')
.option('--base <base branch>', 'set the destination branch for the PR')
.option('--full', 'format all lines in changed files instead of only the changed lines')
.option('--js', 'format javascript code with clang-format')
.option('--rust', 'enables formatting of Rust file types using rustfmt')
.option('--swift',
'enables formatting of Swift file types using swift-format')
.action(util.format)
program
-45
View File
@@ -1,45 +0,0 @@
#!/usr/bin/env vpython
# Copyright (c) 2020 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#!/usr/bin/env vpython
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Copyright (C) 2008 Evan Martin <martine@danga.com>
import os
import sys
import git_cl
def main(args):
"""Runs clang-format and gn format on the current changelist."""
parser = git_cl.OptionParser()
parser.parse_args([])
# Change the current working directory before calling so that it
# shows the correct base.
settings = git_cl.settings
previous_cwd = os.getcwd()
os.chdir(settings.GetRoot())
try:
cmd = ['cl', 'format'] + args
print('git ' + ' '.join(cmd))
git_cl.RunGit(cmd)
except:
e = sys.exc_info()[1]
print('Could not run format: %s' % e.message)
return 1
finally:
os.chdir(previous_cwd)
print('Formatting done.')
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
@@ -56,6 +56,7 @@
// This method should be periodically pruned of year+ old migrations.
void MigrateObsoleteProfilePrefs(Profile* profile) {
// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS
#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
// Added 02/2020.
// Must be called before ChromiumImpl because it's migrating a Chromium pref
@@ -98,10 +99,12 @@ void MigrateObsoleteProfilePrefs(Profile* profile) {
#if BUILDFLAG(ENABLE_BRAVE_TRANSLATE_GO)
translate::MigrateBraveProfilePrefs(profile->GetPrefs());
#endif
// END_MIGRATE_OBSOLETE_PROFILE_PREFS
}
// This method should be periodically pruned of year+ old migrations.
void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
MigrateObsoleteLocalStatePrefs_ChromiumImpl(local_state);
#if BUILDFLAG(ENABLE_WIDEVINE)
@@ -115,4 +118,6 @@ void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
#endif
decentralized_dns::MigrateObsoleteLocalStatePrefs(local_state);
// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS
}
+5 -1
View File
@@ -1,3 +1,7 @@
# This file is used by git-cl to get repository specific information.
GERRIT_HOST: True
# Don't add GERRIT_HOST, it will make PRESUBMIT checks do unnecessary stuff.
# CODE_REVIEW_SERVER is a mock value to not generate errors during PRESUBMIT.
CODE_REVIEW_SERVER: github.com
LINT_IGNORE_REGEX: win_build_output/midl/google_update/.*
@@ -9,6 +9,11 @@
namespace brave {
// static
PrivateCdnHelper* PrivateCdnHelper::GetInstance() {
return base::Singleton<PrivateCdnHelper>::get();
}
bool PrivateCdnHelper::RemovePadding(base::StringPiece* padded_string) const {
if (!padded_string) {
return false;
@@ -6,8 +6,6 @@
#ifndef BRAVE_COMPONENTS_BRAVE_PRIVATE_CDN_PRIVATE_CDN_HELPER_H_
#define BRAVE_COMPONENTS_BRAVE_PRIVATE_CDN_PRIVATE_CDN_HELPER_H_
#include <string>
#include "base/memory/singleton.h"
#include "base/strings/string_piece.h"
@@ -18,9 +16,7 @@ class PrivateCdnHelper final {
PrivateCdnHelper(const PrivateCdnHelper&) = delete;
PrivateCdnHelper& operator=(const PrivateCdnHelper&) = delete;
static PrivateCdnHelper* GetInstance() {
return base::Singleton<PrivateCdnHelper>::get();
}
static PrivateCdnHelper* GetInstance();
bool RemovePadding(base::StringPiece* padded_string) const;
+2
View File
@@ -0,0 +1,2 @@
This file allows src/PRESUBMIT.py to be executed on brave/* files.
See presubmit_support.py for details.
+1
View File
@@ -24,6 +24,7 @@
"pull_l10n": "node ./build/commands/scripts/commands.js pull_l10n",
"chromium_rebase_l10n": "node ./build/commands/scripts/commands.js chromium_rebase_l10n",
"lint": "node ./build/commands/scripts/commands.js lint",
"presubmit": "node ./build/commands/scripts/commands.js presubmit",
"format": "node ./build/commands/scripts/commands.js format",
"test": "node ./build/commands/scripts/commands.js test",
"test:scripts": "jest build/commands/lib build/commands/scripts",
+9
View File
@@ -0,0 +1,9 @@
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 81b1b62c3ef84dedaba01ef067bafc66d9ebfbb4..7e4fb471f68b60b23b03603902f33fb0e90ca204 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -5845,3 +5845,4 @@ def CheckPythonShebang(input_api, output_api):
"Please use '#!/usr/bin/env python/2/3' as the shebang of %s" %
file))
return result
+if not globals().get('__name__'): from lib.import_inline import inline_module; inline_module('chromium_presubmit_overrides', globals(), locals())
+168
View File
@@ -0,0 +1,168 @@
# Copyright (c) 2022 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */
# This file is executed (not imported) in the context of src/PRESUBMIT.py, it
# uses existing functions from a global scope to change them. This allows us to
# alter root Chromium presubmit checks without introducing too much conflict.
# pylint: disable=protected-access
import copy
import lib.chromium_presubmit_utils as chromium_presubmit_utils
_BRAVE_DEFAULT_FILES_TO_SKIP = (r'win_build_output[\\/].*', )
# Modify depot_tools-bundled checks (Chromium calls them canned checks).
def _modify_canned_checks(input_api):
input_api.DEFAULT_FILES_TO_SKIP += _BRAVE_DEFAULT_FILES_TO_SKIP
# pylint: disable=unused-variable
# Disable upstream-specific license check.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckLicense(*_, **__):
return []
# We don't use OWNERS files.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckOwnersFormat(*_, **__):
return []
# We don't use OWNERS files.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckOwners(*_, **__):
return []
# We don't upload change to Chromium git.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckChangeWasUploaded(*_, **__):
return []
# We don't upload change to Chromium git.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckChangeHasBugField(*_, **__):
return []
# We don't upload change to Chromium git.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckTreeIsOpen(*_, **__):
return []
# Enforce format check, fix output text to show our command instead.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def CheckPatchFormatted(original_check, input_api, output_api, *args,
**kwargs):
if 'result_factory' in kwargs:
del kwargs['result_factory']
result = original_check(input_api,
output_api,
*args,
result_factory=output_api.PresubmitError,
**kwargs)
for item in result:
item._message = item._message.replace('git cl format',
'npm run format --')
item._message = item._message.replace('gn format',
'npm run format --')
return result
# Override Pylint checker to do per-file lint.
@chromium_presubmit_utils.override_check(input_api.canned_checks)
def GetPylint(original_check, input_api, *args, **kwargs):
# Change upstream logic to lint each Python file individually instead of
# getting *all* files from the root directory. Upstream does it to catch
# breakages in unmodified files.
def _FetchAllFiles(_, input_api, files_to_check, files_to_skip):
src_filter = lambda f: input_api.FilterSourceFile(
f, files_to_check=files_to_check, files_to_skip=files_to_skip)
return [
f.LocalPath()
for f in input_api.AffectedSourceFiles(src_filter)
]
# Make Pylint work on high core Windows machines. crbug.com/1190269
cpu_count = input_api.cpu_count
if input_api.is_windows:
cpu_count = min(cpu_count, 32)
with chromium_presubmit_utils.override_scope_function(
input_api.canned_checks, _FetchAllFiles):
with chromium_presubmit_utils.override_scope_variable(
input_api, 'cpu_count', cpu_count):
# is_committing = True forces pylint to be errors.
with chromium_presubmit_utils.override_scope_variable(
input_api, 'is_committing', True):
return original_check(input_api, *args, **kwargs)
# Override the first ever check defined in PRESUBMIT.py to make changes to
# input_api/output_api before any real check is run.
@chromium_presubmit_utils.override_check(
globals(), chromium_presubmit_utils.get_first_check_name(globals()))
def OverriddenFirstCheck(original_check, input_api, output_api, *args,
**kwargs):
_modify_canned_checks(input_api)
return original_check(input_api, output_api, *args, **kwargs)
# We don't use OWNERS files.
@chromium_presubmit_utils.override_check(globals())
def CheckSecurityOwners(*_, **__):
return []
# This validates added strings with screenshot tests which we don't use.
@chromium_presubmit_utils.override_check(globals())
def CheckStrings(*_, **__):
return []
# Don't check upstream pydeps.
@chromium_presubmit_utils.override_check(globals())
def CheckPydepsNeedsUpdating(*_, **__):
return []
# Override to add brave/ prefix for the header guard checks.
@chromium_presubmit_utils.override_check(globals())
def CheckForIncludeGuards(original_check, input_api, output_api, *args,
**kwargs):
def AffectedSourceFiles(self, original_method, source_file):
def PrependBrave(affected_file):
affected_file = copy.copy(affected_file)
affected_file._path = f'brave/{affected_file._path}'
return affected_file
return [
PrependBrave(f) for f in filter(self.FilterSourceFile,
original_method(source_file))
]
with chromium_presubmit_utils.override_scope_function(
input_api, AffectedSourceFiles):
return original_check(input_api, output_api, *args, **kwargs)
# Ignore some files to check for JSON parse errors.
@chromium_presubmit_utils.override_check(globals())
def CheckParseErrors(original_check, input_api, output_api, *args, **kwargs):
# pylint: disable=undefined-variable
_KNOWN_TEST_DATA_AND_INVALID_JSON_FILE_PATTERNS.append(r'tsconfig\.json$')
return original_check(input_api, output_api, *args, **kwargs)
# Modify check to include all files to check, otherwise it checks only
# ^chrome, ^components, etc. (ignoring ^brave).
@chromium_presubmit_utils.override_check(globals())
def CheckMPArchApiUsage(original_check, input_api, output_api, *args,
**kwargs):
def AffectedFiles(self, original_method, *_, **__):
return original_method(False, self.FilterSourceFile)
with chromium_presubmit_utils.override_scope_function(
input_api, AffectedFiles):
return original_check(input_api, output_api, *args, **kwargs)
+92
View File
@@ -0,0 +1,92 @@
# Copyright (c) 2022 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */
import contextlib
import inspect
import types
def override_check(scope, name=None):
"""Replaces existing PRESUBMIT check. Can be used with globals() scope or a
class scope (such as input_api.canned_checks)."""
def decorator(new_func):
is_dict_scope = isinstance(scope, dict)
check_name = name or new_func.__name__
if is_dict_scope:
original_check = scope.get(check_name, None)
else:
original_check = getattr(scope, check_name, None)
if not callable(original_check):
print(f'WARNING: {check_name} check to override not found.\n'
'Please update chromium_presubmit_overrides.py!')
def noop_check(*_, **__):
return []
return noop_check
def wrapped_f(*args, **kwargs):
return new_func(original_check, *args, **kwargs)
if is_dict_scope:
scope[check_name] = wrapped_f
else:
setattr(scope, check_name, wrapped_f)
return wrapped_f
return decorator
@contextlib.contextmanager
def override_scope_function(scope, new_function, name=None):
"""Scoped function override helper. Can override a scope method or a class
function."""
function_name = name or new_function.__name__
original_function = getattr(scope, function_name, None)
try:
if not callable(original_function):
raise NameError(f'Failed to override scope function: '
f'{function_name} not found or not callable')
def wrapped_method(self, *args, **kwargs):
return new_function(self, original_function, *args, **kwargs)
def wrapped_function(*args, **kwargs):
return new_function(original_function, *args, **kwargs)
if inspect.ismethod(original_function):
setattr(scope, function_name,
types.MethodType(wrapped_method, scope))
else:
setattr(scope, function_name, wrapped_function)
yield
finally:
if original_function:
setattr(scope, function_name, original_function)
@contextlib.contextmanager
def override_scope_variable(scope, name, value):
"""Scoped variable override helper."""
if not hasattr(scope, name):
raise NameError(f'Failed to override scope variable: {name} not found')
original_value = getattr(scope, name)
try:
setattr(scope, name, value)
yield
finally:
setattr(scope, name, original_value)
def get_first_check_name(scope):
"""Returns first Check* method from the scope."""
assert isinstance(scope, dict)
for key, value in scope.items():
if key.startswith('Check') and callable(value):
return key
raise LookupError('Check* method not found in scope')
+17
View File
@@ -0,0 +1,17 @@
# Copyright (c) 2022 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */
import importlib
def inline_module(module_name, _globals, _locals):
"""Finds module and inlines it by executing using passed scopes."""
module_spec = importlib.util.find_spec(module_name)
if not module_spec:
raise ModuleNotFoundError(
f"Can't find module to inline: {module_name}")
# pylint: disable=exec-used
exec(module_spec.loader.get_data(module_spec.loader.path), _globals,
_locals)
+1
View File
@@ -0,0 +1 @@
exclude_files=midl