From 20474ae2321f0fb456731646b5b2e3090990b8df Mon Sep 17 00:00:00 2001
From: Joel Challis <git@zvecr.com>
Date: Wed, 11 Jan 2023 01:38:35 +0000
Subject: [PATCH] Fix CLI community detection (#19562)

---
 lib/python/qmk/cli/generate/api.py |  6 ++++
 lib/python/qmk/info.py             | 11 +++---
 lib/python/qmk/keymap.py           | 56 +++++++++++++++---------------
 3 files changed, 40 insertions(+), 33 deletions(-)

diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py
index dd4830f543..d662e14e00 100755
--- a/lib/python/qmk/cli/generate/api.py
+++ b/lib/python/qmk/cli/generate/api.py
@@ -10,6 +10,7 @@ from qmk.datetime import current_datetime
 from qmk.info import info_json
 from qmk.json_encoders import InfoJSONEncoder
 from qmk.json_schema import json_load
+from qmk.keymap import list_keymaps
 from qmk.keyboard import find_readme, list_keyboards
 from qmk.keycodes import load_spec, list_versions, list_languages
 
@@ -111,6 +112,11 @@ def generate_api(cli):
     # Generate and write keyboard specific JSON files
     for keyboard_name in keyboard_list:
         kb_all[keyboard_name] = info_json(keyboard_name)
+
+        # Populate the list of JSON keymaps
+        for keymap in list_keymaps(keyboard_name, c=False, fullpath=True):
+            kb_all[keyboard_name]['keymaps'][keymap.name] = {'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap}/keymap.json'}
+
         keyboard_dir = v1_dir / 'keyboards' / keyboard_name
         keyboard_info = keyboard_dir / 'info.json'
         keyboard_readme = keyboard_dir / 'readme.md'
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py
index 86b1e2c063..af3b0f3091 100644
--- a/lib/python/qmk/info.py
+++ b/lib/python/qmk/info.py
@@ -10,7 +10,6 @@ from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS
 from qmk.c_parse import find_layouts, parse_config_h_file, find_led_config
 from qmk.json_schema import deep_update, json_load, validate
 from qmk.keyboard import config_h, rules_mk
-from qmk.keymap import list_keymaps, locate_keymap
 from qmk.commands import parse_configurator_json
 from qmk.makefile import parse_rules_mk_file
 from qmk.math import compute
@@ -99,10 +98,6 @@ def info_json(keyboard):
         'maintainer': 'qmk',
     }
 
-    # Populate the list of JSON keymaps
-    for keymap in list_keymaps(keyboard, c=False, fullpath=True):
-        info_data['keymaps'][keymap.name] = {'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap}/keymap.json'}
-
     # Populate layout data
     layouts, aliases = _search_keyboard_h(keyboard)
 
@@ -872,6 +867,9 @@ def find_info_json(keyboard):
 def keymap_json_config(keyboard, keymap):
     """Extract keymap level config
     """
+    # TODO: resolve keymap.py and info.py circular dependencies
+    from qmk.keymap import locate_keymap
+
     keymap_folder = locate_keymap(keyboard, keymap).parent
 
     km_info_json = parse_configurator_json(keymap_folder / 'keymap.json')
@@ -881,6 +879,9 @@ def keymap_json_config(keyboard, keymap):
 def keymap_json(keyboard, keymap):
     """Generate the info.json data for a specific keymap.
     """
+    # TODO: resolve keymap.py and info.py circular dependencies
+    from qmk.keymap import locate_keymap
+
     keymap_folder = locate_keymap(keyboard, keymap).parent
 
     # Files to scan
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 315af35b73..ce518e379d 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -12,8 +12,9 @@ from pygments.token import Token
 from pygments import lex
 
 import qmk.path
-from qmk.keyboard import find_keyboard_from_dir, rules_mk, keyboard_folder
+from qmk.keyboard import find_keyboard_from_dir, keyboard_folder
 from qmk.errors import CppError
+from qmk.info import info_json
 
 # The `keymap.c` template to use when a keyboard doesn't have its own
 DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
@@ -374,11 +375,11 @@ def locate_keymap(keyboard, keymap):
         return keymap_path
 
     # Check community layouts as a fallback
-    rules = rules_mk(keyboard)
+    info = info_json(keyboard)
 
-    if "LAYOUTS" in rules:
-        for layout in rules["LAYOUTS"].split():
-            community_layout = Path('layouts/community') / layout / keymap
+    for community_parent in Path('layouts').glob('*/'):
+        for layout in info.get("community_layouts", []):
+            community_layout = community_parent / layout / keymap
             if community_layout.exists():
                 if (community_layout / 'keymap.json').exists():
                     return community_layout / 'keymap.json'
@@ -408,37 +409,36 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa
     Returns:
         a sorted list of valid keymap names.
     """
-    # parse all the rules.mk files for the keyboard
-    rules = rules_mk(keyboard)
     names = set()
 
-    if rules is not None:
-        keyboards_dir = Path('keyboards')
-        kb_path = keyboards_dir / keyboard
+    keyboards_dir = Path('keyboards')
+    kb_path = keyboards_dir / keyboard
 
-        # walk up the directory tree until keyboards_dir
-        # and collect all directories' name with keymap.c file in it
-        while kb_path != keyboards_dir:
-            keymaps_dir = kb_path / "keymaps"
+    # walk up the directory tree until keyboards_dir
+    # and collect all directories' name with keymap.c file in it
+    while kb_path != keyboards_dir:
+        keymaps_dir = kb_path / "keymaps"
 
-            if keymaps_dir.is_dir():
-                for keymap in keymaps_dir.iterdir():
+        if keymaps_dir.is_dir():
+            for keymap in keymaps_dir.iterdir():
+                if is_keymap_dir(keymap, c, json, additional_files):
+                    keymap = keymap if fullpath else keymap.name
+                    names.add(keymap)
+
+        kb_path = kb_path.parent
+
+    # Check community layouts as a fallback
+    info = info_json(keyboard)
+
+    for community_parent in Path('layouts').glob('*/'):
+        for layout in info.get("community_layouts", []):
+            cl_path = community_parent / layout
+            if cl_path.is_dir():
+                for keymap in cl_path.iterdir():
                     if is_keymap_dir(keymap, c, json, additional_files):
                         keymap = keymap if fullpath else keymap.name
                         names.add(keymap)
 
-            kb_path = kb_path.parent
-
-        # if community layouts are supported, get them
-        if "LAYOUTS" in rules:
-            for layout in rules["LAYOUTS"].split():
-                cl_path = Path('layouts/community') / layout
-                if cl_path.is_dir():
-                    for keymap in cl_path.iterdir():
-                        if is_keymap_dir(keymap, c, json, additional_files):
-                            keymap = keymap if fullpath else keymap.name
-                            names.add(keymap)
-
     return sorted(names)