blob: 1ffa35160767123b5672977ce760918e8166e24b [file]
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Top-level presubmit script for build.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
details on the presubmit API built into gcl.
"""
PRESUBMIT_VERSION = '2.0.0'
USE_PYTHON3 = True
DISABLED_PYLINT_WARNINGS = [
'no-init', # Class has no __init__ method
'super-init-not-called', # __init__ method from base class is not called
'cyclic-import',
'unused-argument',
'import-outside-toplevel',
'inconsistent-return-statements',
'no-member',
'no-value-for-parameter',
'stop-iteration-return',
'subprocess-run-check',
# TODO(crbug.com/441191932): Re-enable these checks.
# ************* Module action
# siso/subcmd/ninja/testdata/TestBuild_CopyLocalOut/tools/action.py:21:8: W0612: Unused variable 'i' (unused-variable)
# ************* Module clang++
# siso/subcmd/ninja/testdata/TestBuild_Cleandead/tools/clang++.py:24:12: W0622: Redefining built-in 'input' (redefined-builtin)
# ************* Module link
# siso/subcmd/ninja/testdata/TestBuild_EdgeRule/tools/link.py:19:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_EdgeRule_solibs/tools/link.py:19:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_EdgeRule_solibs_recursive/tools/link.py:19:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_EdgeRule_stamp_solibs/tools/link.py:20:6: W0622: Redefining built-in 'input' (redefined-builtin)
# ************* Module ar
# siso/subcmd/ninja/testdata/TestBuild_EdgeRule/tools/ar.py:20:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_Local_Inputs/tools/action.py:17:6: W0622: Redefining built-in 'input' (redefined-builtin)
# ************* Module gen
# siso/subcmd/ninja/testdata/TestBuild_RemovedArtifact/tools/gen.py:8:0: W0622: Redefining built-in 'input' (redefined-builtin)
# ************* Module archive
# siso/subcmd/ninja/testdata/TestBuild_OutputDir/tools/archive.py:19:8: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_Restat/tools/action.py:17:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_RestatMultiout/tools/action.py:18:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_Restat_RestatContent/tools/action.py:17:6: W0622: Redefining built-in 'input' (redefined-builtin)
# ************* Module action2
# siso/subcmd/ninja/testdata/TestBuild_RestatMultiout/tools/action2.py:17:6: W0622: Redefining built-in 'input' (redefined-builtin)
# siso/subcmd/ninja/testdata/TestBuild_offline/tools/gen.py:9:0: W0622: Redefining built-in 'input' (redefined-builtin)
'redefined-builtin',
# ************* Module action
# siso/subcmd/ninja/testdata/TestBuild_CopyLocalOut/tools/action.py:21:8: W0612: Unused variable 'i' (unused-variable)
'unused-variable',
]
THIRD_PARTY_DIRS = [
'siso/third_party',
]
def CheckChange(input_api, output_api):
# Default source file filter doesn't include Go.
# e.g. CheckChangeHasNoTabs would conflict since gofmt enforces tabs.
source_file_filter_incl_go = lambda path: input_api.FilterSourceFile(
path,
files_to_check=list(input_api.DEFAULT_FILES_TO_CHECK) + [
r'.+\.go$',
],
files_to_skip=[
r'.*pb[^/]*\.go$',
] + [rf'{d}/.*' for d in THIRD_PARTY_DIRS])
results = []
results += input_api.canned_checks.CheckDoNotSubmit(input_api, output_api)
results += input_api.canned_checks.CheckChangeHasNoTabs(
input_api, output_api)
results += input_api.canned_checks.CheckPatchFormatted(
input_api, output_api, check_clang_format=False)
results += input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
input_api, output_api, source_file_filter=source_file_filter_incl_go)
results += input_api.canned_checks.CheckInclusiveLanguage(
input_api, output_api)
results += input_api.canned_checks.CheckLicense(
input_api, output_api, source_file_filter=source_file_filter_incl_go)
return results
def CheckGoChanges(input_api, output_api):
file_filter = lambda path: input_api.FilterSourceFile(
path,
files_to_check=[r'.*\.go$'],
files_to_skip=THIRD_PARTY_DIRS + [r'.*\.pb\.go$', r'.*\.gen\.go$'])
affected_files = sorted(
[
# TODO(b/430465030): Fix this.
# pylint: disable=unnecessary-comprehension
f for f in input_api.AffectedFiles(
include_deletes=False, file_filter=file_filter)
],
key=lambda source: source.AbsoluteLocalPath())
if not affected_files:
return []
# Fetch dependencies from CIPD.
# This is done in this script because we don't use gclient to manage Go
# dependencies.
# golangci-lint should always be fetched to ensure errors are consistent
# between local developer machines and presubmit bots.
cipd_root = input_api.os_path.join(input_api.change.RepositoryRoot(),
'.cipd_bin')
ensure_file_content = ('infra/3pp/tools/golangci-lint/${platform} '
'version:3@2.12.2.chromium.1\n')
go = 'go'
gofmt = 'gofmt'
golangci_lint = input_api.os_path.join(cipd_root, 'golangci-lint')
env = input_api.environ.copy()
if input_api.is_committing and input_api.gerrit:
# Go is only needed on presubmit bots.
# This is because we use go.mod to manage the expected Go version on local
# developer machines, and expect Go to be available on $PATH.
ensure_file_content += ('infra/3pp/tools/go/${platform} '
'version:3@1.26.3\n')
go = input_api.os_path.join(cipd_root, 'bin', 'go')
gofmt = input_api.os_path.join(cipd_root, 'bin', 'gofmt')
env['PATH'] = input_api.os_path.join(cipd_root, 'bin') + ':' + env['PATH']
input_api.subprocess.check_call(
[
'cipd',
'ensure',
'-log-level',
'warning',
'-root',
str(cipd_root),
'-ensure-file',
'-',
],
stdin=ensure_file_content.encode('utf-8'),
cwd=input_api.change.RepositoryRoot(),
)
# Make sure Go is available on $PATH.
try:
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"go isn't available on your $PATH: {e}")
]
# Run `go fmt` on files individually.
bad = []
for f in affected_files:
try:
stdout, _ = input_api.subprocess.check_call_out(
[gofmt, '-s', '-d', f.LocalPath()],
stdout=input_api.subprocess.PIPE,
stderr=input_api.subprocess.PIPE)
if stdout.strip():
bad.append(f)
except input_api.subprocess.CalledProcessError as e:
return [output_api.PresubmitError(f'gofmt failed to run: {e}')]
if bad:
return [
output_api.PresubmitError(('Found badly formatted Go file(s). '
'Run `gofmt -s -w .` to fix them.'), bad)
]
# Run `golangci-lint` on folders.
dirs = {
input_api.os_path.dirname(f.AbsoluteLocalPath()):
input_api.os_path.dirname(f.LocalPath()) for f in affected_files
}
if input_api.is_committing:
error_type = output_api.PresubmitError
else:
error_type = output_api.PresubmitPromptWarning
tests = []
for absolute, pretty in sorted(dirs.items()):
kwargs = {'cwd': absolute}
if env:
kwargs['env'] = env
tests.append(
input_api.Command(
name=f'Check golangci-lint on {pretty}',
cmd=[
golangci_lint, 'run', '--timeout=15m',
'--allow-parallel-runners', '.'
],
kwargs=kwargs,
message=error_type))
return input_api.RunTests(tests)
def CheckPythonChanges(input_api, output_api):
files_to_skip = list(input_api.DEFAULT_FILES_TO_SKIP)
files_to_skip += [rf'{d}/.*' for d in THIRD_PARTY_DIRS]
files_to_skip += _IgnoredPaths(input_api)
return input_api.RunTests(
input_api.canned_checks.GetPylint(
input_api,
output_api,
files_to_skip=files_to_skip,
disabled_warnings=DISABLED_PYLINT_WARNINGS,
version='2.7',
))
def _IgnoredPaths(input_api):
# This computes the list if repository-root-relative paths which are
# ignored by .gitignore files. There is probably a faster way to do this.
status_output = input_api.subprocess.check_output(
['git', 'status', '--porcelain', '--ignored'], text=True)
statuses = [(line[:2], line[3:]) for line in status_output.splitlines()]
return [
input_api.re.escape(path)
for (mode, path) in statuses
if mode in ('!!', '??') and not path.endswith('.pyc')
]