~ruther/qmk_firmware

d888ac17ea35b6093d0a6717462ae2c18519ec41 — QMK Bot 4 years ago 5b8f2ec + ed84a4e
Merge remote-tracking branch 'origin/master' into develop
M docs/_summary.md => docs/_summary.md +1 -0
@@ 19,6 19,7 @@
  * [Overview](newbs_building_firmware_configurator.md)
  * [Step by Step](configurator_step_by_step.md)
  * [Troubleshooting](configurator_troubleshooting.md)
  * [Architecture](configurator_architecture.md)
  * QMK API
    * [Overview](api_overview.md)
    * [API Documentation](api_docs.md)

A docs/configurator_architecture.md => docs/configurator_architecture.md +61 -0
@@ 0,0 1,61 @@
# QMK Configurator Architecture

This page describes the web architecture behind QMK Configurator at a high level. If you are interested in the architecture of the QMK Configurator code itself you should start at the [qmk_configurator](https://github.com/qmk/qmk_configurator) repository.

# Overview

![QMK Configurator Architecture Diagram](configurator_diagram.svg)

# Detailed Description

QMK Configurator is a [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application) that allows users to create custom keymaps for their QMK-compatible keyboard. They can export JSON representation of their keymaps and compile firmware binaries that can be flashed to their keyboard using a tool like [QMK Toolbox](https://github.com/qmk/qmk_toolbox).

Configurator gets metadata about keyboards from the Keyboard Metadata store and submits compile requests to the QMK API. The results of those compile requests will be made available on [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces/), an S3-compatible data store.

## Configurator Frontend

Address: <https://config.qmk.fm>

The [Configurator Frontend](https://config.qmk.fm) is compiled into a set of static files that are served by Github Pages. This action happens every time a commit is pushed to the [qmk_configurator `master`](https://github.com/qmk/qmk_configurator) branch. You can view the status of these jobs on the [qmk_configurator actions tab](https://github.com/qmk/qmk_configurator/actions/workflows/build.yml).

## Keyboard Metadata

Address: <https://keyboards.qmk.fm>

The Keyboard Metadata is generated every time a keyboard in [qmk_firmware](https://github.com/qmk/qmk_firmware) changes. The resulting JSON files are uploaded to Spaces and used by Configurator to generate UI for each keyboard. You can view the status of this job on the [qmk_firmware actions tab](https://github.com/qmk/qmk_firmware/actions/workflows/api.yml). If you are a QMK Collaborator you can manually run this job using the `workflow_dispatch` event trigger.

## QMK API

Address: <http://api.qmk.fm>

The QMK API accepts `keymap.json` files for compilation. These are the same files you can use directly with `qmk compile` and `qmk flash`. When a `keymap.json` is submitted the browser will poll the status of the job periodically (every 2 seconds or longer, preferably) until the job has completed. The final status JSON will contain pointers to source and binary downloads for the keymap.

QMK API always presents the source and binary downloads side-by-side to comply with the GPL.

There are 3 non-error status responses from the API-

1. Compile Job Queued
2. Compile Job Running
3. Compile Job Finished

### Compile Job Queued

This status indicates that the job has not yet been picked up by a [QMK Compiler](#qmk-compiler) node. Configurator shows this status as "Waiting for an oven".

### Compile Job Running

This status indicates that the job has started compiling. Configurator shows this status as "Baking".

### Compile Job Finished

This status indicates that the job has completed. There will be keys in the status JSON for source and binary downloads.

## Redis/RQ

QMK API uses RQ to distribute jobs to the available [QMK Compiler](#qmk-compiler) nodes. When a `keymap.json` is received it's put into the RQ queue, where a `qmk_compiler` node will pick it up from.

## QMK Compiler

[QMK Compiler](https://github.com/qmk/qmk_compiler) is what actually performs the compilation of the `keymap.json`. It does so by checking out the requested `qmk_firmware` branch, running `qmk compile keymap.json`, and then uploading the resulting source and binary to Digital Ocean Spaces. 

When users download their source/binary, API will redirect them to the authenticated Spaces download URL.

A docs/configurator_diagram.drawio => docs/configurator_diagram.drawio +1 -0
@@ 0,0 1,1 @@
<mxfile host="Electron" modified="2021-08-09T19:46:29.036Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="PQ2r34UrZa0TfW4Fw0EV" version="14.6.13" type="device"><diagram id="NEtccoSKIy4HskWlhJpu" name="Page-1">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</diagram></mxfile>
\ No newline at end of file

A docs/configurator_diagram.svg => docs/configurator_diagram.svg +3 -0
@@ 0,0 1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1261px" height="991px" viewBox="-0.5 -0.5 1261 991" style="background-color: rgb(255, 255, 255);"><defs/><g><path d="M 885.2 40 L 1060 40" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><image x="804.7" y="-0.5" width="80" height="80" xlink:href="data:image/png;base64,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"/><path d="M 845.33 302.4 C 815.12 302.4 794 277.79 794 251.59 C 794 220.27 819.22 200 845.71 200 C 871.22 200 896.4 220.93 896.4 251.18 C 896.4 278.28 874.84 302.4 845.33 302.4 Z" fill="#0d2636" stroke="none" pointer-events="all"/><path d="M 834.25 291.19 C 834.25 293.4 832.57 294.32 830.06 293.46 C 814.91 288.15 800.34 271.75 800.34 251.27 C 800.34 223.57 824.03 206.1 844.57 206.1 C 871.22 206.1 890.18 227.94 890.18 251 C 890.18 270.03 877.94 287.54 859.59 293.69 C 857.52 294.24 856.23 293.13 856.23 291.31 L 856.23 277.98 C 856.23 275.4 855.18 272.65 853.24 270.74 C 860.69 269.89 865.21 268.02 868.67 264.46 C 872.07 261.08 873.6 256.13 873.88 250.01 C 874.07 245.15 872.77 240.41 869.25 236.8 C 870.48 233.82 870.69 229.86 868.83 224.97 C 865.14 224.69 860.94 226.9 856.64 229.56 C 849.08 227.59 841.52 227.33 833.96 229.67 C 830.58 227.47 827.51 225 821.65 224.97 C 820.1 229.29 819.86 233.23 821.19 236.72 C 817 241.37 816.52 245.93 816.6 250.47 C 817.06 259.25 820.27 263.68 823.83 266.33 C 826.79 268.52 830.94 269.9 837.27 270.83 C 835.57 272.51 834.62 274.51 834.46 276.86 C 830.74 278.56 825.42 279.41 821.78 274.25 C 820.15 271.66 817.93 268.73 813.72 268.74 C 813.03 268.72 812.35 268.98 812.25 269.28 C 812.15 269.61 812.54 270.3 813.01 270.57 C 816.7 272.92 817.29 274.23 818.8 277.44 C 820.2 281.19 822.62 282.63 825.24 283.64 C 827.92 284.59 832.25 284.32 834.25 283.64 Z" fill="#ffffff" stroke="none" pointer-events="all"/><path d="M 845.2 193.63 L 845.2 86.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 845.2 198.88 L 841.7 191.88 L 845.2 193.63 L 848.7 191.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 845.2 81.12 L 848.7 88.12 L 845.2 86.37 L 841.7 88.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="1060" y="10" width="200" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 40px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: white; white-space: normal; word-wrap: normal; ">Clients Supported:<br />Chrome, Firefox<br />Desktop Only</div></div></div></foreignObject><text x="1160" y="44" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Clients Supported:...</text></switch></g><rect x="1060" y="206.2" width="200" height="90" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 251px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><span>https://config.qmk.fm</span><br /><span>Single Page Site<br /></span>JavaScript/VUE<br />Source: qmk/qmk_configurator<br /><span>Host: Github Pages</span></div></div></div></foreignObject><text x="1160" y="255" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">https://config.qmk.fm...</text></switch></g><path d="M 896.4 251.2 L 1060 251.2" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 880 457.9 L 1060 457.9" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 833.22 495.8 C 828.16 495.8 821.72 491.64 821.72 483.75 L 821.72 475.34 L 813.15 475.34 C 811.65 475.34 810 474.49 810.03 471.92 L 810.03 455.36 C 810.03 453.81 810.93 452.17 812.91 452.12 L 821.72 452.12 L 821.72 431.37 C 821.72 425.69 826.62 420 833.69 420 L 856.49 420 C 858.53 420 861.18 421.45 863.72 423.93 L 876.39 436.67 C 879.49 440.04 880 441.77 880 443.93 L 880 484.09 C 880 490.1 875.34 495.8 867.72 495.8 Z" fill="#000000" stroke="none" pointer-events="all"/><path d="M 850.47 469.58 L 850.47 457.06 L 854.43 457.06 L 858.82 463.99 L 858.82 457.06 L 861.89 457.06 L 861.89 469.58 L 858.67 469.58 L 853.58 461.29 L 853.58 469.58 Z M 844.7 463.4 C 844.69 461.89 844.28 459.4 841.65 459.33 C 839.7 459.3 838.71 461.33 838.71 463.26 C 838.77 466.2 840.13 467.37 841.74 467.37 C 843.53 467.33 844.68 465.87 844.7 463.4 Z M 835.26 462.55 C 835.34 459.28 837.91 457.1 840.4 456.87 C 843.13 456.63 844.5 457.13 845.74 457.91 C 847.41 459.11 848.53 461.32 848.06 464.79 C 847.61 467.23 846.07 469.6 842.36 469.85 C 841 469.92 838.36 469.99 836.56 467.58 C 835.73 466.38 835.22 465.51 835.26 462.55 Z M 823.19 465.77 L 823.65 465.72 C 825.75 467.29 827.29 467.4 828.78 467.35 C 829.66 467.23 830.41 467.01 830.47 466.24 C 830.51 465.69 829.93 465.31 829.36 465.17 C 827.6 464.71 826.16 464.86 824.34 463.46 C 822.89 462.1 822.96 460.46 823.66 459.16 C 824.9 457.24 827.05 456.94 828.36 456.83 C 830.57 456.81 832.33 457.19 833.44 457.73 L 833.43 460.67 L 832.94 460.71 C 831.93 459.92 830.82 459.41 829.27 459.32 C 828.41 459.31 827.38 459.3 826.76 460 C 826.43 461.17 827.29 461.25 828.04 461.42 C 830.02 461.87 831.13 461.99 832.65 462.95 C 834.58 464.43 833.97 466.96 832.96 468.1 C 831.63 469.39 830.35 469.86 827.92 469.85 C 826.52 469.83 824.81 469.63 823.17 468.83 Z M 813.29 466.83 C 814.07 467.09 814.63 467.45 816.44 467.23 C 817.09 467.05 817.76 466.56 817.76 465.57 L 817.76 459.51 L 814.91 459.51 L 814.91 457.06 L 821.13 457.06 L 821.13 466.31 C 820.87 468.24 819.78 469.26 818.09 469.59 C 816.91 469.84 815.53 469.98 813.29 469.57 Z M 827.52 475.34 L 862.48 475.34 C 864.3 475.34 865.44 473.93 865.44 472.17 L 865.44 455.09 C 865.44 453.39 864.22 452.12 862.32 452.12 L 827.52 452.12 L 827.52 431.76 C 827.52 427.61 830.96 425.79 833.33 425.79 L 854.21 425.79 C 856.33 425.79 856.7 428.23 856.7 429.52 L 856.71 440.31 C 856.71 442.3 858.38 443.31 859.64 443.31 L 870.53 443.31 C 872.02 443.31 874.24 443.77 874.24 446.39 L 874.24 483.83 C 874.24 487.16 872.07 489.95 868.16 489.95 L 833.53 489.95 C 830.7 489.95 827.52 487.95 827.52 483.92 Z" fill="#ffffff" stroke="none" pointer-events="all"/><rect x="1060" y="412.9" width="200" height="90" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 458px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">https://keyboards.qmk.fm<br />Keyboard Metadata<br />Source: qmk/qmk_firmware<br />GH Action: Update API Data<br />Host: DigitalOcean Spaces</div></div></div></foreignObject><text x="1160" y="462" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">https://keyboards.qmk.fm...</text></switch></g><path d="M 845.04 420 L 845.14 308.77" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 845.15 303.52 L 848.64 310.52 L 845.14 308.77 L 841.64 310.51 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 905 630 L 1060 630" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 815 610 C 791 610 785 630 804.2 634 C 785 642.8 806.6 662 822.2 654 C 833 670 869 670 881 654 C 905 654 905 638 890 630 C 905 614 881 598 860 606 C 845 594 821 594 815 610 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 630px; margin-left: 786px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">QMK API</div></div></div></foreignObject><text x="845" y="634" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">QMK API</text></switch></g><rect x="1060" y="595" width="200" height="70" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 630px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">https://api.qmk.fm<br />RESTful API<br />Source: qmk/qmk_api<br />Host: Rancher on DO VM's</div></div></div></foreignObject><text x="1160" y="634" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">https://api.qmk.fm...</text></switch></g><path d="M 794 275.46 L 709.04 315.72 Q 700 320 700 330 L 700 570 Q 700 580 709.45 583.26 L 785.69 609.55" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 790.65 611.26 L 782.89 612.29 L 785.69 609.55 L 785.17 605.67 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 460 251.8 L 290 251.8" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 490 229 C 466 229 460 251.8 479.2 256.36 C 460 266.39 481.6 288.28 497.2 279.16 C 508 297.4 544 297.4 556 279.16 C 580 279.16 580 260.92 565 251.8 C 580 233.56 556 215.32 535 224.44 C 520 210.76 496 210.76 490 229 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 252px; margin-left: 461px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Digital Ocean<br />Spaces<br />(S3)</div></div></div></foreignObject><text x="520" y="255" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Digital Ocean...</text></switch></g><rect x="0" y="221.8" width="290" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 288px; height: 1px; padding-top: 252px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">https://qmk-api.nyc3.cdn.digitaloceanspaces.com<br />Space: qmk-api<br />Host: Digital Ocean</div></div></div></foreignObject><text x="145" y="255" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">https://qmk-api.nyc3.cdn.digitaloceanspaces.com...</text></switch></g><path d="M 580 251.8 L 787.63 251.31" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 792.88 251.3 L 785.89 254.81 L 787.63 251.31 L 785.87 247.81 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 875 790 L 1060 790" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 815 768 C 815 757.33 875 757.33 875 768 L 875 812 C 875 822.67 815 822.67 815 812 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 815 768 C 815 776 875 776 875 768 M 815 772 C 815 780 875 780 875 772 M 815 776 C 815 784 875 784 875 776" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 800px; margin-left: 816px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">RQ</div></div></div></foreignObject><text x="845" y="804" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">RQ</text></switch></g><rect x="1060" y="755" width="200" height="70" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 790px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Redis / RQ<br />Job Queue<br />Source: qmk/qmk_redis<br />Host: Rancher on DO VM's</div></div></div></foreignObject><text x="1160" y="794" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Redis / RQ...</text></switch></g><path d="M 845 670 L 845 753.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 845 758.88 L 841.5 751.88 L 845 753.63 L 848.5 751.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="785" y="910" width="120" height="80" rx="12" ry="12" fill="#ffffff" stroke="#000000" pointer-events="all"/><rect x="787" y="912" width="116" height="76" rx="11.4" ry="11.4" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 114px; height: 1px; padding-top: 950px; margin-left: 788px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">qmk_complier</div></div></div></foreignObject><text x="845" y="954" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">qmk_complier</text></switch></g><rect x="1060" y="915" width="200" height="70" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 950px; margin-left: 1061px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">QMK Compiler<br />Job Runners<br />Source: qmk/qmk_compiler<br />Host: Rancher on DO VM's</div></div></div></foreignObject><text x="1160" y="954" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">QMK Compiler...</text></switch></g><path d="M 845 820 L 845 903.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 845 908.88 L 841.5 901.88 L 845 903.63 L 848.5 901.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 779.03 925.64 L 529.38 833.46 Q 520 830 520 820 L 520 303.77" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 783.95 927.46 L 776.17 928.32 L 779.03 925.64 L 778.6 921.75 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 520 298.52 L 523.5 305.52 L 520 303.77 L 516.5 305.52 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 905 950 L 1060 950" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file

M lib/python/qmk/cli/lint.py => lib/python/qmk/cli/lint.py +5 -4
@@ 4,7 4,7 @@ from milc import cli

from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.keyboard import keyboard_completer
from qmk.keyboard import find_readme, keyboard_completer
from qmk.keymap import locate_keymap
from qmk.path import is_keyboard, keyboard



@@ 31,7 31,8 @@ def lint(cli):
    ok = True
    keyboard_path = keyboard(cli.config.lint.keyboard)
    keyboard_info = info_json(cli.config.lint.keyboard)
    readme_path = keyboard_path / 'readme.md'
    readme_path = find_readme(cli.config.lint.keyboard)
    missing_readme_path = keyboard_path / 'readme.md'

    # Check for errors in the info.json
    if keyboard_info['parse_errors']:


@@ 43,9 44,9 @@ def lint(cli):
        cli.log.error('Warnings found when generating info.json (Strict mode enabled.)')

    # Check for a readme.md and warn if it doesn't exist
    if not readme_path.exists():
    if not readme_path:
        ok = False
        cli.log.error('Missing %s', readme_path)
        cli.log.error('Missing %s', missing_readme_path)

    # Keymap specific checks
    if cli.config.lint.keymap: