Skip to content

CI/CD Integration

Add envdrift to your CI/CD pipeline to catch drift before deployment.

GitHub Actions

# .github/workflows/validate-env.yml
name: Validate Environment

on:
  push:
    branches: [main]
    paths:
      - '.env.*'
      - 'config/**'
  pull_request:
    paths:
      - '.env.*'
      - 'config/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install envdrift
        run: pip install envdrift

      - name: Validate production env
        run: |
          envdrift validate .env.production \
            --schema config.settings:ProductionSettings \
            --ci

      - name: Check encryption
        run: |
          envdrift encrypt .env.production --check

      - name: Guard for plaintext secrets
        run: |
          envdrift guard --ci --fail-on high

      - name: Diff dev vs prod
        run: |
          envdrift diff .env.development .env.production --format json

GitLab CI

# .gitlab-ci.yml
validate-env:
  stage: test
  image: python:3.11-slim
  script:
    - pip install envdrift
    - envdrift validate .env.production --schema config.settings:ProductionSettings --ci
    - envdrift encrypt .env.production --check
    - envdrift guard --ci --fail-on high
  only:
    changes:
      - .env.*
      - config/**

Pre-commit Hooks

Local validation before commits:

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: envdrift-validate
        name: Validate env schema
        entry: envdrift validate --ci --schema config.settings:Settings
        language: system
        files: ^\.env\.(production|staging|development)$
        pass_filenames: true

      - id: envdrift-encryption
        name: Check env encryption
        entry: envdrift encrypt --check
        language: system
        files: ^\.env\.(production|staging)$
        pass_filenames: true

      - id: envdrift-guard
        name: Guard staged env files
        entry: envdrift guard --staged --native-only --ci
        language: system
        always_run: true
        pass_filenames: false

envdrift-guard is the config-aware safety net. Keep it enabled for custom [vault.sync].mappings.env_file names that do not match .env*.

Install:

pip install pre-commit
pre-commit install

Exit Codes

Exit codes differ by command, so handle them per command in CI.

For validate and encrypt --check:

Exit Code Meaning
0 Validation passed
1 Validation failed (missing vars, type errors, etc.)

guard uses graduated exit codes based on the highest-severity finding (at or above the --fail-on threshold in CI mode), plus dedicated codes for an incomplete scan and operational errors:

Exit Code Meaning
0 No blocking findings (none at/above --fail-on)
1 Critical severity findings detected
2 High severity findings detected
3 Medium severity findings detected
4 Low severity findings detected (policy violations)
5 Scan incomplete: a selected scanner ran but failed
6 Operational error (bad config, invalid path or flags)

A run in which a selected scanner errored never exits 0: if nothing blocks the run but a scanner failed, guard exits 5 so CI fails closed instead of passing on a scan that never completed.

Because guard does not collapse failures to a flat 1, CI scripts that key off its result should test for any non-zero exit ($? -ne 0) rather than $? -eq 1.

Multi-Environment Validation

Validate all environments in one job:

jobs:
  validate:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - env: development
            settings_class: DevelopmentSettings
          - env: staging
            settings_class: StagingSettings
          - env: production
            settings_class: ProductionSettings
    steps:
      - uses: actions/checkout@v4

      - name: Install envdrift
        run: pip install envdrift

      - name: Validate ${{ matrix.env }}
        run: |
          envdrift validate .env.${{ matrix.env }} \
            --schema config.settings:${{ matrix.settings_class }} \
            --ci

Drift Detection in PRs

Comment on PRs with drift report:

- name: Check drift
  id: drift
  run: |
    envdrift diff .env.development .env.production --format json > drift.json

- name: Comment on PR
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const drift = JSON.parse(fs.readFileSync('drift.json', 'utf8'));

      if (drift.summary.has_drift) {
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: `## Environment Drift Detected\n\n` +
                `- Added: ${drift.summary.added}\n` +
                `- Removed: ${drift.summary.removed}\n` +
                `- Changed: ${drift.summary.changed}`
        });
      }