agent didnt pin the migration CLI version in CI and a minor release changed the generated SQL
How to fix CI where an unpinned migration CLI version drifted and a minor release changed the generated SQL: pinning the exact version and re-verifying the output. Use when CI generates different SQL than local runs. Not for SQL differences caused by different database versions.
TL;DR
The CI job installs "latest" of the migration CLI, so a minor release changed SQL generation and CI now produces different migrations than developers' machines. Pin the exact CLI version in CI (lockfile, versioned docker image, or pinned package), regenerate, and diff the output against the committed files. Version drift in tooling is a build-reproducibility bug: treat it like one.
The query
agent didnt pin the migration CLI version in CI and a minor release changed the generated SQLUse this when
- CI generates different migration SQL than local runs
- Unpinned migration CLI drifted in CI
- A minor release changed generated SQL
- An agent's CI stopped reproducing local migrations
Not for
- SQL differences from different database server versions (pin the database too)
- Hand-written SQL differing between environments (a code problem)
- A CLI upgrade done deliberately (verify deliberately, then pin the new version)
Steps
1. Prove the drift is the CLI version
[cli] --versionRun it in CI and locally. Expected output: two different versions, e.g. CI on 5.3.1 and local on 5.2.0. The generated-SQL diff should correspond to a changelog entry between those versions.
2. Pin the exact version in CI
Options: a lockfile entry, a versioned docker image tag (not latest), or a pinned package version in the CI config. Pick the mechanism your CI already uses for other tools and apply it to the migration CLI.
Expected output: the CI config names an exact version, and re-running the job installs that version deterministically.
3. Regenerate and diff against committed files
With the pinned version, regenerate the migrations (or the SQL preview) in CI and diff against what is committed. Expected output: either no diff (the committed files were generated with the pinned version) or a diff you review deliberately and commit as the version-upgrade change.
4. Align local environments to the pin
Developers and agents install the pinned version too: document it, or better, make the repo's setup script install it. Expected output: local --version and CI --version print the same string.
5. Add a version assertion to the job
A CI step that fails when the installed CLI version differs from the pinned one. Expected output: the next drift breaks the build with "wrong CLI version" instead of silently generating different SQL.
Tool compatibility
Tool-agnostic: applies to prisma, flyway, liquibase, dbmate, and any CLI that generates SQL.
Variant phrasings
migration CLI version drift CI different SQL
Steps 1 and 2: prove the version gap, then pin.
pin prisma cli version in CI
Step 2: pin in package.json / lockfile so CI and local resolve identically.
minor release changed generated migration SQL
Step 3: regenerate with the pinned version and review the diff as a deliberate upgrade commit.
Why it happens
SQL generation is code, and code changes between releases: formatters improve, defaults change, bugs get fixed. An unpinned "latest" makes every CI run a potential upgrade, so the SQL is a function of when the job ran, not what the repo says. Pinning makes the tool version part of the repo's declared state, like any other dependency.
Edge cases
- Pinning the CLI but not the database server leaves half the drift: pin both, or the next mystery diff comes from the server side.
- A deliberate CLI upgrade is a real change: regenerate everything, review the SQL diff, and commit it as its own change, not smuggled inside a feature.
- Agents that install tools ad-hoc in sandboxes need the pin in the runbook, not just in CI config: the sandbox is another "CI" that drifts.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstL7KqX5ZWTsXDZ6oTXCZRg