The same checks, without an editor
lpg runs the resolution, validation and generation the extension runs, in plain
Node. That is what lets a pull request be gated on the model being valid.
lpg - labeled property graph modeler
Usage:
lpg check <model.lpg.yaml>
lpg emit <model.lpg.yaml> --target <gql|ladybug|linkml|neo4j|owl|pgschema|shacl> [options]
lpg ids <model.lpg.yaml> assign any missing stable element ids
lpg targets
Options:
--target <name> generation target (repeatable)
--out <dir> write artifacts to this directory instead of stdout
--edition <name> neo4j edition: community (default) or enterprise
check
Resolves the model and its imports, validates it, and prints every diagnostic as
file:line:column severity code: message. Exits 1 when the model
has errors, 0 otherwise — warnings alone do not fail the run.
$ lpg check social.lpg.yaml
0 error(s), 0 warning(s)
emit
Generates one or more targets. --target is repeatable. Without
--out, artifacts go to stdout; with it, each is written as
<model-stem>.<target>.<ext> in that directory, which is
created if it does not exist.
$ lpg emit social.lpg.yaml --target ladybug --target shacl --out ./schema
schema/social.ladybug.cypher
schema/social.shacl.ttl
The standards targets work the same way, and are what you reach for when the model has to be read by something this project does not own:
$ lpg emit social.lpg.yaml --target gql --target pgschema --target linkml --out ./schema
schema/social.gql.gql
schema/social.pgschema.pgs
schema/social.linkml.yaml
A model with errors produces nothing at all — the command reports them and exits
1 rather than writing a partial artifact. Downgrade warnings are printed to
stderr after the artifact list, and each one is also recorded as a comment inside the
artifact:
$ lpg emit social.lpg.yaml --target ladybug --out ./schema
schema/social.ladybug.cypher
social.lpg.yaml:26:7 warning [ladybug] downgrade-required: Property
'Person.email' is required, which LadybugDB cannot enforce: it has no
NOT NULL and only the primary key is non-null.
ids
Assigns a stable element id to every node type, edge type, property and mixin that lacks one, editing the model file in place. Useful after hand-writing a model, or after a merge. Existing ids are never rewritten.
$ lpg ids social.lpg.yaml
assigned 14 id(s) in /work/social.lpg.yaml
targets
Prints the registered target names, one per line.
$ lpg targets
gql
ladybug
linkml
neo4j
owl
pgschema
shacl
Options
- --target <name>
- Which generator to run. Repeat the flag to run several in one pass. An unknown name is an error naming the ones that exist.
- --out <dir>
- Write artifacts into this directory instead of stdout. Created recursively if missing.
- --edition <community|enterprise>
- Neo4j edition. Community — the default — cannot enforce existence or node key constraints, so under it those become downgrades and comments.
Exit codes
| Code | When |
|---|---|
0 | Success. Warnings may still have been printed. |
1 | The model has errors, a file could not be read, or a generator failed. |
2 | Usage error — an unknown flag, a missing target, or no model path. |
In continuous integration
Gate the branch on the model staying valid, and on the committed schema staying in sync with it:
name: schema
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22' }
- run: npm ci
# Fails the build on any model error.
- run: npx lpg-modeler-cli check model/domain.lpg.yaml
# Regenerate and fail if the committed artifacts drifted.
- run: npx lpg-modeler-cli emit model/domain.lpg.yaml --target ladybug --target shacl --out schema
- run: git diff --exit-code schema
The second step is the one that matters over time. It turns "someone forgot to regenerate the DDL" from a thing you discover during an incident into a red build.