CI Gate: Stop a Destructive Migration Before It Merges
The CI gate runs as a step in your own pipeline. It fires when a pull request touches a .sql file, reads the migrations in that pull request, and blocks the PR if they drop a table, a column or an index. Everything else in your repository is left alone, so the rest of your pipeline pays nothing for it.
Nothing runs against your database. The gate reads the migration files and reports a verdict; the only thing it can stop is the merge.
What the gate checks
- Destructive statements in the migrations the PR touches. A migration that drops a table, a column or an index is what stops a pull request.
- Only matching files. The step takes a migration glob —
**/*.sqlby default — and analyses only what matches. - Nothing else runs. A pull request that touches no
.sqlfile does not invoke the gate at all.
The three verdicts
A run takes the verdict of its most severe finding: one BLOCK makes the whole gate a BLOCK, whatever else came back clean.
Step 1 — Create a key for your pipeline
In the dashboard, open CI Gate and choose Connect a repository. The key it creates signs every call from the gate and is what tells us the analysis is yours. It is shown once only — we store just a hash, so we cannot show it to you again. Name it after the repository it belongs to.
Step 2 — Add the step to your pipeline
GitHub Actions
Create .github/workflows/optiquery.yml with the job the wizard prints for you:
# .github/workflows/optiquery.yml
name: Praxion CI Gate
on:
pull_request:
paths:
- "**/*.sql"
jobs:
optiquery-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Praxion CI Gate
uses: optiquery/ci-action@v1
with:
control-plane-url: ${{ secrets.OPTIQUERY_CONTROL_PLANE_URL }}
api-key: ${{ secrets.OPTIQUERY_API_KEY }}
repository: ${{ github.repository }}
pr-number: ${{ github.event.pull_request.number }}
sha: ${{ github.event.pull_request.head.sha }}
migration-glob: "migrations/**/*.sql"
The pull_request.paths filter is what keeps the gate out of the way: it only wakes up when the pull request touches a .sql file. Set migration-glob to **/*.sql if your migrations are not all under one directory.
Store both secrets in Settings → Secrets and variables → Actions:
OPTIQUERY_CONTROL_PLANE_URL—https://praxion-api-782r.onrender.comOPTIQUERY_API_KEY— the key you created in step 1
GitLab CI
Include the job rather than copying it, so the version stays pinned by URL:
# .gitlab-ci.yml
include:
- remote: 'https://raw.githubusercontent.com/optiquery/ci-action/v1/.gitlab-ci.yml'
variables:
OPTIQUERY_CONTROL_PLANE_URL: $OPTIQUERY_CONTROL_PLANE_URL
OPTIQUERY_API_KEY: $OPTIQUERY_API_KEY
OPTIQUERY_REPOSITORY: $CI_PROJECT_PATH
OPTIQUERY_MIGRATION_GLOB: "migrations/**/*.sql"
Store both variables in Settings → CI/CD → Variables, marked as masked.
Step 3 — Check it works
Open a pull request that touches a .sql file. The CI gate screen detects it on its own. Until the first call arrives the screen is empty — and an empty gate is worth reading carefully, because a gate that never runs does not fail. It is simply absent. Setup is not finished until you have seen a verdict in the dashboard.
Reading a BLOCK
A BLOCK is a question, not an error message. A migration dropped a table, a column or an index, and a useful review answers it one of two ways:
- The migration gets fixed. The drop was collateral damage and it goes back to doing whatever it was supposed to do.
- Someone means it. A dead column that really is dead should be dropped as the consequence of a person deciding so — not of a green check nobody read.
Scope and permissions
- The gate reads the migration files in the pull request. It never executes a migration and never writes to your database.
- The CI key is workspace-scoped and shown once — the hash is all we keep.
- Rotate the key in the dashboard whenever someone with access to your CI secrets leaves.
- Every call from the gate and its verdict land on the CI gate screen, so a build that failed silently shows up there instead of only in your provider's logs.
Gate your next migration.
Growth plan includes the CI gate for GitHub Actions and GitLab CI.
See plans →