Skip to content

Validate before you deploy: pre-flight checks

You’re about to quench a fleet of tenant databases. Three servers, some of them behind a VPN, one you haven’t touched since the last sprint. The MinimumVersion floor in your package says SQL Server 2019 or newer. You think the target query matches shop_tenant_a/b/c. You’ll find out for certain the moment the deploy starts — which is exactly the wrong moment, because SchemaSmith is already opening connections, and if the version is wrong or the targets don’t match, you’ve burned the first minute of a production window discovering a fact you could have checked before you crossed the threshold.

Hey folks. I’m Forge Barrett, master of the Content Forge here at SchemaSmith.

This module is about the two switches that let you read the fire before you commit the metal. --TestConnection and --PreviewTargets are read-only pre-flight checks: they connect, validate, enumerate — and stop. Neither one deploys a byte. Run them before a quench and you know exactly what you’re stepping into. Wire them into CI and the gate refuses to open when the conditions aren’t right.

These switches shipped in v2.2.0 (#312).

A blacksmith tests the temperature of the fire before the workpiece goes in. --TestConnection does the same: it connects to every server in your config, validates that each one meets the declared MinimumVersion floor, and reports the result — exit 0 if everything’s ready, exit 2 if not.

Terminal window
schemaquench --ConfigFile:quench.settings.json --TestConnection

When the server meets the floor, this is what comes back:

Pre-flight diagnostics for Shop (--TestConnection)
Testing connection to configured servers
localhost,11433 (663cfb8abdfa) connection succeeded
Validate server version floor
localhost,11433: detected SqlServer version 16.0.4260.1
Validate Minimum Version
RESULT: PASS (connections and minimum version validated)

Exit 0. Connection succeeded, version cleared. Two version checks ran, and they answer different questions. Validate server version floor is SchemaSmith’s own intrinsic floor — the oldest release of that engine the tool supports at all — and it names the version it detected, so the number is in your log whether the run passes or fails. Validate Minimum Version is your floor, the one your package declares. The engine’s floor you can’t argue with; yours you chose.

PostgreSQL, MySQL, and MariaDB follow the same shape — localhost (172.21.0.3) connection succeeded on PG, the same structure on the MySQL family — same two version lines, same RESULT: PASS, same exit 0.

Now raise the floor above the server. Set MinimumVersion to 99 in Product.json and re-run:

Validate server version floor
localhost,11433: detected SqlServer version 16.0.4260.1
Validate Minimum Version
Pre-flight FAILED: One or more target servers are below the product's declared MinimumVersion; aborting before any deployment:
localhost,11433: detected version 16.0.4260.1 is below the product's declared MinimumVersion 99

Exit 2. The manifest names the server and both version numbers — the detected one and the floor you declared. Each engine reports in its own native form: SQL Server 16.0.4260.1, PostgreSQL 16, MySQL 8.0.45, MariaDB 11.4.12-MariaDB-ubu2404. That’s the server’s own answer, not a format SchemaSmith imposes. Set the floor to 9.9 on MySQL and you see detected version 8.0.45 is below the product's declared MinimumVersion 9.9. Same structure, different dialect.

Restore MinimumVersion to the floor the lab declares — 2019 for SQL Server, 15 for PostgreSQL, 8.0 for MySQL, 10.6 for MariaDB — and you’re back to exit 0 on all four. Those are values this lab picked, deliberately high so there’s room to fail against. They’re nowhere near SchemaSmith’s own floors, which reach back to SQL Server 2008, PostgreSQL 12, MySQL 5.7, and MariaDB 10.2.

--TestConnection proves the fire is ready. --PreviewTargets goes one step further: it runs the full connection-and-version check, then executes the DatabaseIdentificationScript from each template and prints the target roster. You see every database that would be quenched — resolved, named, laid out template by template — before anything moves.

Terminal window
schemaquench --ConfigFile:quench.settings.json --PreviewTargets

Three tenants, one template:

Pre-flight diagnostics for Shop (--PreviewTargets)
Testing connection to configured servers
localhost,11433 (663cfb8abdfa) connection succeeded
Validate server version floor
localhost,11433: detected SqlServer version 16.0.4260.1
Validate Minimum Version
Load Template Schema: Main
Check for Template Special Script Tokens
Locate Databases To Quench (localhost,11433)
Template: Main [required]
db: shop_tenant_a
db: shop_tenant_b
db: shop_tenant_c
RESULT: PASS

Exit 0. Three databases under Template: Main [required]. That’s the roster — the exact set the next quench would process, nothing invented, nothing assumed. PostgreSQL, MySQL, and MariaDB produce the identical tree under their template. (The MySQL family lists databases only; on SQL Server or PostgreSQL a schema-template would add schemas: sub-lines, but that’s out of scope for a DB-level module.)

Now break the discovery. Point DatabaseIdentificationScript at shop_tenant_z% — a pattern that matches nothing — and re-run. RequireAtLeastOneTarget defaults to true, so matching zero is a failure:

Locate Databases To Quench (localhost,11433)
Template: Main [required]
ERROR: matched 0 targets for required template 'Main' - no databases or schemas were discovered
RESULT: FAIL (one or more required templates matched nothing)

Exit 2. All four engines. The failure tells you exactly which template matched nothing and why — not a silent no-op, not a vague error after a partial deploy. It’s the gate that won’t open until the roster is proven. Restore the pattern to shop_tenant_% and the pass is back.

Exit 0 means proceed. Exit 2 means abort. That’s the whole contract — and it’s enough to wire into any CI pipeline.

Bash:

Terminal window
schemaquench --ConfigFile:quench.settings.json --TestConnection || { echo "GATE: pre-flight failed — aborting deploy"; exit 1; }
schemaquench --ConfigFile:quench.settings.json --PreviewTargets || { echo "GATE: target preview failed — aborting deploy"; exit 1; }
echo "GATE: pre-flight green — safe to deploy"

PowerShell:

Terminal window
schemaquench --ConfigFile:quench.settings.json --TestConnection
if ($LASTEXITCODE -ne 0) { Write-Error "Pre-flight failed — aborting deploy"; exit 1 }
schemaquench --ConfigFile:quench.settings.json --PreviewTargets
if ($LASTEXITCODE -ne 0) { Write-Error "Target preview failed — aborting deploy"; exit 1 }
Write-Host "Pre-flight green — safe to deploy"

Both pass → the green line prints and the deploy step is allowed to run. Introduce the floor violation from the first scenario — raise MinimumVersion to 99 — and --TestConnection exits 2, the gate prints the abort message, and the pipeline stops before --PreviewTargets even runs. The quench step never fires.

The pattern is two checks before the deploy command. Connection and version first, then targets. The whole sequence is read-only: nothing writes, nothing applies, nothing moves until both gates say green.

Connection details, identification queries, and version-string format differ per engine; everything else — the switch names, the exit codes, the output structure, the CI gate pattern — is identical.

SQL ServerPostgreSQLMySQLMariaDB
Connectionlocalhost,11433 (sa)localhost:15432 (postgres)localhost:13306 (root)localhost:13307 (root)
DatabaseIdentificationScriptSELECT [Name] FROM master.sys.databases WHERE [Name] LIKE 'shop_tenant_%'SELECT datname FROM pg_database WHERE datname LIKE 'shop_tenant_%'SELECT SCHEMA_NAME FROM information_schema.schemata WHERE SCHEMA_NAME LIKE 'shop_tenant_%'SELECT SCHEMA_NAME FROM information_schema.schemata WHERE SCHEMA_NAME LIKE 'shop_tenant_%'
Detected version display16.0.4260.1168.0.4511.4.12-MariaDB-ubu2404
Pass floor used2019158.010.6

Four engines, four shapes — and that’s on purpose, not an inconsistency to squint past. The detected string is whatever the server itself publishes, because on three of these four the digits past the major are the ones that decide what you get. CREATE OR ALTER arrived in SQL Server 2016 SP1, so 13.0.4001 and 13.0.1601 are both “2016” and behave differently. CHECK constraints arrive at MySQL 8.0.16, RENAME COLUMN at MariaDB 10.5.2. Trim all four to a bare major and the log reads tidier while telling you less — right when you’re diagnosing why a feature degraded. PostgreSQL prints a bare major because a bare major is genuinely all it gates on.

You don’t have to match that shape when you declare a floor. MinimumVersion has its own friendlier grammar — 2019 or 16 on SQL Server, 15 on PostgreSQL, 8.0 on MySQL, 10.6 on MariaDB — and SchemaSmith compares it correctly against whatever the server reports. The lab above declares 2019 and passes against a server reporting 16.0.4260.1. Declare the floor you need; the matching is SchemaSmith’s problem.

Check yourself: --PreviewTargets exits 0 and shows three tenant databases. Does that mean the quench will process all three?

Yes — that’s exactly what it means. —PreviewTargets resolves the target roster by executing the real DatabaseIdentificationScript against the live server. The three databases it lists are the ones the next quench will open and process. If any of them shouldn’t be there, the place to fix it is the identification script, not the deploy command.


A good smith reads the fire before the workpiece goes in. Not because something always goes wrong — because the one time it does, you want to know before you’ve committed the heat. --TestConnection checks the temperature. --PreviewTargets checks the work. Both are free; both exit cleanly; and when either one says no, the quench stays home.

Pre-flight something you’re not sure about? Email me at forgebarrett@schemasmith.com — I read every one.

More’s coming from the forge — CI/CD governance for your extensions and packaging a patch down to only the objects that changed.

Until then, may you always read the fire before you commit the metal, and never cross a threshold you haven’t already proven will hold.

— Forge