1
0
Fork 0

Generate api data on each push (#10609)

* add new qmk generate-api command, to generate a complete set of API data.

* Generate api data and push it to the keyboard repo

* fix typo

* Apply suggestions from code review

Co-authored-by: Joel Challis <git@zvecr.com>

* fixup api workflow

* remove file-changes-action

* use a more mainstream github action

* fix yaml error

* Apply suggestions from code review

Co-authored-by: Erovia <Erovia@users.noreply.github.com>

* more uniform date handling

* make flake8 happy

* Update lib/python/qmk/decorators.py

Co-authored-by: Erovia <Erovia@users.noreply.github.com>

Co-authored-by: Joel Challis <git@zvecr.com>
Co-authored-by: Erovia <Erovia@users.noreply.github.com>
This commit is contained in:
Zach White 2020-10-25 14:48:44 -07:00 committed by GitHub
parent 8ef82c466e
commit 0c42f91f4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 399 additions and 127 deletions

View file

@ -29,33 +29,37 @@ __KEYMAP_GOES_HERE__
"""
def template(keyboard, type='c'):
"""Returns the `keymap.c` or `keymap.json` template for a keyboard.
def template_json(keyboard):
"""Returns a `keymap.json` template for a keyboard.
If a template exists in `keyboards/<keyboard>/templates/keymap.c` that
text will be used instead of `DEFAULT_KEYMAP_C`.
If a template exists in `keyboards/<keyboard>/templates/keymap.json` that
text will be used instead of an empty dictionary.
If a template exists in `keyboards/<keyboard>/templates/keymap.json` that text will be used instead of an empty dictionary.
Args:
keyboard
The keyboard to return a template for.
type
'json' for `keymap.json` and 'c' (or anything else) for `keymap.c`
"""
if type == 'json':
template_file = Path('keyboards/%s/templates/keymap.json' % keyboard)
template = {'keyboard': keyboard}
if template_file.exists():
template.update(json.loads(template_file.read_text()))
template_file = Path('keyboards/%s/templates/keymap.json' % keyboard)
template = {'keyboard': keyboard}
if template_file.exists():
template.update(json.loads(template_file.read_text()))
return template
def template_c(keyboard):
"""Returns a `keymap.c` template for a keyboard.
If a template exists in `keyboards/<keyboard>/templates/keymap.c` that text will be used instead of an empty dictionary.
Args:
keyboard
The keyboard to return a template for.
"""
template_file = Path('keyboards/%s/templates/keymap.c' % keyboard)
if template_file.exists():
template = template_file.read_text()
else:
template_file = Path('keyboards/%s/templates/keymap.c' % keyboard)
if template_file.exists():
template = template_file.read_text()
else:
template = DEFAULT_KEYMAP_C
template = DEFAULT_KEYMAP_C
return template
@ -69,15 +73,65 @@ def _strip_any(keycode):
return keycode
def is_keymap_dir(keymap):
def is_keymap_dir(keymap, c=True, json=True, additional_files=None):
"""Return True if Path object `keymap` has a keymap file inside.
Args:
keymap
A Path() object for the keymap directory you want to check.
c
When true include `keymap.c` keymaps.
json
When true include `keymap.json` keymaps.
additional_files
A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`
"""
for file in ('keymap.c', 'keymap.json'):
files = []
if c:
files.append('keymap.c')
if json:
files.append('keymap.json')
for file in files:
if (keymap / file).is_file():
if additional_files:
for file in additional_files:
if not (keymap / file).is_file():
return False
return True
def generate(keyboard, layout, layers, type='c', keymap=None):
def generate_json(keymap, keyboard, layout, layers):
"""Returns a `keymap.json` for the specified keyboard, layout, and layers.
Args:
keymap
A name for this keymap.
keyboard
The name of the keyboard.
layout
The LAYOUT macro this keymap uses.
layers
An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
"""
new_keymap = template_json(keyboard)
new_keymap['keymap'] = keymap
new_keymap['layout'] = layout
new_keymap['layers'] = layers
return new_keymap
def generate_c(keyboard, layout, layers):
"""Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers.
Args:
@ -89,32 +143,57 @@ def generate(keyboard, layout, layers, type='c', keymap=None):
layers
An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
type
'json' for `keymap.json` and 'c' (or anything else) for `keymap.c`
"""
new_keymap = template(keyboard, type)
if type == 'json':
new_keymap['keymap'] = keymap
new_keymap['layout'] = layout
new_keymap['layers'] = layers
else:
layer_txt = []
for layer_num, layer in enumerate(layers):
if layer_num != 0:
layer_txt[-1] = layer_txt[-1] + ','
new_keymap = template_c(keyboard)
layer_txt = []
for layer_num, layer in enumerate(layers):
if layer_num != 0:
layer_txt[-1] = layer_txt[-1] + ','
layer = map(_strip_any, layer)
layer_keys = ', '.join(layer)
layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
layer = map(_strip_any, layer)
layer_keys = ', '.join(layer)
layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
keymap = '\n'.join(layer_txt)
new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
keymap = '\n'.join(layer_txt)
new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
return new_keymap
def write(keyboard, keymap, layout, layers, type='c'):
def write_file(keymap_filename, keymap_content):
keymap_filename.parent.mkdir(parents=True, exist_ok=True)
keymap_filename.write_text(keymap_content)
cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_filename)
return keymap_filename
def write_json(keyboard, keymap, layout, layers):
"""Generate the `keymap.json` and write it to disk.
Returns the filename written to.
Args:
keyboard
The name of the keyboard
keymap
The name of the keymap
layout
The LAYOUT macro this keymap uses.
layers
An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
"""
keymap_json = generate_json(keyboard, keymap, layout, layers)
keymap_content = json.dumps(keymap_json)
keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json'
return write_file(keymap_file, keymap_content)
def write(keyboard, keymap, layout, layers):
"""Generate the `keymap.c` and write it to disk.
Returns the filename written to.
@ -131,23 +210,11 @@ def write(keyboard, keymap, layout, layers, type='c'):
layers
An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
type
'json' for `keymap.json` and 'c' (or anything else) for `keymap.c`
"""
keymap_content = generate(keyboard, layout, layers, type)
if type == 'json':
keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json'
keymap_content = json.dumps(keymap_content)
else:
keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c'
keymap_content = generate_c(keyboard, layout, layers)
keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c'
keymap_file.parent.mkdir(parents=True, exist_ok=True)
keymap_file.write_text(keymap_content)
cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_file)
return keymap_file
return write_file(keymap_file, keymap_content)
def locate_keymap(keyboard, keymap):
@ -189,38 +256,58 @@ def locate_keymap(keyboard, keymap):
return community_layout / 'keymap.c'
def list_keymaps(keyboard):
""" List the available keymaps for a keyboard.
def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False):
"""List the available keymaps for a keyboard.
Args:
keyboard: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
keyboard
The keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
c
When true include `keymap.c` keymaps.
json
When true include `keymap.json` keymaps.
additional_files
A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`
fullpath
When set to True the full path of the keymap relative to the `qmk_firmware` root will be provided.
Returns:
a set with the names of the available keymaps
a sorted list of valid keymap names.
"""
# parse all the rules.mk files for the keyboard
rules = rules_mk(keyboard)
names = set()
if rules:
# qmk_firmware/keyboards
keyboards_dir = Path('keyboards')
# path to the keyboard's directory
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"
if keymaps_dir.exists():
names = names.union([keymap.name for keymap in keymaps_dir.iterdir() if is_keymap_dir(keymap)])
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
# 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.exists():
names = names.union([keymap.name for keymap in cl_path.iterdir() if is_keymap_dir(keymap)])
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)