mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
Meta: Ensure that all port patches are documented in the linter
This adds a list of ports without descriptions to filter out the existing ports with unexplained ports, this list should *not* be appended to! It also adds a (for now) disabled check that ensures all ports have patches made with (or compatible with) git.
This commit is contained in:
parent
fc02370dc7
commit
8d30e14d28
Notes:
sideshowbarker
2024-07-17 20:47:44 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/8d30e14d28d Pull-request: https://github.com/SerenityOS/serenity/pull/11809 Reviewed-by: https://github.com/bgianfo
1 changed files with 276 additions and 0 deletions
|
@ -4,6 +4,8 @@ import os
|
|||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
# Matches e.g. "| [`bash`](bash/) | GNU Bash | 5.0 | https://www.gnu.org/software/bash/ |"
|
||||
# and captures "bash" in group 1, "bash/" in group 2, "<spaces>" in group 3, "GNU Bash" in group 4, "5.0" in group 5
|
||||
|
@ -26,6 +28,114 @@ IGNORE_FILES = {
|
|||
'.hosted_defs.sh'
|
||||
}
|
||||
|
||||
# Matches port names in Ports/foo/ReadMe.md
|
||||
PORT_NAME_REGEX = re.compile(r'([ .()[\]{}\w-]+)\.patch')
|
||||
PORTS_MISSING_DESCRIPTIONS = {
|
||||
'Another-World',
|
||||
'binutils',
|
||||
'chester',
|
||||
'cmatrix',
|
||||
'c-ray',
|
||||
'curl',
|
||||
'dash',
|
||||
'diffutils',
|
||||
'dosbox-staging',
|
||||
'dropbear',
|
||||
'ed',
|
||||
'emu2',
|
||||
'epsilon',
|
||||
'figlet',
|
||||
'flex',
|
||||
'fontconfig',
|
||||
'freeciv',
|
||||
'freedink',
|
||||
'freetype',
|
||||
'gawk',
|
||||
'gcc',
|
||||
'gdb',
|
||||
'genemu',
|
||||
'gettext',
|
||||
'git',
|
||||
'gltron',
|
||||
'gmp',
|
||||
'gnucobol',
|
||||
'gnupg',
|
||||
'gnuplot',
|
||||
'gsl',
|
||||
'harfbuzz',
|
||||
'indent',
|
||||
'jq',
|
||||
'klong',
|
||||
'libassuan',
|
||||
'libgcrypt',
|
||||
'libgd',
|
||||
'libgpg-error',
|
||||
'libiconv',
|
||||
'libicu',
|
||||
'libjpeg',
|
||||
'libksba',
|
||||
'libmodplug',
|
||||
'liboggz',
|
||||
'libpng',
|
||||
'libpuffy',
|
||||
'libsodium',
|
||||
'libvorbis',
|
||||
'libzip',
|
||||
'lua',
|
||||
'm4',
|
||||
'make',
|
||||
'mandoc',
|
||||
'mbedtls',
|
||||
'milkytracker',
|
||||
'mrsh',
|
||||
'mruby',
|
||||
'nano',
|
||||
'ncurses',
|
||||
'neofetch',
|
||||
'nethack',
|
||||
'ninja',
|
||||
'npiet',
|
||||
'npth',
|
||||
'ntbtls',
|
||||
'nyancat',
|
||||
'oksh',
|
||||
'openssh',
|
||||
'openssl',
|
||||
'openttd',
|
||||
'opentyrian',
|
||||
'p7zip',
|
||||
'patch',
|
||||
'pcre2',
|
||||
'pfetch',
|
||||
'php',
|
||||
'pkgconf',
|
||||
'pt2-clone',
|
||||
'qt6-qtbase',
|
||||
'ruby',
|
||||
'sam',
|
||||
'scummvm',
|
||||
'SDL2_image',
|
||||
'SDL2_mixer',
|
||||
'SDL2_net',
|
||||
'SDL2_ttf',
|
||||
'sl',
|
||||
'sqlite',
|
||||
'tcl',
|
||||
'tinycc',
|
||||
'tr',
|
||||
'tuxracer',
|
||||
'vitetris',
|
||||
'wget',
|
||||
'xz',
|
||||
'zsh',
|
||||
'zstd',
|
||||
}
|
||||
|
||||
# FIXME: Once everything is converted into `git format-patch`-style patches,
|
||||
# enable this to allow only `git format-patch` patches.
|
||||
REQUIRE_GIT_PATCHES = False
|
||||
GIT_PATCH_SUBJECT_RE = re.compile(r'Subject: (.*)\n')
|
||||
|
||||
|
||||
def read_port_table(filename):
|
||||
"""Open a file and find all PORT_TABLE_REGEX matches.
|
||||
|
@ -132,6 +242,163 @@ def check_package_files(ports):
|
|||
return all_good
|
||||
|
||||
|
||||
def get_and_check_port_patch_list(ports):
|
||||
"""Checks all port patches and returns the port list/properties
|
||||
|
||||
Args:
|
||||
ports (list): List of all ports to check
|
||||
|
||||
Returns:
|
||||
all_good (bool): No errors encountered
|
||||
all_properties (dict): Mapping of port to port properties
|
||||
"""
|
||||
all_port_properties = {}
|
||||
all_good = True
|
||||
|
||||
for port in ports:
|
||||
patches_directory = f"{port}/patches"
|
||||
|
||||
if not os.path.exists(patches_directory):
|
||||
continue
|
||||
|
||||
if not os.path.isdir(patches_directory):
|
||||
print(f"Ports/{port}/patches exists, but is not a directory. This is not right!")
|
||||
all_good = False
|
||||
continue
|
||||
|
||||
patches_path = Path(patches_directory)
|
||||
patches_readme_path = patches_path / "ReadMe.md"
|
||||
patch_files = set(patches_path.glob("*.patch"))
|
||||
non_patch_files = set(patches_path.glob("*")) - patch_files - {patches_readme_path}
|
||||
|
||||
port_properties = {
|
||||
"patches_path": patches_path,
|
||||
"patches_readme_path": patches_readme_path,
|
||||
"patch_files": patch_files,
|
||||
"non_patch_files": non_patch_files
|
||||
}
|
||||
all_port_properties[port] = port_properties
|
||||
|
||||
if len(non_patch_files) != 0:
|
||||
print("Ports/{port}/patches contains the following non-patch files:",
|
||||
', '.join(x.name for x in non_patch_files))
|
||||
all_good = False
|
||||
|
||||
return all_good, all_port_properties
|
||||
|
||||
|
||||
def check_descriptions_for_port_patches(patches):
|
||||
"""Ensure that ports containing patches have them documented.
|
||||
|
||||
Args:
|
||||
patches (dict): Dictionary mapping ports to all their patches
|
||||
|
||||
Returns:
|
||||
bool: no errors encountered
|
||||
"""
|
||||
|
||||
all_good = True
|
||||
for port, properties in patches.items():
|
||||
patches_readme_path = properties["patches_readme_path"]
|
||||
patch_files = properties["patch_files"]
|
||||
|
||||
readme_file_exists = patches_readme_path.exists()
|
||||
if len(patch_files) == 0:
|
||||
print(f"Ports/{port}/patches exists, but contains no patches", end="")
|
||||
if readme_file_exists:
|
||||
print(", yet it contains a ReadMe.md")
|
||||
else:
|
||||
print()
|
||||
all_good = False
|
||||
continue
|
||||
|
||||
if not readme_file_exists:
|
||||
if port not in PORTS_MISSING_DESCRIPTIONS:
|
||||
print(f"Ports/{port}/patches contains patches but no ReadMe.md describing them")
|
||||
all_good = False
|
||||
continue
|
||||
|
||||
with open(str(patches_readme_path), 'r', encoding='utf-8') as f:
|
||||
readme_contents = []
|
||||
for line in f:
|
||||
if not line.startswith('#'):
|
||||
continue
|
||||
match = PORT_NAME_REGEX.search(line)
|
||||
if match:
|
||||
readme_contents.append(match.group(1))
|
||||
|
||||
patch_names = set(Path(x).stem for x in patch_files)
|
||||
|
||||
patches_ok = True
|
||||
for patch_name in patch_names:
|
||||
if patch_name not in readme_contents:
|
||||
if port not in PORTS_MISSING_DESCRIPTIONS:
|
||||
print(f"Ports/{port}/patches/{patch_name}.patch does not appear to be described in"
|
||||
" the corresponding ReadMe.md")
|
||||
all_good = False
|
||||
patches_ok = False
|
||||
|
||||
for patch_name in readme_contents:
|
||||
if patch_name not in patch_names:
|
||||
if port not in PORTS_MISSING_DESCRIPTIONS:
|
||||
print(f"Ports/{port}/patches/{patch_name}.patch is described in ReadMe.md, "
|
||||
"but does not actually exist")
|
||||
all_good = False
|
||||
patches_ok = False
|
||||
|
||||
if port in PORTS_MISSING_DESCRIPTIONS and patches_ok:
|
||||
print(f"Ports/{port}/patches are all described correctly, but the port is marked "
|
||||
"as MISSING_DESCRIPTIONS, make sure to remove it from the list in lint-ports.py")
|
||||
all_good = False
|
||||
|
||||
return all_good
|
||||
|
||||
|
||||
def try_parse_git_patch(path_to_patch):
|
||||
with open(path_to_patch, 'rb') as f:
|
||||
contents_of_patch = f.read()
|
||||
|
||||
with NamedTemporaryFile('r+b') as message_file:
|
||||
res = subprocess.run(
|
||||
f"git mailinfo {message_file.name} /dev/null",
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
input=contents_of_patch)
|
||||
|
||||
if res.returncode != 0:
|
||||
return None
|
||||
|
||||
message = message_file.read().decode('utf-8')
|
||||
subject = GIT_PATCH_SUBJECT_RE.search(res.stdout.decode("utf-8"))
|
||||
if subject:
|
||||
message = subject.group(1) + "\n" + message
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def check_patches_are_git_patches(patches):
|
||||
"""Ensure that all patches are patches made by (or compatible with) `git format-patch`.
|
||||
|
||||
Args:
|
||||
patches (dict): Dictionary mapping ports to all their patches
|
||||
|
||||
Returns:
|
||||
bool: no errors encountered
|
||||
"""
|
||||
|
||||
all_good = True
|
||||
|
||||
for port, properties in patches.items():
|
||||
for patch_path in properties["patch_files"]:
|
||||
result = try_parse_git_patch(patch_path)
|
||||
if not result:
|
||||
print(f"Ports/{port}/patches: {patch_path.stem} does not appear to be a valid "
|
||||
"git patch.")
|
||||
all_good = False
|
||||
continue
|
||||
return all_good
|
||||
|
||||
|
||||
def check_available_ports(from_table, ports):
|
||||
"""Check AvailablePorts.md for correct properties.
|
||||
|
||||
|
@ -208,6 +475,15 @@ def run():
|
|||
if not check_available_ports(from_table, ports):
|
||||
all_good = False
|
||||
|
||||
patch_list_good, port_properties = get_and_check_port_patch_list(ports.keys())
|
||||
all_good = all_good and patch_list_good
|
||||
|
||||
if not check_descriptions_for_port_patches(port_properties):
|
||||
all_good = False
|
||||
|
||||
if REQUIRE_GIT_PATCHES and not check_patches_are_git_patches(port_properties):
|
||||
all_good = False
|
||||
|
||||
if not all_good:
|
||||
sys.exit(1)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue