CI/CD / DATABASE SCHEMA DRIFT DETECTION

Your code moved on.
Did your database?

Catch unexpected tables, columns, indexes, and constraints before a deployment proceeds. Use dbhydrate to compare an expected-state database with your target, then review the differences in your CI workflow.

1. Why Schema Drift Causes Outages

Database schema drift occurs when an environment’s actual schema catalog diverges from the expected state defined in your application codebase. Common culprits include:

  • Emergency Hotfixes: An engineer adds an index or alters a column directly on production during an incident and forgets to backport the change into migration scripts.
  • Out-of-Order Branch Merges: Two feature branches branch off main, apply conflicting DDL, and get deployed in reverse order.
  • Manual DBA Maintenance: Index rebuilds, vacuum tweaks, or table partition adjustments that were not documented in source control.

When your next deployment pipeline runs, application ORMs (like Prisma, Drizzle, Hibernate, or ActiveRecord) attempt to run migrations against unexpected column structures, resulting in failed transactions, locked tables, and customer downtime.

2. Deterministic Exit Codes in dbhydrate

The dbHydrate CLI is engineered specifically for non-interactive scripting. Every command returns standard POSIX exit codes so your CI runners can make clear pass/fail decisions:

Exit Code 0 : Clean parity. Schemas are identical (in sync).
Exit Code 1 : Drift detected! Schema differences found between source and target.
Exit Code 2 : Connection error. Database unreachable, bad credentials, or SSL failure.
Exit Code 3 : Safety gate violation. Target schema modified during execution or unconfirmed drop.
PIPELINE RECIPES

Interactive CI/CD Workflow Builder

Adapt these templates for a prepared macOS runner with dbhydrate installed and an approved same-engine comparison profile. Review flags, exit behavior, credentials, and trigger rules for your installed version.

.github/workflows/db-drift.ymlYAML
name: Database Schema Drift Gate
on:
  pull_request:
    branches: [main]
    paths:
      - 'migrations/**'
      - 'schema/**'

jobs:
  verify-schema:
    runs-on: [self-hosted, macOS]
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Check prepared dbhydrate installation
        run: dbhydrate --version

      - name: Check Schema Drift Against Staging
        env:
          DBHYDRATE_MASTER_KEY: ${{ secrets.DBHYDRATE_MASTER_KEY }}
          TARGET_DB_URL: ${{ secrets.STAGING_DATABASE_URL }}
        run: |
          # Configure branch protection and command exit behavior for your workflow
          dbhydrate compare --profile staging-sync

3. GitHub Actions Integration

Start with a manually triggered workflow on a prepared macOS runner. Configure your approved profile and review the installed command behavior before adding pull-request or deployment triggers:

name: Database Schema Review

on:
  workflow_dispatch:

jobs:
  compare:
    # Prepare dbhydrate, database access, and an approved profile on this runner.
    runs-on: [self-hosted, macOS]
    steps:
      - name: Check installed version
        run: dbhydrate --version
      - name: Compare the approved profile
        run: dbhydrate compare --profile ci-staging-check

4. GitLab CI Pipeline Example

Use a prepared macOS shell runner tagged macos and dbHydrate. Adapt the profile, credentials, and job policy before adding this example to .gitlab-ci.yml:

stages: [database-review]

schema_review:
  stage: database-review
  tags: [macos, dbhydrate]
  # Prepare dbhydrate, database access, and the comparison profile on this runner.
  script:
    - dbhydrate --version
    - dbhydrate compare --profile ci-staging-check
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

5. Automated PR Commenting

When dbhydrate compare catches differences, you can automatically post the planned synchronization DDL as a comment on the pull request. This lets database administrators and tech leads review the exact ALTER TABLE and CREATE INDEX statements before approving the PR.

6. Production Protection Controls

Even in headless CI environments, dbHydrate respects connection protection profiles:

  • Pre-Apply Snapshot: On high-protection targets, dbhydrate apply automatically takes a schema snapshot before executing DDL.
  • Destructive Statement Gates: If a sync plan contains DROP TABLE or DROP COLUMN, execution requires the explicit flag --allow-destructive to prevent accidental data loss.
  • Re-Check Target Parity: Immediately before running the first SQL statement, dbHydrate re-checks the target schema catalog. If another transaction changed the database, execution aborts with exit code 3.
AUTOMATION QUESTIONS

CI/CD integration FAQs.

How does dbhydrate detect schema drift in CI/CD workflows?

dbhydrate inspects the live system catalogs of your source database (for example, an expected-state database built from migration history) and target (e.g. pre-production or production DB). If any table structure, index, foreign key, or column definition differs, dbhydrate outputs a structured diff and terminates with exit code 1, immediately halting the CI/CD pipeline before bad changes deploy.

What deterministic exit codes does dbhydrate return?

dbhydrate returns standardized POSIX exit codes: 0 means schemas are completely in sync; 1 indicates drift was detected (differences found); 2 signifies a connection or authentication failure; and 3 means a safety policy gate failed (e.g., target schema changed during execution or destructive drop detected without override).

Can dbhydrate generate migration plans headlessly in CI?

Yes. With `dbhydrate plan --profile pr-check --out ./drift-plan.sql`, dbhydrate generates the complete, dependency-ordered synchronization DDL. You can attach this SQL file as an artifact or post it as a comment on pull requests for developer review.

How are database credentials secured in CI runners?

You can pass connection strings via standard environment variables (e.g. DBHYDRATE_TARGET_URL) using GitHub Actions Secrets or GitLab CI Masked Variables. Alternatively, you can use pre-configured profiles encrypted with AES-256-GCM unlocked via a single master password secret.