Skip to content

Commit 0c42f91

Browse files
skullydazedzvecrErovia
authored
Generate api data on each push (qmk#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 <[email protected]> * 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 <[email protected]> * more uniform date handling * make flake8 happy * Update lib/python/qmk/decorators.py Co-authored-by: Erovia <[email protected]> Co-authored-by: Joel Challis <[email protected]> Co-authored-by: Erovia <[email protected]>
1 parent 8ef82c4 commit 0c42f91

File tree

18 files changed

+397
-125
lines changed

18 files changed

+397
-125
lines changed

.github/workflows/api.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Update API Data
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- 'keyboards/**'
9+
- 'layouts/community/**'
10+
11+
jobs:
12+
api_data:
13+
runs-on: ubuntu-latest
14+
container: qmkfm/base_container
15+
16+
steps:
17+
- uses: actions/checkout@v2
18+
with:
19+
fetch-depth: 1
20+
persist-credentials: false
21+
22+
- name: Generate API Data
23+
run: qmk generate-api
24+
25+
- name: Upload API Data
26+
uses: JamesIves/[email protected]
27+
with:
28+
ACCESS_TOKEN: ${{ secrets.API_TOKEN_GITHUB }}
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
BRANCH: main
31+
FOLDER: api_data/v1
32+
CLEAN: true
33+
GIT_CONFIG_EMAIL: [email protected]
34+
REPOSITORY_NAME: qmk/qmk_keyboards
35+
TARGET_FOLDER: v1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*.swp
1717
tags
1818
*~
19+
api_data/v1
1920
build/
2021
.build/
2122
*.bak

api_data/_config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
theme: jekyll-theme-cayman

api_data/readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# QMK Keyboard Metadata
2+
3+
This directory contains machine parsable data about keyboards supported by QMK. The latest version is always available online at <https://keyboards.qmk.fm>.
4+
5+
Do not edit anything here by hand. It is generated with the `qmk generate-api` command.

lib/python/qmk/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from . import docs
1414
from . import doctor
1515
from . import flash
16+
from . import generate
1617
from . import hello
1718
from . import info
1819
from . import json

lib/python/qmk/cli/c2json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def c2json(cli):
4444

4545
# Generate the keymap.json
4646
try:
47-
keymap_json = qmk.keymap.generate(keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers'], type='json', keymap=keymap_json['keymap'])
47+
keymap_json = qmk.keymap.generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers'])
4848
except KeyError:
4949
cli.log.error('Something went wrong. Try to use --no-cpp.')
5050
sys.exit(1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import api

lib/python/qmk/cli/generate/api.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""This script automates the generation of the QMK API data.
2+
"""
3+
from pathlib import Path
4+
from shutil import copyfile
5+
import json
6+
7+
from milc import cli
8+
9+
from qmk.datetime import current_datetime
10+
from qmk.info import info_json
11+
from qmk.keyboard import list_keyboards
12+
13+
14+
@cli.subcommand('Creates a new keymap for the keyboard of your choosing', hidden=False if cli.config.user.developer else True)
15+
def generate_api(cli):
16+
"""Generates the QMK API data.
17+
"""
18+
api_data_dir = Path('api_data')
19+
v1_dir = api_data_dir / 'v1'
20+
keyboard_list = v1_dir / 'keyboard_list.json'
21+
keyboard_all = v1_dir / 'keyboards.json'
22+
usb_file = v1_dir / 'usb.json'
23+
24+
if not api_data_dir.exists():
25+
api_data_dir.mkdir()
26+
27+
kb_all = {'last_updated': current_datetime(), 'keyboards': {}}
28+
usb_list = {'last_updated': current_datetime(), 'devices': {}}
29+
30+
# Generate and write keyboard specific JSON files
31+
for keyboard_name in list_keyboards():
32+
kb_all['keyboards'][keyboard_name] = info_json(keyboard_name)
33+
keyboard_dir = v1_dir / 'keyboards' / keyboard_name
34+
keyboard_info = keyboard_dir / 'info.json'
35+
keyboard_readme = keyboard_dir / 'readme.md'
36+
keyboard_readme_src = Path('keyboards') / keyboard_name / 'readme.md'
37+
38+
keyboard_dir.mkdir(parents=True, exist_ok=True)
39+
keyboard_info.write_text(json.dumps(kb_all['keyboards'][keyboard_name]))
40+
41+
if keyboard_readme_src.exists():
42+
copyfile(keyboard_readme_src, keyboard_readme)
43+
44+
if 'usb' in kb_all['keyboards'][keyboard_name]:
45+
usb = kb_all['keyboards'][keyboard_name]['usb']
46+
47+
if usb['vid'] not in usb_list['devices']:
48+
usb_list['devices'][usb['vid']] = {}
49+
50+
if usb['pid'] not in usb_list['devices'][usb['vid']]:
51+
usb_list['devices'][usb['vid']][usb['pid']] = {}
52+
53+
usb_list['devices'][usb['vid']][usb['pid']][keyboard_name] = usb
54+
55+
# Write the global JSON files
56+
keyboard_list.write_text(json.dumps({'last_updated': current_datetime(), 'keyboards': sorted(kb_all['keyboards'])}))
57+
keyboard_all.write_text(json.dumps(kb_all))
58+
usb_file.write_text(json.dumps(usb_list))

lib/python/qmk/cli/info.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
COL_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijilmnopqrstuvwxyz'
1717

1818

19-
def show_keymap(info_json, title_caps=True):
19+
def show_keymap(kb_info_json, title_caps=True):
2020
"""Render the keymap in ascii art.
2121
"""
2222
keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)
@@ -36,7 +36,7 @@ def show_keymap(info_json, title_caps=True):
3636
else:
3737
cli.echo('{fg_cyan}layer_%s{fg_reset}:', layer_num)
3838

39-
print(render_layout(info_json['layouts'][layout_name]['layout'], layer))
39+
print(render_layout(kb_info_json['layouts'][layout_name]['layout'], layer))
4040

4141

4242
def show_layouts(kb_info_json, title_caps=True):
@@ -48,10 +48,10 @@ def show_layouts(kb_info_json, title_caps=True):
4848
print(layout_art) # Avoid passing dirty data to cli.echo()
4949

5050

51-
def show_matrix(info_json, title_caps=True):
51+
def show_matrix(kb_info_json, title_caps=True):
5252
"""Render the layout with matrix labels in ascii art.
5353
"""
54-
for layout_name, layout in info_json['layouts'].items():
54+
for layout_name, layout in kb_info_json['layouts'].items():
5555
# Build our label list
5656
labels = []
5757
for key in layout['layout']:
@@ -69,54 +69,54 @@ def show_matrix(info_json, title_caps=True):
6969
else:
7070
cli.echo('{fg_blue}matrix_%s{fg_reset}:', layout_name)
7171

72-
print(render_layout(info_json['layouts'][layout_name]['layout'], labels))
72+
print(render_layout(kb_info_json['layouts'][layout_name]['layout'], labels))
7373

7474

75-
def print_friendly_output(info_json):
75+
def print_friendly_output(kb_info_json):
7676
"""Print the info.json in a friendly text format.
7777
"""
78-
cli.echo('{fg_blue}Keyboard Name{fg_reset}: %s', info_json.get('keyboard_name', 'Unknown'))
79-
cli.echo('{fg_blue}Manufacturer{fg_reset}: %s', info_json.get('manufacturer', 'Unknown'))
80-
if 'url' in info_json:
81-
cli.echo('{fg_blue}Website{fg_reset}: %s', info_json.get('url', ''))
82-
if info_json.get('maintainer', 'qmk') == 'qmk':
78+
cli.echo('{fg_blue}Keyboard Name{fg_reset}: %s', kb_info_json.get('keyboard_name', 'Unknown'))
79+
cli.echo('{fg_blue}Manufacturer{fg_reset}: %s', kb_info_json.get('manufacturer', 'Unknown'))
80+
if 'url' in kb_info_json:
81+
cli.echo('{fg_blue}Website{fg_reset}: %s', kb_info_json.get('url', ''))
82+
if kb_info_json.get('maintainer', 'qmk') == 'qmk':
8383
cli.echo('{fg_blue}Maintainer{fg_reset}: QMK Community')
8484
else:
85-
cli.echo('{fg_blue}Maintainer{fg_reset}: %s', info_json['maintainer'])
86-
cli.echo('{fg_blue}Keyboard Folder{fg_reset}: %s', info_json.get('keyboard_folder', 'Unknown'))
87-
cli.echo('{fg_blue}Layouts{fg_reset}: %s', ', '.join(sorted(info_json['layouts'].keys())))
88-
if 'width' in info_json and 'height' in info_json:
89-
cli.echo('{fg_blue}Size{fg_reset}: %s x %s' % (info_json['width'], info_json['height']))
90-
cli.echo('{fg_blue}Processor{fg_reset}: %s', info_json.get('processor', 'Unknown'))
91-
cli.echo('{fg_blue}Bootloader{fg_reset}: %s', info_json.get('bootloader', 'Unknown'))
85+
cli.echo('{fg_blue}Maintainer{fg_reset}: %s', kb_info_json['maintainer'])
86+
cli.echo('{fg_blue}Keyboard Folder{fg_reset}: %s', kb_info_json.get('keyboard_folder', 'Unknown'))
87+
cli.echo('{fg_blue}Layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
88+
if 'width' in kb_info_json and 'height' in kb_info_json:
89+
cli.echo('{fg_blue}Size{fg_reset}: %s x %s' % (kb_info_json['width'], kb_info_json['height']))
90+
cli.echo('{fg_blue}Processor{fg_reset}: %s', kb_info_json.get('processor', 'Unknown'))
91+
cli.echo('{fg_blue}Bootloader{fg_reset}: %s', kb_info_json.get('bootloader', 'Unknown'))
9292

9393
if cli.config.info.layouts:
94-
show_layouts(info_json, True)
94+
show_layouts(kb_info_json, True)
9595

9696
if cli.config.info.matrix:
97-
show_matrix(info_json, True)
97+
show_matrix(kb_info_json, True)
9898

9999
if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
100-
show_keymap(info_json, True)
100+
show_keymap(kb_info_json, True)
101101

102102

103-
def print_text_output(info_json):
103+
def print_text_output(kb_info_json):
104104
"""Print the info.json in a plain text format.
105105
"""
106-
for key in sorted(info_json):
106+
for key in sorted(kb_info_json):
107107
if key == 'layouts':
108-
cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(info_json['layouts'].keys())))
108+
cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
109109
else:
110-
cli.echo('{fg_blue}%s{fg_reset}: %s', key, info_json[key])
110+
cli.echo('{fg_blue}%s{fg_reset}: %s', key, kb_info_json[key])
111111

112112
if cli.config.info.layouts:
113-
show_layouts(info_json, False)
113+
show_layouts(kb_info_json, False)
114114

115115
if cli.config.info.matrix:
116-
show_matrix(info_json, False)
116+
show_matrix(kb_info_json, False)
117117

118118
if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':
119-
show_keymap(info_json, False)
119+
show_keymap(kb_info_json, False)
120120

121121

122122
@cli.argument('-kb', '--keyboard', help='Keyboard to show info for.')

lib/python/qmk/cli/json2c.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def json2c(cli):
3838
user_keymap = json.load(fd)
3939

4040
# Generate the keymap
41-
keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
41+
keymap_c = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
4242

4343
if cli.args.output:
4444
cli.args.output.parent.mkdir(parents=True, exist_ok=True)

0 commit comments

Comments
 (0)