mirror of https://github.com/mongodb/mongo
SERVER-98403 Lint redundant/bad CODEOWNERS rules (#31416)
GitOrigin-RevId: cc41658a1e273216772c363c49b93170d695dbe9
This commit is contained in:
parent
e8301ecf3c
commit
9b01b02ef5
|
|
@ -0,0 +1,5 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"CODEOWNERS",
|
||||
])
|
||||
|
|
@ -11,6 +11,7 @@ exports_files([
|
|||
"pyproject.toml",
|
||||
"poetry.lock",
|
||||
"symbols.orderfile",
|
||||
"codeowners-validator",
|
||||
])
|
||||
|
||||
npm_link_all_packages(name = "node_modules")
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ exports_files([
|
|||
|
||||
py_binary(
|
||||
name = "codeowners",
|
||||
srcs = ["codeowners_generate.py"],
|
||||
srcs = [
|
||||
"codeowners_generate.py",
|
||||
"download_codeowners_validator.py",
|
||||
"validate_codeowners.py",
|
||||
],
|
||||
main = "codeowners_generate.py",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
|
|
@ -15,6 +19,42 @@ py_binary(
|
|||
"pyyaml",
|
||||
group = "core",
|
||||
),
|
||||
dependency(
|
||||
"typer",
|
||||
group = "core",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "download_codeowners_validator",
|
||||
srcs = ["download_codeowners_validator.py"],
|
||||
main = "download_codeowners_validator.py",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
dependency(
|
||||
"typer",
|
||||
group = "core",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "validate_codeowners",
|
||||
srcs = [
|
||||
"download_codeowners_validator.py",
|
||||
"validate_codeowners.py",
|
||||
],
|
||||
data = [
|
||||
"//.github:CODEOWNERS",
|
||||
],
|
||||
main = "validate_codeowners.py",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
dependency(
|
||||
"typer",
|
||||
group = "core",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from functools import lru_cache
|
|||
|
||||
import yaml
|
||||
|
||||
from buildscripts.validate_codeowners import run_validator
|
||||
|
||||
OWNERS_FILE_NAME = "OWNERS.yml"
|
||||
|
||||
|
||||
|
|
@ -170,6 +172,25 @@ def print_diff_and_instructions(old_codeowners_contents, new_codeowners_contents
|
|||
print("python buildscripts/install_bazel.py")
|
||||
|
||||
|
||||
def validate_generated_codeowners() -> int:
|
||||
"""Validate the generated CODEOWNERS file.
|
||||
|
||||
Returns:
|
||||
int: 0 if validation succeeds, non-zero otherwise.
|
||||
"""
|
||||
print("\nValidating generated CODEOWNERS file...")
|
||||
try:
|
||||
validation_result = run_validator("./")
|
||||
if validation_result != 0:
|
||||
print("CODEOWNERS validation failed!", file=sys.stderr)
|
||||
return validation_result
|
||||
print("CODEOWNERS validation successful!")
|
||||
return 0
|
||||
except Exception as exc:
|
||||
print(f"Error during CODEOWNERS validation: {str(exc)}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def main():
|
||||
# If we are running in bazel, default the directory to the workspace
|
||||
default_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY")
|
||||
|
|
@ -245,13 +266,14 @@ def main():
|
|||
return 1
|
||||
|
||||
print("CODEOWNERS file is up to date")
|
||||
return 0
|
||||
return validate_generated_codeowners()
|
||||
|
||||
with open(output_file, "w") as file:
|
||||
file.write(new_contents)
|
||||
print(f"Successfully wrote to the CODEOWNERS file at: {os.path.abspath(output_file)}")
|
||||
|
||||
return 0
|
||||
# Add validation after generating CODEOWNERS file
|
||||
return validate_generated_codeowners()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Script for downloading codeowners-validator."""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import platform
|
||||
import stat
|
||||
import tarfile
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from typing import Annotated
|
||||
|
||||
import typer
|
||||
|
||||
VALIDATOR_VERSION = "0.7.4"
|
||||
VALIDATOR_BINARY_NAME = "codeowners-validator"
|
||||
RELEASE_URL = (
|
||||
f"https://github.com/mszostok/codeowners-validator/releases/download/v{VALIDATOR_VERSION}/"
|
||||
)
|
||||
|
||||
VALIDATOR_SHA256 = {
|
||||
"windows": {
|
||||
"x86_64": "4e71fcc686ad4f275a2fe9af0e3290a443e2907b07bd946f174109d5c057cda5",
|
||||
"i386": "08bc65d8773b264a1e45d7589d53aab05ce697f1ddb46ba15e0c7468b0574fb6",
|
||||
},
|
||||
"linux": {
|
||||
"x86_64": "73677228e3c7ddf3f9296246f2c088375c5b1b82385069360867d026ab790028",
|
||||
"i386": "6384762879d26c886da2a3ae9e99b076d1cf35749173ae99fea1ebb6c245b094",
|
||||
"arm64": "9286238af043e3bd42b2309530844e14ed51ec6c5c1aac9ac034068eb8d78668",
|
||||
},
|
||||
"darwin": {
|
||||
"x86_64": "25ae64da52eb2aad357af1275adb46fdf0c2d560def1cb9479800c775e65aa8e",
|
||||
"arm64": "efa77bf32d971181e007825a9c1b1239a690d8bab56e3e606e010c61511fd19e",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def determine_platform():
|
||||
"""Determine the operating system."""
|
||||
syst = platform.system()
|
||||
pltf = None
|
||||
if syst == "Darwin":
|
||||
pltf = "darwin"
|
||||
elif syst == "Windows":
|
||||
pltf = "windows"
|
||||
elif syst == "Linux":
|
||||
pltf = "linux"
|
||||
else:
|
||||
raise RuntimeError("Platform cannot be inferred.")
|
||||
return pltf
|
||||
|
||||
|
||||
def determine_architecture():
|
||||
"""Determine the CPU architecture."""
|
||||
arch = None
|
||||
machine = platform.machine().lower()
|
||||
if machine in ("amd64", "x86_64"):
|
||||
arch = "x86_64"
|
||||
elif machine in ("arm64", "aarch64"):
|
||||
arch = "arm64"
|
||||
elif machine in ("i386", "i686", "x86"):
|
||||
arch = "i386"
|
||||
else:
|
||||
raise RuntimeError(f"Detected architecture is not supported: {machine}")
|
||||
return arch
|
||||
|
||||
|
||||
def sha256_file(filename: str) -> str:
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(filename, "rb") as f:
|
||||
for block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(block)
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
def download_validator_binary(download_location: str):
|
||||
"""Download the codeowners-validator binary."""
|
||||
|
||||
# expand user to get absolute path
|
||||
download_location = os.path.expanduser(download_location)
|
||||
workspace_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY", ".")
|
||||
if workspace_dir:
|
||||
download_location = os.path.join(workspace_dir, download_location)
|
||||
|
||||
operating_system = determine_platform()
|
||||
architecture = determine_architecture()
|
||||
|
||||
if operating_system == "windows":
|
||||
extension = ".zip"
|
||||
else:
|
||||
extension = ".tar.gz"
|
||||
|
||||
binary_name = (
|
||||
f"{VALIDATOR_BINARY_NAME}_{VALIDATOR_VERSION}_{operating_system}_{architecture}{extension}"
|
||||
)
|
||||
url = f"{RELEASE_URL}{binary_name}"
|
||||
|
||||
# Download the archive
|
||||
archive_location = os.path.join(download_location, binary_name)
|
||||
urllib.request.urlretrieve(url, archive_location)
|
||||
print(f"Downloaded codeowners-validator from {url} to {archive_location}")
|
||||
|
||||
# Extract archive
|
||||
if operating_system == "windows":
|
||||
with zipfile.ZipFile(archive_location) as zip_ref:
|
||||
for file_name in zip_ref.namelist():
|
||||
if file_name == VALIDATOR_BINARY_NAME:
|
||||
zip_ref.extract(file_name, download_location)
|
||||
else:
|
||||
with tarfile.open(archive_location) as tar:
|
||||
for member in tar.getmembers():
|
||||
if member.name == VALIDATOR_BINARY_NAME:
|
||||
tar.extract(member, download_location)
|
||||
|
||||
binary_path = os.path.join(
|
||||
download_location, VALIDATOR_BINARY_NAME + (".exe" if operating_system == "windows" else "")
|
||||
)
|
||||
|
||||
expected_sha = VALIDATOR_SHA256.get(operating_system, {}).get(architecture)
|
||||
print(f"Expected SHA256: {expected_sha}")
|
||||
if not expected_sha:
|
||||
raise RuntimeError(f"No SHA256 hash found for {operating_system}/{architecture}")
|
||||
|
||||
calculated_sha = sha256_file(binary_path)
|
||||
print(f"Calculated SHA256: {calculated_sha}")
|
||||
if calculated_sha != expected_sha:
|
||||
raise RuntimeError(
|
||||
f"Downloaded file from {url} calculated sha ({calculated_sha}) did not match expected sha ({expected_sha})"
|
||||
)
|
||||
|
||||
# Set executable permissions on Unix-like systems
|
||||
if operating_system != "windows":
|
||||
os.chmod(binary_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
print(f"Set user executable permissions on {binary_path}")
|
||||
|
||||
# Clean up archive
|
||||
os.remove(archive_location)
|
||||
|
||||
|
||||
def main(
|
||||
download_location: Annotated[
|
||||
str,
|
||||
typer.Option(
|
||||
help="Directory to download the codeowners-validator binary to.",
|
||||
),
|
||||
] = "./",
|
||||
):
|
||||
"""Downloads codeowners-validator for use in evergreen and local development."""
|
||||
download_validator_binary(download_location=download_location)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Script for validating CODEOWNERS file."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Annotated
|
||||
|
||||
import typer
|
||||
|
||||
from buildscripts.download_codeowners_validator import download_validator_binary
|
||||
|
||||
|
||||
def get_validator_env() -> dict:
|
||||
"""Prepare the environment for the codeowners-validator."""
|
||||
env = os.environ.copy()
|
||||
|
||||
env.update(
|
||||
{
|
||||
"REPOSITORY_PATH": ".",
|
||||
"CHECKS": "duppatterns,syntax",
|
||||
"EXPERIMENTAL_CHECKS": "avoid-shadowing",
|
||||
"OWNER_CHECKER_REPOSITORY": "10gen/mongo",
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
def run_validator(validator_path: str) -> int:
|
||||
"""Run the codeowners validation."""
|
||||
validator_path = os.path.join(os.path.expanduser(validator_path), "codeowners-validator")
|
||||
downloaded_by_current_script = False
|
||||
# If we are running in bazel, default the directory to the workspace
|
||||
workspace_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY", ".")
|
||||
if workspace_dir:
|
||||
validator_path = os.path.join(workspace_dir, validator_path)
|
||||
|
||||
if not os.path.isfile(validator_path):
|
||||
print(f"Validator not found at {validator_path}, attempting to download...")
|
||||
try:
|
||||
download_validator_binary(os.path.dirname(validator_path))
|
||||
if not os.path.isfile(validator_path):
|
||||
print(
|
||||
f"Error: Validator still not found at {validator_path} after download attempt",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
downloaded_by_current_script = True
|
||||
except Exception as exc:
|
||||
print(f"Failed to download validator: {str(exc)}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"Using validator at: {validator_path}")
|
||||
env = get_validator_env()
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[validator_path], env=env, check=True, capture_output=True, text=True
|
||||
)
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
return 0
|
||||
except subprocess.CalledProcessError as exc:
|
||||
if exc.stdout:
|
||||
print(exc.stdout, file=sys.stderr)
|
||||
if exc.stderr:
|
||||
print(exc.stderr, file=sys.stderr)
|
||||
return exc.returncode
|
||||
except FileNotFoundError:
|
||||
print("Error: Failed to run codeowners-validator after installation", file=sys.stderr)
|
||||
return 1
|
||||
finally:
|
||||
if downloaded_by_current_script and os.path.isfile(validator_path):
|
||||
os.remove(validator_path)
|
||||
|
||||
|
||||
def main(
|
||||
validator_path: Annotated[
|
||||
str, typer.Option(help="Path to the codeowners-validator binary")
|
||||
] = "./",
|
||||
) -> int:
|
||||
"""Validate CODEOWNERS file using codeowners-validator."""
|
||||
return run_validator(validator_path=validator_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
||||
Loading…
Reference in New Issue