Browse Source
* Initial hardware metadata JSON schema. * GH Action to validate all schemas for boards/shields.xmkb
Peter Johanson
4 years ago
committed by
Pete Johanson
5 changed files with 368 additions and 0 deletions
@ -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 |
@ -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 |
@ -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) |
@ -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…
Reference in new issue