Unified SCRIPT statement (via NSchema.Core 4.4.0). SCRIPT '<name>' RUN [ALWAYS | ONCE] ON <event> AS $$…$$; is the new canonical form of deployment scripts and data migrations: the event is PRE DEPLOYMENT, POST DEPLOYMENT, or a structural change (ADD COLUMN / ALTER COLUMN TYPE / ADD CONSTRAINT with a target path).
Run-once scripts. A RUN ONCE script is recorded in the state backend on a successful apply and skipped by later plans; a recorded script whose body has since changed stays skipped and warns. Plan output marks run-once scripts in the pre/post-deployment sections ((run once); runCondition in --json). Recording requires a state backend — planning without one warns.
Scripts in schema templates. Both script kinds can be declared inside a TEMPLATE … BEGIN … END; body and instantiate once per applied schema, with the {schema} token substituted in the name and the SQL.
Changed
Script names must be unique across the project (they identify scripts in diagnostics and run-once tracking); a template-declared script applied to multiple schemas can include {schema} in its name.
Deprecated
The PRE|POST DEPLOYMENT '<name>' AS $$…$$; and MIGRATION ['name'] FOR <trigger> <path> AS $$…$$; forms still work, but plan/apply/validate now surface a deprecations warning naming the SCRIPT replacement. They will be removed in NSchema 5.0.
refresh --force refresh fails if it finds an unreadable state payload, instead of silently overwriting it; --force replaces it, resetting the script ledger.
state pull [file] to pull the raw recorded state payload out of the configured backend. Writes to a file or stdout.
state push <file> to push the raw recorded state payload into the configured backend. Push takes the state lock (--no-lock to skip).
script command group to manage the scripts recorded in the state:
script list shows the recorded scripts (name, execution time, body hash); --json emits them as a single array.
script hash [name] computes the body hash of the project’s script declarations, bare on stdout for one script, or a listing of all of them, for hand-editing pulled state.
script taint <name> removes a script’s record, so it runs again on the next apply.
script untaint <name> records a script as executed without running it, using the body hash from the script’s declaration. Taint and untaint take the state lock (--no-lock to skip).