Skip to content

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

  • --scope takes an object, not just a schema. A value is an address — --scope app for a whole schema, --scope app.orders for one object. Read under the NSQL identifier rules, so a quoted segment can carry dots and spaces (--scope '"my.schema"."Order Details"').
  • --ephemeral on plan, apply, and destroy runs against an in-memory state store discarded when the command exits, standing in for a configured STATE store — 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. init resolves each PLUGIN — 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 to init.
  • plugin update [<label>] re-resolves ranges to their highest available version and rewrites the lockfile — every plugin, or a single one by label.
  • plugin outdated shows each plugin’s pinned version against the newest its range allows (what update would install) and the newest available for this engine.
  • new asks 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> on new answers a question up front, so a scripted run never blocks. Repeatable.

Changed

  • Environments select configuration, not schema. --environment layers the environment’s files over the base configuration; schema files are no longer overlaid per environment. An overlay merges the DATABASE, STATE, or ENGINE statement attributes.
  • DATABASE and STATE replace PROVIDER and BACKEND. Each names the thing it configures. The built-in local-file store is STATE file ( path = '…' );.
  • PLUGIN declares plugin dependencies. PLUGIN <label> ( source = '…', version = '…' ); names the package and pins its version; DATABASE/STATE reference the label. The built-in label-to-package map is gone — every plugin is declared explicitly, and version/source no longer ride the configuring statement. A version may 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 to dotnet tool update.
  • new authors the ENGINE assertion. 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.
  • new runs init afterwards, resolving and locking the plugins it declared so the project is ready to plan immediately. Pass --no-init to skip it for an offline or edit-first workflow.
  • Planning always diffs recorded state against the project, so plan (and a fresh apply) now require both a database and a state store. Planning against the live database is no longer available; use refresh to capture the live schema first.
  • destroy reads 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 diff object 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-actions accepts Ignore alongside Error, Warn, and Allow.
  • Abbreviated commands are spelled out. fmt is format, db is database, and scaffold is new. The old names are gone rather than aliased, so a script still using one fails loudly instead of drifting.
  • apply re-runs policies. The policy flags now apply to apply --plan-file too, re-checking the saved plan before executing it.
  • script hash, script taint, and script untaint operate on deployment scripts. A template-scoped script is addressed as schema.name, as script hash lists 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 refused keeps the part that matters.
  • --json distinguishes the two. An expected failure emits a {"type":"diagnostics"} event; an internal error’s {"type":"error"} event gains exception and stack, so a consumer can tell “your project is wrong” from “NSchema is broken” by a null check.

Fixed

  • Editing a PLUGIN version now works correctly. init preferred the lockfile’s pin unconditionally, so changing a declared version and re-running init silently 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 update exits non-zero when a restore fails, rather than reporting the problem and returning 0.
  • Plugin loading now resolves a plugin’s native libraries (e.g. SQLite’s e_sqlite3) from its restored dependency closure.
  • new names the right environment variable. It pointed at a per-provider variable (NSCHEMA_POSTGRES_CONNECTION_STRING) that no longer has any effect; it now names NSCHEMA_DATABASE_CONNECTION_STRING.
  • Contradictory flags fail fast. --quiet with --verbose, or --json with a non-json --format, are now rejected while parsing.
  • A failed command always exits non-zero. lock release, init, new, and the plugin commands reported failures but still exited 0.
  • state show reports an error when no state has been recorded yet instead of failing on a missing source.