agent: Update specs downloader and document commit style - Make downloader.py dynamic to find latest spec versions automatically - Remove leading whitespaces from empty lines in downloader.py - Add commit message guidelines to GEMINI.md Test: Verified downloader script execution and file formatting Change-Id: I687ca9fb55bebae97b0a679e1dbc1101a4bc6a43 Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/3241
diff --git a/.agents/skills/le_profiles_specifications/scripts/downloader.py b/.agents/skills/le_profiles_specifications/scripts/downloader.py index a50acec..d9ed4ef 100644 --- a/.agents/skills/le_profiles_specifications/scripts/downloader.py +++ b/.agents/skills/le_profiles_specifications/scripts/downloader.py
@@ -1,4 +1,5 @@ import os +import re from urllib.request import urlopen, Request from urllib.error import URLError from bs4 import BeautifulSoup @@ -8,71 +9,66 @@ "specifications", ) -# List of specifications to download -SPECS = [ - { - "name": "BAP_v1.0.2", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/BAP_v1.0.2/out/en/index-en.html", - }, - { - "name": "CAP_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/CAP_v1.0.1/out/en/index-en.html", - }, - { - "name": "TMAP_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/TMAP_v1.0.1/out/en/index-en.html", - }, - { - "name": "HAP_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/HAP_v1.0.1/out/en/index-en.html", - }, - { - "name": "PBP_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/PBP_v1.0.1/out/en/index-en.html", - }, - { - "name": "MCP_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/28982-MCP-html5/out/en/index-en.html", - }, - { - "name": "CCP_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/26587-CCP-html5/out/en/index-en.html", - }, - { - "name": "MICP_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/21264-MICP-html5/out/en/index-en.html", - }, - { - "name": "VCP_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/76878-VCP-html5/out/en/index-en.html", - }, - { - "name": "ASCS_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/ASCS_v1.0.1/out/en/index-en.html", - }, - { - "name": "PACS_v1.0.2", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/PACS_v1.0.2/out/en/index-en.html", - }, - { - "name": "CSIS_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/28085-CSIS-html5/out/en/index-en.html", - }, - { - "name": "CSIP_v1.0", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/27407-CSIP-html5/out/en/index-en.html", - }, - { - "name": "BASS_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/BASS_v1.0.1/out/en/index-en.html", - }, - { - "name": "AICS_v1.0.1", - "url": "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/AICS_v1.0.1/out/en/index-en.html", - }, -] +ROOT_URL = "https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/" -def download_spec(name, url): +# Targets to download +TARGETS = ['BAP', 'CAP', 'TMAP', 'HAP', 'PBP', 'MCP', 'CCP', 'MICP', 'VCP', 'ASCS', 'PACS', 'CSIS', 'CSIP', 'BASS', 'AICS'] + +def get_all_index_links(): + print(f"Fetching directory listing from {ROOT_URL}...") + headers = {'User-Agent': 'Mozilla/5.0'} + try: + req = Request(ROOT_URL, headers=headers) + with urlopen(req, timeout=30) as response: + html_content = response.read() + soup = BeautifulSoup(html_content, 'html.parser') + links = [] + for a in soup.find_all('a'): + href = a.get('href', '') + if "index-en.html" in href: + links.append(href) + return links + except URLError as e: + print(f"Error fetching root directory: {e}") + return [] + +def match_target(path, acronym): + pattern = re.compile(rf"(?:^|/|_|-)({acronym})(?:[._-]|html5)", re.IGNORECASE) + return bool(pattern.search(path)) + +def parse_version(path, acronym): + pattern = re.compile(rf"{acronym}_v(\d+(?:[.-]\d+)*)", re.IGNORECASE) + match = pattern.search(path) + if match: + return match.group(1).replace('-', '.') + return "0.0" + +def get_latest_link(links, acronym): + target_links = [l for l in links if match_target(l, acronym)] + if not target_links: + return None + + timestamp_pattern = re.compile(r"_(\d{10})/") + + def sort_key(path): + version_str = parse_version(path, acronym) + version_parts = [] + for x in version_str.split('.'): + try: + version_parts.append(int(x)) + except ValueError: + version_parts.append(0) + + timestamp_match = timestamp_pattern.search(path) + timestamp = int(timestamp_match.group(1)) if timestamp_match else 0 + + return (version_parts, timestamp) + + sorted_links = sorted(target_links, key=sort_key, reverse=True) + return sorted_links[0] + +def download_spec(name, relative_url): + url = ROOT_URL + relative_url print(f"Downloading {name} from {url}...") headers = {'User-Agent': 'Mozilla/5.0'} try: @@ -104,8 +100,24 @@ print(f"An error occurred: {e}") def main(): - for spec in SPECS: - download_spec(spec["name"], spec["url"]) + links = get_all_index_links() + if not links: + print("No links found.") + return + + for target in TARGETS: + latest_link = get_latest_link(links, target) + if latest_link: + version = parse_version(latest_link, target) + name = f"{target}_v{version}" if version != "0.0" else target + if version == "0.0": + match = re.search(rf"(\d+)-{target}-html5", latest_link, re.IGNORECASE) + if match: + name = f"{target}_ID_{match.group(1)}" + + download_spec(name, latest_link) + else: + print(f"No link found for {target}") if __name__ == "__main__": main()
diff --git a/GEMINI.md b/GEMINI.md index 3b84746..6628610 100644 --- a/GEMINI.md +++ b/GEMINI.md
@@ -1,3 +1,8 @@ # Gemini Assistant Guidelines - Bluetooth Workspace -See [README.md](./README.md) for project structure and description, and general guidelines for development. \ No newline at end of file +See [README.md](./README.md) for project structure and description, and general guidelines for development. + +## Guidelines + +### Commit Messages +When asked to write or suggest a commit message, strictly follow the rules in [Commit message style](./docs/commit_messages.md). \ No newline at end of file