linkml-scala-action
September 1, 2026 ยท View on GitHub
A GitHub Action to validate and generate from LinkML schemas in CI, powered by linkml-scala.
- ๐ Pure Node.js โ no Docker, no JVM, no Python, no binary download. Uses the
@neverblink/linkmlnpm package. - ๐ฅ๏ธ Runs everywhere โ Linux, macOS, and Windows runners.
- ๐ท๏ธ Inline annotations โ schema problems posted as GitHub annotations (on the PR "Files changed" tab and the check summary), pinned to a line and column when the engine reports one.
- โก Fast โ validating a schema is a few milliseconds after Node starts up.
Quick start
Validate every schema in your repo on each push:
name: linkml
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: NeverBlink-OSS/linkml-scala-action@v0.15.1
with:
files: "schemas/**/*.yaml"
Generate JSON Schema and commit/upload it as an artifact:
- uses: NeverBlink-OSS/linkml-scala-action@v0.15.1
with:
command: generate
generator: json-schema
files: "schemas/**/*.yaml"
output: build/json-schema
- uses: actions/upload-artifact@v4
with:
name: json-schema
path: build/json-schema
Fail the build on warnings too:
- uses: NeverBlink-OSS/linkml-scala-action@v0.15.1
with:
files: "schemas/**/*.yaml"
strict: true
See CHANGELOG.md for what changed between releases.
Inputs
| Input | Default | Description |
|---|---|---|
command | validate | validate or generate. |
files | required | Schema files. Space/newline-separated; globs incl. ** supported. |
strict | false | Treat warnings as failures. Errors always fail; warnings only with this on. |
generator | โ | generate: json-schema, shacl, rdfs, linkml, frictionless, graphql, or scala. |
output | โ | generate: output directory (one file per input schema). If omitted, output is printed to the job log. |
open | false | generate json-schema/shacl: allow additional properties (open shapes). |
format | ttl | generate shacl/rdfs: RDF serialization โ ttl (Turtle, prefixed and pretty-printed) or nt (N-Triples). |
package | linkml | generate scala: target package name. |
pruning-mode | skip | generate frictionless: which classes become tables โ treeRoot (only those reachable from the tree_root class), schema (only those reachable from a class defined in the root schema), or skip (every class). |
tree-root | โ | generate frictionless: tree root class name to use instead of the schema's own tree_root. Only has an effect with pruning-mode: treeRoot. |
skip-classes-without-identifier | false | generate frictionless: skip classes with no identifier slot. Such a table gets no primary key and nothing can reference it. |
imports | โ | Directory of extra .yaml schemas made available to imports: (keyed by path, relative to working-directory). |
ignore | โ | Newline-separated issue type names to silence, one per line. |
annotations | true | Emit GitHub error/warning annotations. |
working-directory | . | Base directory for resolving files, imports, and output. |
Outputs
| Output | Description |
|---|---|
problems | Total number of problems found across all schemas. |
files | Number of schema files processed. |
linkml-version | The bundled linkml-scala version. |
Behavior
Loading a schema validates it, so both commands report the same problems. Each has a severity:
| Severity | Meaning | Effect |
|---|---|---|
FATAL | The schema could not be loaded at all (unparseable YAML, an unresolvable import, an unknown class reference). | Fails the step. Nothing is generated from the schema. |
ERROR | The schema loaded but is invalid (e.g. two tree_root classes, a non-unique name). | Fails the step. Generation still runs, so the output is there to inspect. |
WARNING | Advisory (e.g. no tree_root class). | Reported only; fails the step when strict: true. |
- Generators write one output file per input schema into
output, named after the schema (person.yamlโperson.schema.json). Thescalaandfrictionlessgenerators emit multiple files per schema, so those go underoutput/<schema-name>/(forfrictionless, adatapackage.jsonplus oneschemas/<table>.jsonper table). - Imports: if your schemas use
imports: [shared], pointimportsat a directory containingshared.yaml. Files are keyed by filename. ignoresilences problems by issue type. Every problem the engine reports is tagged with the class it belongs to in the validation report.
Resolving imports โ example
- uses: NeverBlink-OSS/linkml-scala-action@v0.15.1
with:
command: generate
generator: shacl
files: "schemas/main.yaml" # e.g. imports: [common/types]
imports: "schemas" # dir holding schemas/common/types.yaml
output: build/shacl
Versioning
The linkml-scala engine version is bundled into each release of this action, so the action version tracks the engine version. Pin an exact tag:
- uses: NeverBlink-OSS/linkml-scala-action@v0.15.1
New engine releases are picked up automatically by the track-linkml-scala workflow, which bumps the bundled engine, rebuilds, re-runs the test suite against it, updates the examples above, and โ only if that passes โ cuts the matching vX.Y.Z release. It runs daily and can also be triggered manually (with an optional target version and a dry-run mode).
Development
npm ci
npm run build # bundles src/ + @neverblink/linkml into dist/index.cjs (committed)
npm test # simulates the Actions runtime and asserts behavior
The bundled dist/ is committed so the action needs no install step at runtime; CI
verifies it stays in sync with src/.
License
This project is being developed and maintained by NeverBlink. For any inquiries, please reach out to us via email.