merge/upstream-full #105
136
scripts/check-new-xeps.py
Executable file
136
scripts/check-new-xeps.py
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
from itertools import zip_longest
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
# Namespaces in DOAP
|
||||
NS = {
|
||||
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||
"foaf": "http://xmlns.com/foaf/0.1/",
|
||||
"doap": "http://usefulinc.com/ns/doap#",
|
||||
"xmpp": "https://linkmauve.fr/ns/xmpp-doap#",
|
||||
"schema": "https://schema.org/",
|
||||
}
|
||||
|
||||
|
||||
def compare_versions(v1: str, v2: str) -> int:
|
||||
"""
|
||||
Compare two version strings.
|
||||
Returns 1 if v2 > v1, -1 if v1 > v2, 0 if equal.
|
||||
"""
|
||||
def parse_v(v: str) -> List[int]:
|
||||
return [int(x) for x in re.split(r"[^0-9]+", v) if x]
|
||||
|
||||
parts1 = parse_v(v1)
|
||||
parts2 = parse_v(v2)
|
||||
|
||||
for p1, p2 in zip_longest(parts1, parts2, fillvalue=0):
|
||||
if p2 > p1:
|
||||
return 1
|
||||
if p1 > p2:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def find_doap() -> Optional[str]:
|
||||
"""Try to find profanity.doap in current or parent directory."""
|
||||
candidates = ["profanity.doap", "../profanity.doap"]
|
||||
for c in candidates:
|
||||
if os.path.exists(c):
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def get_implemented_xeps(doap_path: str) -> Dict[str, str]:
|
||||
"""Parse DOAP file and return a map of XEP number to version."""
|
||||
implemented_xeps: Dict[str, str] = {}
|
||||
try:
|
||||
tree = ET.parse(doap_path)
|
||||
root = tree.getroot()
|
||||
|
||||
for implements in root.findall(".//doap:implements", NS):
|
||||
supported_xep = implements.find(".//xmpp:SupportedXep", NS)
|
||||
if supported_xep is not None:
|
||||
xep_res = supported_xep.find(".//xmpp:xep", NS)
|
||||
version_elem = supported_xep.find(".//xmpp:version", NS)
|
||||
|
||||
if xep_res is not None and version_elem is not None:
|
||||
resource = xep_res.attrib.get(f"{{{NS['rdf']}}}resource", "")
|
||||
match = re.search(r"xep-(\d+)\.html", resource)
|
||||
if match:
|
||||
xep_num = match.group(1)
|
||||
current_version = version_elem.text.strip() if version_elem.text else ""
|
||||
implemented_xeps[xep_num] = current_version
|
||||
except (ET.ParseError, PermissionError) as e:
|
||||
print(f"Error reading {doap_path}: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
return implemented_xeps
|
||||
|
||||
|
||||
def check_xeps() -> None:
|
||||
"""Main logic for checking XEP updates."""
|
||||
doap_path = find_doap()
|
||||
if not doap_path:
|
||||
print("Error: Could not find DOAP file.")
|
||||
sys.exit(1)
|
||||
|
||||
implemented_xeps = get_implemented_xeps(doap_path)
|
||||
if not implemented_xeps:
|
||||
print("No XEPs found in DOAP file.")
|
||||
return
|
||||
|
||||
print(f"XEPs tracked: {len(implemented_xeps)}")
|
||||
|
||||
try:
|
||||
url = "https://xmpp.org/extensions/xeplist.xml"
|
||||
with urllib.request.urlopen(url, timeout=15) as response:
|
||||
xeplist_xml = response.read()
|
||||
except Exception as e:
|
||||
print(f"Error fetching xeplist.xml: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
xeplist_tree = ET.fromstring(xeplist_xml)
|
||||
except ET.ParseError as e:
|
||||
print(f"Error parsing xeplist.xml: {e}")
|
||||
return
|
||||
|
||||
print(f"\n{'XEP':<10} | {'Name':<35} | {'Ours':<8} | {'Latest':<8}")
|
||||
print("-" * 80)
|
||||
|
||||
updates_found = 0
|
||||
for xep_node in xeplist_tree.findall("xep"):
|
||||
number_node = xep_node.find("number")
|
||||
title_node = xep_node.find("title")
|
||||
last_revision_node = xep_node.find("last-revision")
|
||||
|
||||
if number_node is None or title_node is None or last_revision_node is None:
|
||||
continue
|
||||
|
||||
number = (number_node.text or "0").zfill(4)
|
||||
name = title_node.text or "Unknown"
|
||||
version_node = last_revision_node.find("version")
|
||||
|
||||
if version_node is None or version_node.text is None:
|
||||
continue
|
||||
|
||||
latest_version = version_node.text
|
||||
|
||||
if number in implemented_xeps:
|
||||
old_version = implemented_xeps[number]
|
||||
if compare_versions(old_version, latest_version) > 0:
|
||||
print(f"XEP-{number:<4} | {name[:35]:<35} | {old_version:<8} | {latest_version:<8}")
|
||||
updates_found += 1
|
||||
|
||||
if updates_found == 0:
|
||||
print("All tracked XEPs are up to date.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_xeps()
|
||||
Reference in New Issue
Block a user