CLI
Version 5.0.0
v5.0 moves the CLI onto NSchema.Core 5.0, whose rearchitecture reshapes configuration, plugins, and planning. Changes below are relative to 4.5.1.
Added
--scopetakes an object, not just a schema. A value is an address —--scope appfor a whole schema,--scope app.ordersfor one object. Read under the NSQL identifier rules, so a quoted segment can carry dots and spaces (--scope '"my.schema"."Order Details"').--ephemeralonplan,apply, anddestroyruns against an in-memory state store discarded when the command exits, standing in for a configuredSTATEstore — for CI pipelines that bootstrap disposable databases. Run-once script history does not persist across runs in this mode.- A lockfile (
nschema.lock) pins declared plugin versions to concrete versions.initresolves eachPLUGIN— a range to the highest available version, an exact pin to itself — and records it; later commands read the pin, so a plugin without a lockfile entry is an error that points toinit. plugin update [<label>]re-resolves ranges to their highest available version and rewrites the lockfile — every plugin, or a single one by label.plugin outdatedshows each plugin’s pinned version against the newest its range allows (whatupdatewould install) and the newest available for this engine.newasks the plugins what it needs to know. A database or state plugin can declare the questions its configuration needs, composing the answers into the statement it writes.--set <key>=<value>onnewanswers a question up front, so a scripted run never blocks. Repeatable.
Changed
- Environments select configuration, not schema.
--environmentlayers the environment’s files over the base configuration; schema files are no longer overlaid per environment. An overlay merges theDATABASE,STATE, orENGINEstatement attributes. DATABASEandSTATEreplacePROVIDERandBACKEND. Each names the thing it configures. The built-in local-file store isSTATE file ( path = '…' );.PLUGINdeclares plugin dependencies.PLUGIN <label> ( source = '…', version = '…' );names the package and pins its version;DATABASE/STATEreference the label. The built-in label-to-package map is gone — every plugin is declared explicitly, andversion/sourceno longer ride the configuring statement. Aversionmay be an exact pin or a NuGet-style range ([5.0,6.0)).ENGINE ( version = '…' );asserts the engine version. A project can require an engine version range; a mismatch fails with a pointer todotnet tool update.newauthors theENGINEassertion. A scaffolded project pins the engine to the CLI’s current major (e.g.ENGINE ( version = '[5.0,6.0)' );), so it fails fast on an incompatible tool.newrunsinitafterwards, resolving and locking the plugins it declared so the project is ready toplanimmediately. Pass--no-initto skip it for an offline or edit-first workflow.- Planning always diffs recorded state against the project, so
plan(and a freshapply) now require both a database and a state store. Planning against the live database is no longer available; userefreshto capture the live schema first. destroyreads the managed schema from the recorded state. The fallback to the working-directory schema when no store was configured is gone, and a state store is now required.- Plan output folds scripts into the diff. Deployment and change-event scripts are first-class parts of the diff, shown in the plan tree (and carried on the
diffobject in--json); the separate pre/post-deployment and data-migration sections are gone. - Policy-blocked plans still render. A plan blocked by policy shows the complete diff and SQL alongside the blocking diagnostics; error severity is what stops an apply.
--destructive-actionsacceptsIgnorealongsideError,Warn, andAllow.- Abbreviated commands are spelled out.
fmtisformat,dbisdatabase, andscaffoldisnew. The old names are gone rather than aliased, so a script still using one fails loudly instead of drifting. applyre-runs policies. The policy flags now apply toapply --plan-filetoo, re-checking the saved plan before executing it.script hash,script taint, andscript untaintoperate on deployment scripts. A template-scoped script is addressed asschema.name, asscript hashlists it.- Errors you can fix are reported as diagnostics; anything else is considered a a bug. A broken configuration file, an unresolvable plugin, an unreachable database — each renders in the diagnostics table, sourced by the file and line or the plugin label that caused it. Anything else reaching the top level is a defect in NSchema: it names the exception type, links the issue tracker, and prints a stack trace unconditionally, so a report needs no re-run with a flag. Messages carry the inner-exception chain, so a cause like
Failed to connect to 127.0.0.1:5432 -> Connection refusedkeeps the part that matters. --jsondistinguishes the two. An expected failure emits a{"type":"diagnostics"}event; an internal error’s{"type":"error"}event gainsexceptionandstack, so a consumer can tell “your project is wrong” from “NSchema is broken” by a null check.
Fixed
- Editing a
PLUGINversion now works correctly.initpreferred the lockfile’s pin unconditionally, so changing a declared version and re-runninginitsilently kept restoring the old one. - An incompatible plugin doesn’t crash the CLI. This CLI will now give a proper explanation of the problem instead.
plugin updateexits non-zero when a restore fails, rather than reporting the problem and returning0.- Plugin loading now resolves a plugin’s native libraries (e.g. SQLite’s
e_sqlite3) from its restored dependency closure. newnames the right environment variable. It pointed at a per-provider variable (NSCHEMA_POSTGRES_CONNECTION_STRING) that no longer has any effect; it now namesNSCHEMA_DATABASE_CONNECTION_STRING.- Contradictory flags fail fast.
--quietwith--verbose, or--jsonwith a non-json--format, are now rejected while parsing. - A failed command always exits non-zero.
lock release,init,new, and theplugincommands reported failures but still exited0. state showreports an error when no state has been recorded yet instead of failing on a missing source.