2021-02-13 15:51:52 +03:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import os
|
|
|
|
import re
|
2021-04-02 12:26:37 +03:00
|
|
|
import sys
|
2021-04-23 14:53:53 +03:00
|
|
|
import subprocess
|
2021-02-13 15:51:52 +03:00
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
# 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
|
|
|
|
# and "https://www.gnu.org/software/bash/" in group 6.
|
|
|
|
PORT_TABLE_REGEX = re.compile(
|
|
|
|
r'^\| \[`([^`]+)`\]\(([^\)]+)\)([^\|]+) \| ([^\|]+) \| ([^\|]+?) \| ([^\|]+) \|+$', re.MULTILINE
|
|
|
|
)
|
|
|
|
|
|
|
|
# Matches non-abbreviated git hashes
|
|
|
|
GIT_HASH_REGEX = re.compile(r'^[0-9a-f]{40}$')
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
PORT_TABLE_FILE = 'AvailablePorts.md'
|
2021-04-02 12:26:37 +03:00
|
|
|
IGNORE_FILES = {
|
|
|
|
'.gitignore',
|
|
|
|
'.port_include.sh',
|
|
|
|
PORT_TABLE_FILE,
|
|
|
|
'build_all.sh',
|
|
|
|
'build_installed.sh',
|
2021-04-03 06:53:41 +03:00
|
|
|
'README.md',
|
|
|
|
'.hosted_defs.sh'
|
2021-04-02 12:26:37 +03:00
|
|
|
}
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
|
|
|
|
def read_port_table(filename):
|
2021-04-02 12:26:37 +03:00
|
|
|
"""Open a file and find all PORT_TABLE_REGEX matches.
|
|
|
|
|
|
|
|
Args:
|
2021-04-29 22:46:15 +03:00
|
|
|
filename (str): filename
|
2021-04-02 12:26:37 +03:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
set: all PORT_TABLE_REGEX matches
|
|
|
|
"""
|
2021-04-23 14:53:53 +03:00
|
|
|
ports = {}
|
2021-02-13 15:51:52 +03:00
|
|
|
with open(filename, 'r') as fp:
|
2021-04-23 14:53:53 +03:00
|
|
|
matches = PORT_TABLE_REGEX.findall(fp.read())
|
|
|
|
for match in matches:
|
|
|
|
line_len = sum([len(part) for part in match])
|
|
|
|
ports[match[0]] = {
|
|
|
|
"dir_ref": match[1],
|
|
|
|
"name": match[2].strip(),
|
|
|
|
"version": match[4].strip(),
|
|
|
|
"url": match[5].strip(),
|
|
|
|
"line_len": line_len
|
|
|
|
}
|
|
|
|
return ports
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
|
|
|
|
def read_port_dirs():
|
2021-04-02 12:26:37 +03:00
|
|
|
"""Check Ports directory for unexpected files and check each port has a package.sh file.
|
|
|
|
|
|
|
|
Returns:
|
2021-04-16 20:40:23 +03:00
|
|
|
list: all ports (set), no errors encountered (bool)
|
2021-04-02 12:26:37 +03:00
|
|
|
"""
|
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
ports = {}
|
2021-02-13 15:51:52 +03:00
|
|
|
all_good = True
|
|
|
|
for entry in os.listdir():
|
|
|
|
if entry in IGNORE_FILES:
|
|
|
|
continue
|
|
|
|
if not os.path.isdir(entry):
|
2021-04-16 20:40:23 +03:00
|
|
|
print(f"Ports/{entry} is neither a port (not a directory) nor an ignored file?!")
|
2021-02-13 15:51:52 +03:00
|
|
|
all_good = False
|
|
|
|
continue
|
|
|
|
if not os.path.exists(entry + '/package.sh'):
|
2021-04-16 20:40:23 +03:00
|
|
|
print(f"Ports/{entry}/ is missing its package.sh?!")
|
2021-02-13 15:51:52 +03:00
|
|
|
all_good = False
|
|
|
|
continue
|
2021-04-23 14:53:53 +03:00
|
|
|
ports[entry] = get_port_properties(entry)
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
return ports, all_good
|
|
|
|
|
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
PORT_PROPERTIES = ('port', 'version', 'files', 'auth_type')
|
|
|
|
|
|
|
|
|
|
|
|
def get_port_properties(port):
|
|
|
|
"""Retrieves common port properties from its package.sh file.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
dict: keys are values from PORT_PROPERTIES, values are from the package.sh file
|
|
|
|
"""
|
|
|
|
|
|
|
|
props = {}
|
|
|
|
for prop in PORT_PROPERTIES:
|
|
|
|
res = subprocess.run(f"cd {port}; exec ./package.sh showproperty {prop}", shell=True, capture_output=True)
|
|
|
|
if res.returncode == 0:
|
|
|
|
props[prop] = res.stdout.decode('utf-8').strip()
|
|
|
|
else:
|
|
|
|
print((
|
|
|
|
f'Executing "./package.sh showproperty {prop}" script for port {port} failed with '
|
|
|
|
f'exit code {res.returncode}, output from stderr:\n{res.stderr.decode("utf-8").strip()}'
|
|
|
|
))
|
|
|
|
props[prop] = ''
|
|
|
|
return props
|
|
|
|
|
|
|
|
|
2021-04-16 20:40:23 +03:00
|
|
|
def check_package_files(ports):
|
|
|
|
"""Check port package.sh file for required properties.
|
|
|
|
|
|
|
|
Args:
|
2021-04-23 14:53:53 +03:00
|
|
|
ports (list): List of all ports to check
|
2021-04-16 20:40:23 +03:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: no errors encountered
|
|
|
|
"""
|
|
|
|
|
|
|
|
all_good = True
|
|
|
|
for port in ports:
|
|
|
|
package_file = f"{port}/package.sh"
|
|
|
|
if not os.path.exists(package_file):
|
|
|
|
continue
|
2021-04-23 14:53:53 +03:00
|
|
|
|
|
|
|
props = get_port_properties(port)
|
2021-04-25 12:54:14 +03:00
|
|
|
|
|
|
|
if not props['auth_type'] in ('sha256', 'sig', ''):
|
|
|
|
print(f"Ports/{port} uses invalid signature algorithm '{props['auth_type']}' for 'auth_type'")
|
|
|
|
all_good = False
|
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
for prop in PORT_PROPERTIES:
|
|
|
|
if prop == 'auth_type' and re.match('^https://github.com/SerenityOS/', props["files"]):
|
|
|
|
continue
|
|
|
|
if props[prop] == '':
|
|
|
|
print(f"Ports/{port} is missing required property '{prop}'")
|
|
|
|
all_good = False
|
|
|
|
|
|
|
|
return all_good
|
|
|
|
|
|
|
|
|
|
|
|
def check_available_ports(from_table, ports):
|
|
|
|
"""Check AvailablePorts.md for correct properties.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
from_table (dict): Ports table from AvailablePorts.md
|
|
|
|
ports (dict): Dictionary with port properties from package.sh
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: no errors encountered
|
|
|
|
"""
|
|
|
|
|
|
|
|
all_good = True
|
|
|
|
|
|
|
|
previous_line_len = None
|
|
|
|
|
|
|
|
for port in from_table.keys():
|
|
|
|
if previous_line_len is None:
|
|
|
|
previous_line_len = from_table[port]["line_len"]
|
|
|
|
if previous_line_len != from_table[port]["line_len"]:
|
|
|
|
print(f"Table row for port {port} is improperly aligned with other rows.")
|
|
|
|
all_good = False
|
|
|
|
else:
|
|
|
|
previous_line_len = from_table[port]["line_len"]
|
|
|
|
|
|
|
|
actual_ref = from_table[port]["dir_ref"]
|
|
|
|
expected_ref = f"{port}/"
|
|
|
|
if actual_ref != expected_ref:
|
|
|
|
print((
|
|
|
|
f'Directory link target in AvailablePorts.md for port {port} is '
|
|
|
|
f'incorrect, expected "{expected_ref}", found "{actual_ref}"'
|
|
|
|
))
|
|
|
|
all_good = False
|
|
|
|
|
|
|
|
actual_version = from_table[port]["version"]
|
|
|
|
expected_version = ports[port]["version"]
|
|
|
|
if GIT_HASH_REGEX.match(expected_version):
|
|
|
|
expected_version = expected_version[0:7]
|
|
|
|
if expected_version == "git":
|
|
|
|
expected_version = ""
|
|
|
|
if actual_version != expected_version:
|
|
|
|
print((
|
|
|
|
f'Version in AvailablePorts.md for port {port} is incorrect, '
|
|
|
|
f'expected "{expected_version}", found "{actual_version}"'
|
|
|
|
))
|
|
|
|
all_good = False
|
2021-04-16 20:40:23 +03:00
|
|
|
|
|
|
|
return all_good
|
|
|
|
|
|
|
|
|
2021-02-13 15:51:52 +03:00
|
|
|
def run():
|
2021-04-16 20:40:23 +03:00
|
|
|
"""Check Ports directory and package files for errors."""
|
2021-04-02 12:26:37 +03:00
|
|
|
|
2021-02-13 15:51:52 +03:00
|
|
|
from_table = read_port_table(PORT_TABLE_FILE)
|
2021-04-16 20:40:23 +03:00
|
|
|
ports, all_good = read_port_dirs()
|
2021-02-13 15:51:52 +03:00
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
from_table_set = set(from_table.keys())
|
|
|
|
ports_set = set(ports.keys())
|
|
|
|
|
|
|
|
if from_table_set - ports_set:
|
2021-02-13 15:51:52 +03:00
|
|
|
all_good = False
|
|
|
|
print('AvailablePorts.md lists ports that do not appear in the file system:')
|
2021-05-07 22:08:45 +03:00
|
|
|
for port in sorted(from_table_set - ports_set):
|
2021-04-16 20:40:23 +03:00
|
|
|
print(f" {port}")
|
2021-02-13 15:51:52 +03:00
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
if ports_set - from_table_set:
|
2021-02-13 15:51:52 +03:00
|
|
|
all_good = False
|
|
|
|
print('AvailablePorts.md is missing the following ports:')
|
2021-04-23 14:53:53 +03:00
|
|
|
for port in sorted(ports_set - from_table_set):
|
2021-04-16 20:40:23 +03:00
|
|
|
print(f" {port}")
|
|
|
|
|
2021-04-23 14:53:53 +03:00
|
|
|
if not check_package_files(ports.keys()):
|
|
|
|
all_good = False
|
|
|
|
|
|
|
|
if not check_available_ports(from_table, ports):
|
2021-04-16 20:40:23 +03:00
|
|
|
all_good = False
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
if not all_good:
|
2021-04-02 12:26:37 +03:00
|
|
|
sys.exit(1)
|
2021-02-13 15:51:52 +03:00
|
|
|
|
|
|
|
print('No issues found.')
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2021-04-16 20:40:23 +03:00
|
|
|
os.chdir(f"{os.path.dirname(__file__)}/../Ports")
|
2021-02-13 15:51:52 +03:00
|
|
|
run()
|