Files
google--adk-python/scripts/compliance_checks.py
T
George Weale 656af9306d chore: prune redundant entries from the mTLS exclusion list
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 960529884
2026-08-06 15:15:28 -07:00

156 lines
5.4 KiB
Python
Executable File

#!/usr/bin/env python3
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Runs compliance checks on ADK source files.
This script is used as a pre-commit hook and in CI to enforce coding standards.
"""
import argparse
import os
import re
import sys
# Legacy files that still hardcode a non-mTLS googleapis.com endpoint. A file
# belongs here only while it would fail the mTLS check; once it passes on its
# own, drop its entry so the check applies again. Do not add new files to this
# list. All new code must support mTLS.
_EXCLUDED_FROM_MTLS = {
'contributing/samples/environment_and_skills/e2b_environment/agent.py',
'contributing/samples/integrations/gcp_auth/agent.py',
'contributing/samples/integrations/integration_connector_euc_agent/agent.py',
'contributing/samples/integrations/oauth_calendar_agent/agent.py',
'contributing/samples/mcp/mcp_service_account_agent/agent.py',
'contributing/samples/models/interactions_api/main.py',
'contributing/samples/multimodal/static_non_text_content/agent.py',
'src/google/adk/auth/auth_credential.py',
'src/google/adk/integrations/bigquery/metadata_tool.py',
'src/google/adk/tools/_google_credentials.py',
'src/google/adk/tools/apihub_tool/clients/apihub_client.py',
'src/google/adk/tools/google_api_tool/google_api_toolset.py',
'src/google/adk/tools/openapi_tool/auth/auth_helpers.py',
'tests/unittests/auth/test_credential_manager.py',
'tests/unittests/flows/llm_flows/test_functions_request_euc.py',
'tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py',
'tests/unittests/tools/data_agent/test_data_agent_tool.py',
'tests/unittests/tools/google_api_tool/test_docs_batchupdate.py',
'tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py',
'tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py',
'tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py',
'tests/unittests/tools/test_base_google_credentials_manager.py',
'tests/unittests/workflow/utils/test_workflow_hitl_utils.py',
}
def check_logger(content: str) -> bool:
# Forbidden: getLogger(__name__) without the 'google_adk.' prefix.
pattern = re.compile(r'logger\s*=\s*logging\.getLogger\(__name__\)')
return not pattern.search(content)
def check_future_annotations(content: str, filename: str) -> bool:
# Exclude: __init__.py, version.py, tests/, contributing/samples/
if (
filename.endswith('__init__.py')
or filename.endswith('version.py')
or 'tests/' in filename
or 'contributing/samples/' in filename
):
return True
return 'from __future__ import annotations' in content
def check_cli_import(content: str, filename: str) -> bool:
# Exclude: cli/, apihub_toolset.py, tests/, contributing/samples/
if (
'cli/' in filename
or filename.endswith('apihub_toolset.py')
or 'tests/' in filename
or 'contributing/samples/' in filename
):
return True
# Pattern: ^from.*\bcli\b.*import.*$ (multiline)
pattern = re.compile(r'^from.*\bcli\b.*import.*$', re.MULTILINE)
return not pattern.search(content)
def check_mtls(content: str, filename: str) -> bool:
if filename in _EXCLUDED_FROM_MTLS:
return True
urls = re.findall(
r'https?://[a-zA-Z0-9.-]+\.googleapis\.com[^"\'\s]*', content
)
non_scope_urls = [
url
for url in urls
if not re.match(r'https?://www\.googleapis\.com/auth(/|$)', url)
]
if non_scope_urls:
return '.mtls.googleapis.com' in content
return True
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('files', nargs='*', help='Files to check')
args = parser.parse_args()
failed = False
for f in args.files:
# Skip directories if they are passed accidentally
if not os.path.isfile(f):
continue
try:
with open(f, 'r', encoding='utf-8') as file:
content = file.read()
except Exception as e: # pylint: disable=broad-except
print(f'Error reading {f}: {e}')
continue
# Run checks
if not check_logger(content):
print(
f"❌ {f}: Found forbidden use of 'logger ="
" logging.getLogger(__name__)'. Please use 'logger ="
' logging.getLogger("google_adk." + __name__)\' instead.'
)
failed = True
if not check_future_annotations(content, f):
print(f"❌ {f}: Missing 'from __future__ import annotations'.")
failed = True
if not check_cli_import(content, f):
print(
f'❌ {f}: Do not import from the cli package outside of the cli'
' package.'
)
failed = True
if not check_mtls(content, f):
print(
f'❌ {f}: Found hardcoded googleapis.com endpoints without mTLS'
' support.'
)
failed = True
if failed:
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()