feat: Add hardware metadata schema.
* Initial hardware metadata JSON schema. * GH Action to validate all schemas for boards/shields.
This commit is contained in:
parent
4a5454b0f9
commit
1d69bdda60
5 changed files with 368 additions and 0 deletions
35
.github/workflows/hardware-metadata-validation.yml
vendored
Normal file
35
.github/workflows/hardware-metadata-validation.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
name: Hardware Metadata Validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/hardware-metadata-validation.yml"
|
||||||
|
- "schema/hardware-metadata.schema.json"
|
||||||
|
- "app/boards/**/*.zmk.yml"
|
||||||
|
- "app/scripts/west_commands/metadata.py"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/hardware-metadata-validation.yml"
|
||||||
|
- "schema/hardware-metadata.schema.json"
|
||||||
|
- "app/boards/**/*.zmk.yml"
|
||||||
|
- "app/scripts/west_commands/metadata.py"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-metadata:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: zmkfirmware/zmk-dev-arm:2.5
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install -r app/scripts/requirements.txt
|
||||||
|
- name: West init
|
||||||
|
run: west init -l app
|
||||||
|
- name: Update modules (west update)
|
||||||
|
run: west update
|
||||||
|
- name: Export Zephyr CMake package (west zephyr-export)
|
||||||
|
run: west zephyr-export
|
||||||
|
- name: Validate Hardware Metadata
|
||||||
|
run: |
|
||||||
|
cd app
|
||||||
|
west metadata check
|
8
app/scripts/requirements.txt
Normal file
8
app/scripts/requirements.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
# Convert YAML to JSON for validation
|
||||||
|
remarshal>=0.14.0
|
||||||
|
|
||||||
|
# Perform our hardware metadata validation
|
||||||
|
jsonschema>=3.2.0
|
|
@ -7,3 +7,8 @@ west-commands:
|
||||||
- name: test
|
- name: test
|
||||||
class: Test
|
class: Test
|
||||||
help: run ZMK testsuite
|
help: run ZMK testsuite
|
||||||
|
- file: scripts/west_commands/metadata.py
|
||||||
|
commands:
|
||||||
|
- name: metadata
|
||||||
|
class: Metadata
|
||||||
|
help: Operate on ZMK metadata files
|
||||||
|
|
59
app/scripts/west_commands/metadata.py
Normal file
59
app/scripts/west_commands/metadata.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
'''Metadata command for ZMK.'''
|
||||||
|
|
||||||
|
from functools import cached_property
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
from jsonschema import validate, ValidationError
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
from textwrap import dedent # just for nicer code indentation
|
||||||
|
|
||||||
|
from west.commands import WestCommand
|
||||||
|
from west import log # use this for user output
|
||||||
|
|
||||||
|
|
||||||
|
class Metadata(WestCommand):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
'metadata', # gets stored as self.name
|
||||||
|
'ZMK hardware metadata commands', # self.help
|
||||||
|
# self.description:
|
||||||
|
dedent('''Operate on the board/shield metadata.'''))
|
||||||
|
|
||||||
|
def do_add_parser(self, parser_adder):
|
||||||
|
parser = parser_adder.add_parser(self.name,
|
||||||
|
help=self.help,
|
||||||
|
description=self.description)
|
||||||
|
|
||||||
|
parser.add_argument('subcommand', default="check",
|
||||||
|
help='The subcommand to run. Defaults to "check".', nargs="?")
|
||||||
|
return parser # gets stored as self.parser
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def schema(self):
|
||||||
|
return json.load(
|
||||||
|
open("../schema/hardware-metadata.schema.json", 'r'))
|
||||||
|
|
||||||
|
def validate_file(self, file):
|
||||||
|
print("Validating: " + file)
|
||||||
|
with open(file, 'r') as stream:
|
||||||
|
try:
|
||||||
|
validate(yaml.safe_load(stream), self.schema)
|
||||||
|
except yaml.YAMLError as exc:
|
||||||
|
print("Failed loading metadata yaml: " + file)
|
||||||
|
print(exc)
|
||||||
|
return False
|
||||||
|
except ValidationError as vexc:
|
||||||
|
print("Failed validation of: " + file)
|
||||||
|
print(vexc)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def do_run(self, args, unknown_args):
|
||||||
|
status = all([self.validate_file(f) for f in glob.glob(
|
||||||
|
"boards/**/*.zmk.yml", recursive=True)])
|
||||||
|
|
||||||
|
sys.exit(0 if status else 1)
|
261
schema/hardware-metadata.schema.json
Normal file
261
schema/hardware-metadata.schema.json
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
{
|
||||||
|
"$id": "https://zmkfirmware.dev/zmk.metadata.json",
|
||||||
|
"title": "HardwareMetadata",
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/board"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/shield"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/interconnect"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"$defs": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9_]+$"
|
||||||
|
},
|
||||||
|
"keyboard_siblings": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"features"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"$ref": "#/$defs/features"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"keys",
|
||||||
|
"display",
|
||||||
|
"encoder",
|
||||||
|
"underglow",
|
||||||
|
"pointer"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"interconnects": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sibling_details": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"$ref": "#/$defs/features"
|
||||||
|
},
|
||||||
|
"variants": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/variant"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"interconnect": {
|
||||||
|
"title": "Interconnect",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"file_format",
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"url",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"file_format": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "1"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "interconnect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"board": {
|
||||||
|
"title": "Board",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"file_format",
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"url",
|
||||||
|
"arch",
|
||||||
|
"type",
|
||||||
|
"outputs"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"file_format": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "1"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"arch": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9_]+$"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "board"
|
||||||
|
},
|
||||||
|
"siblings": {
|
||||||
|
"$ref": "#/$defs/keyboard_siblings"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"usb",
|
||||||
|
"ble"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"$ref": "#/$defs/features"
|
||||||
|
},
|
||||||
|
"variants": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/variant"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exposes": {
|
||||||
|
"$ref": "#/$defs/interconnects"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shield": {
|
||||||
|
"title": "Shield",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"file_format",
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"url",
|
||||||
|
"type",
|
||||||
|
"requires"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"file_format": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "1"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"$ref": "#/$defs/id"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uri"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "shield"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"$ref": "#/$defs/features"
|
||||||
|
},
|
||||||
|
"variants": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/variant"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"siblings": {
|
||||||
|
"$ref": "#/$defs/keyboard_siblings"
|
||||||
|
},
|
||||||
|
"requires": {
|
||||||
|
"$ref": "#/$defs/interconnects"
|
||||||
|
},
|
||||||
|
"exposes": {
|
||||||
|
"$ref": "#/$defs/interconnects"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue