Skip to main content

driftcheck

CI Release Python License Stars

Detect version drift between docs and toolchain files.

README.md says Rust 1.93.0 but rust-toolchain.toml pins 1.96.1? CONTRIBUTING.md says Node 18 but package.json engines says 24? go.mod says 1.23 but docs say 1.21? driftcheck catches it before your contributors hit a build failure.

pip install git+https://github.com/yunaremaia/driftcheck.git
driftcheck           # scan current repo
driftcheck --json    # machine-readable
driftcheck --fix     # auto-fix drifts in documentation files
driftcheck --sarif   # SARIF 2.1.0 output for GitHub Code Scanning
driftcheck --csv     # CSV output for spreadsheets/data pipelines
driftcheck --list-detectors  # show available detectors
driftcheck --only tool_versions_drifts  # run specific detectors
driftcheck --exclude nvmrc_drifts,lockfile_drifts  # exclude detectors
driftcheck --quiet   # only output drifts, suppress OK
driftcheck --no-informational  # skip informational drifts
driftcheck --version

GitHub Action

Add driftcheck to your CI with a single step:

- uses: yunaremaia/driftcheck@main
  with:
    fail-on-drift: true   # default
    args: "--no-informational"

Or with SARIF upload for GitHub Code Scanning:

- uses: yunaremaia/driftcheck@main
  with:
    sarif: true
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: driftcheck.sarif

SARIF output includes an originalUriBaseIds.repoRoot file:// URI and repo-relative artifact paths tagged with uriBaseId: "repoRoot", so code scanning can resolve findings correctly when driftcheck runs from a subdirectory.

Reusable Workflow (Zero-Config CI)

For the simplest setup, use driftcheck's reusable workflow — no YAML to write in your repo:

# .github/workflows/driftcheck.yml in YOUR repo
name: Driftcheck
on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]

jobs:
  driftcheck:
    uses: yunaremaia/driftcheck/.github/workflows/driftcheck.yml@main
    with:
      fail-on-drift: true
      detectors: 'docker,compose,github-actions,node,python,rust'

With SARIF upload for GitHub Code Scanning alerts:

jobs:
  driftcheck:
    uses: yunaremaia/driftcheck/.github/workflows/driftcheck.yml@main
    with:
      fail-on-drift: true
      output-format: sarif
      sarif-upload: true
    permissions:
      contents: read
      security-events: write

CSV Export

Output drift findings as CSV — useful for spreadsheets, data pipelines, and CI artifact collection:

driftcheck --csv                     # CSV output to stdout
driftcheck --csv > drifts.csv        # save to file for CI artifact
driftcheck --csv --no-informational  # exclude informational drifts

Output columns: file, detector, doc_version, actual_version, severity, message.

Markdown Report

Generate a markdown summary for CI job summaries or PR comments:

driftcheck --report          # output markdown to stdout
driftcheck --report >> $GITHUB_STEP_SUMMARY  # post to GitHub Actions

The report includes a statistical summary at the top:

## driftcheck report

### 📊 Summary

- **Total drifts:** 3
  - ❌ Blocking: 2
  - ℹ️  Informational: 1
- **Detectors fired:** 2
  - `node`: 2
  - `lockfile`: 1
- **Top files:**
  - `README.md`: 2 drift(s)

### ❌ Blocking drifts

**Node.js package.json engines vs README** (node_drifts):
- `README.md`: Node 18 → should be 20

Initialize Config

Generate a starter .driftcheck.toml:

driftcheck --init            # creates .driftcheck.toml with examples

Configuration (.driftcheck.toml)

Place a .driftcheck.toml file in your repo root to customize detection:

[driftcheck]
# Exclude specific detectors (supports short names or drift keys)
exclude_detectors = ["lockfile", "nvmrc", "ci_os"]

# Treat informational drifts as blocking
fail_on_informational = false

# Custom doc paths — additional files to scan for version mentions
# Supports glob patterns (e.g., "docs/*.md")
doc_paths = ["docs/setup.md", "CHANGELOG.md"]

By default, informational drifts (such as missing lockfiles or orphaned configurations) are reported as non-blocking warnings and exit with code 0.

  • --fail-on-informational: Promotes informational drifts to blocking errors, causing driftcheck to exit with code 1. Useful in strict CI pipelines where you want to enforce rules like missing lockfiles or missing .gitattributes (* text=auto eol=lf).
  • --no-informational: Suppresses informational drifts from the output entirely.

You can also use CLI flags --only and --exclude to filter detectors at runtime.

Git-Mode (Incremental Scans)

For large repositories, scan only the detectors relevant to your latest changes:

# Only check files changed since the last commit
driftcheck --git-mode

# Compare against a specific base (e.g., main branch)
driftcheck --git-mode --git-base origin/main

This maps changed file paths to detector patterns (e.g., a package.json change runs only the Node.js and npm-related detectors) and skips the rest. Useful in CI where the full scan is overkill for a targeted PR.

CI/CD Integration

driftcheck is designed to run as a quality gate in CI pipelines. It exits with code 1 when blocking drifts are found, making it easy to fail a build or PR check.

Exit codes:

  • 0 — no blocking drifts (clean)
  • 1 — blocking drifts detected

GitHub Actions (full scan):

- name: Run driftcheck
  run: driftcheck --report >> $GITHUB_STEP_SUMMARY

GitHub Actions (PR comparison against main):

- uses: actions/checkout@v5
  with:
    fetch-depth: 0  # full history for git-base comparison

- name: Driftcheck PR
  run: |
    driftcheck --git-mode --git-base origin/main --report >> $GITHUB_STEP_SUMMARY

Comparing against a specific tag:

driftcheck --git-mode --git-base v1.0.0

Checks (v0.1.47):

Language runtimes:

  • Rust: rust-toolchain.toml channel and Cargo.toml rust-version vs README.md / docs/README*.md / CONTRIBUTING*.md — minor-aware (patch differences ignored)
  • Cargo features: Cargo.toml [features] vs explicit features = [...] lists and --features flags in README
  • npm workspaces: same dependency declared at different ranges across workspace package.json files
  • Node: package.json engines.node vs README
  • Bun: package.json engines.bun vs README — major.minor comparison
  • Package version: package.json version vs explicit npm badge URLs, install commands, and changelog headers in README/docs
  • Python: pyproject.toml requires-python vs README
  • Python tool targets: [tool.ruff], [tool.black], [tool.mypy], [tool.pyright] vs requires-python
  • Python (legacy setup): setup.py / setup.cfg python_requires and explicit install_requires pins vs README/CONTRIBUTING
  • Go: go.mod go directive vs README
  • PHP: composer.json require.php vs README — major.minor comparison
  • Ruby: Gemfile ruby "x.y.z" directive vs README — major.minor comparison
  • .NET/C#: *.csproj <TargetFramework> vs README — handles multi-targeting
  • Elixir: mix.exs elixir: version vs README
  • Kotlin: build.gradle.kts plugin version vs README
  • Swift: Package.swift swift-tools-version and dependency pins vs README
  • Dart/Flutter: pubspec.yaml environment.sdk constraint vs README
  • Deno: deno.json version pins vs README
  • Python Version: .python-version vs pyproject.toml requires-python floor

Package managers & lockfiles:

  • Pipfile: Pipfile vs Pipfile.lock version mismatches
  • Conda: environment.yml unpinned packages
  • Gradle Version Catalog: libs.versions.toml vs README
  • Kotlin Multiplatform (KMP): gradle/libs.versions.toml KMP catalog vs README badges (kotlin, coroutines, compose, AGP, KSP)
  • Lockfile: missing, stale, or orphaned lockfiles (package-lock.json, yarn.lock, Cargo.lock, go.sum, Gemfile.lock, composer.lock, poetry.lock, uv.lock) (informational)
  • package-lock integrity: package.json dependency ranges vs package-lock.json v1/v2/v3 resolved versions; flags missing direct dependencies and semver range violations
  • Nix: flake.lock nixpkgs pins vs README mentions
  • Engines: package.json engines field consistency across package managers
  • Requirements: requirements.txt unpinned packages vs known latest

CI/CD:

  • GitHub Actions: outdated uses: action@version — compares against known latest versions for 18 popular actions; detects deprecated Node 20 runtime
  • GitLab CI: .gitlab-ci.yml image tags vs README
  • CircleCI: .circleci/config.yml docker image tags vs README
  • Jenkins: Jenkinsfile tool versions (nodejs, python, docker.image) vs README
  • CI OS: deprecated GitHub Actions runners (ubuntu-18.04, macos-11, windows-2016)

Infrastructure:

  • Docker: Dockerfile FROM <image>:<tag> vs README
  • Docker Bases: floating/unpinned Dockerfile base images
  • Docker Multistage: multi-stage Dockerfile FROM consistency across stages
  • Docker Compose: docker-compose.yml/compose.yaml image tags vs README
  • Kubernetes: image tags in manifests vs README
  • Helm: Chart.yaml/values.yaml image tags vs README
  • Terraform: versions.tf required_providers block version vs README
  • Environment drift: .env.example vs .env, docker-compose.yml vs docker-compose.prod.yml, values.yaml vs values.prod.yaml

Build tools:

  • Bazel: .bazelversion, MODULE.bazel bazel_dep, and WORKSPACE.bazel http_archive pins vs README
  • Makefile: tool version variables (GCC_VERSION, CMAKE_VERSION, GO_VERSION, etc.)
  • CMake: CMakeLists.txt cmake_minimum_required version vs README
  • Maven: pom.xml java.version, maven.compiler.source, maven.compiler.target, release vs README
  • Java/Gradle: build.gradle sourceCompatibility, jvmTarget, JavaVersion.VERSION_* vs README

Configuration:

  • Tool versions: .tool-versions (asdf/mise) — detects drift for Node, Python, Go, Rust, Ruby, Java, PHP, .NET
  • Mise: mise.toml [tools] section vs README — supports string and dict specs (e.g., node = "22" or python = {version = "3.12"})
  • Version files: .ruby-version, .python-version, .node-version, .java-version, .terraform-version vs README
  • EditorConfig: .editorconfig indent_size/indent_style vs project convention
  • Devcontainer: .devcontainer/devcontainer.json image/tags vs README
  • Renovate: renovate.json configuration drift vs README
  • NVMRC: .nvmrc vs package.json engines.node (informational)
  • Dependabot: ecosystems used but not covered by .github/dependabot.yml (informational)
  • SARIF output: driftcheck --sarif generates SARIF 2.1.0 for GitHub Code Scanning

Security:

  • Typosquat: suspicious package names similar to popular packages
  • package-lock integrity: blocking supply-chain check for package-lock resolutions outside declared package.json ranges

Other:

  • Line endings: missing * text=auto eol=lf in .gitattributes (informational)
  • External resources: third-party CDN dependencies that break offline rendering (informational)
  • Count: skills/ directory count vs README mentions of "N skills"
  • Plugins: custom drift detection via .driftcheck_plugins/ directory
  • NPMRC: .npmrc registry vs README mentions
  • Yarn RC: .yml Yarn version vs README mentions
  • PNPM workspace: pnpm-workspace.yaml packages vs package.json workspaces
  • Git Tag: latest git tag vs README version mentions — detects when README references a stale version
  • Pre-commit: .pre-commit-config.yaml rev: vs README mentions of pre-commit version

Plugins

driftcheck supports plugins for custom drift detection. Create a .driftcheck_plugins/ directory in your repo root and add Python files that define a register() function:

# .driftcheck_plugins/my_detector.py
import re

def register():
    return {"my_detector": find_my_drift}

MY_RE = re.compile(r'my_tool\s+(?P<ver>\d+\.\d+)')

def find_my_drift(root, docs):
    drifts = []
    for fname, content in docs.items():
        for m in MY_RE.finditer(content):
            drifts.append({
                "file": fname,
                "doc_version": m.group("ver"),
                "detail": f"my_tool {m.group('ver')} mentioned",
            })
    return drifts

Plugin results appear as plugin_<name>_drifts in JSON output and are printed in the CLI. Broken plugins are skipped with a warning — they won't crash driftcheck.

Pre-commit hook

driftcheck ships a pre-commit hook that limits work to detectors relevant to the files staged for the next commit:

repos:
  - repo: https://github.com/yunaremaia/driftcheck
    rev: v0.1.47
    hooks:
      - id: driftcheck
        args: ["--no-informational"]

The published hook uses --changed-only, which reads the staged git diff and runs only relevant detectors. You can use the same mode directly:

driftcheck --changed-only

To print the hook manifest entry for tooling or inspection:

driftcheck pre-commit

Or install pre-commit locally:

pip install pre-commit
pre-commit install

Stats

  • 85 detector modules covering 50+ toolchains and file formats
  • 74 registered drift keys (including split environment detectors, lockfile variants, plugin system, and A2A protocol drift detection)
  • 1511 tests with >95% code coverage
  • SARIF 2.1.0 output for GitHub Code Scanning
  • Plugin system for custom detectors
  • Pre-commit hook support

Python API

See the API Reference for embedding driftcheck in scripts and CI pipelines.

Detector Comparison Matrix

Ecosystem Detector Drift Key Example Detected
.NET dotnet dotnet_drifts .NET csproj TargetFramework
C/C++ cmake cmake_drifts CMakeLists.txt cmake_minimum_required
C/C++ makefile makefile_drifts Makefile tool version pins (CC, CMAKE, GO)
CI/CD actions-outdated gh_actions_version_drifts GitHub Actions outdated versions
CI/CD ci-os ci_os_drifts Deprecated CI runner (e.g., ubuntu-20.04)
CI/CD circleci circleci_drifts CircleCI config.yml image
CI/CD gitlab gitlab_drifts GitLab CI image tag
CI/CD jenkins jenkins_drifts Jenkinsfile tool versions (nodejs, python, docker)
Docker compose dc_drifts Docker Compose image
Docker compose-override compose_override_drifts Docker Compose override file image drift
Docker docker docker_drifts Dockerfile FROM tag
Docker docker-bases docker_bases_drifts Floating/unpinned base images
Docker docker-multistage docker_multistage_drifts Multi-stage Dockerfile conflicting tags
Editor/IDE editorconfig editorconfig_drifts .editorconfig vs README/IDE indent
Editor/IDE vscode-ext vscode_ext_drifts VSCode extensions.json vs README recommendations
Elixir elixir elixir_drifts Elixir mix.exs version
Environment env env_drifts .env.example vs .env, compose overrides
Environment env-example env_example_drifts .env.example vs .env key drift
General count count_drifts Skills directory count vs README
General dart dart_drifts Dart pubspec.yaml SDK constraint
General deno deno_drifts Deno deno.json version field
General dependabot dependabot_drifts Dependabot coverage gaps (informational)
General external external_resource_drifts External CDN resources in HTML (informational)
General lineending lineending_drifts Missing .gitattributes line ending config
General lockfile lockfile_drifts Lockfile missing/stale/orphaned (informational)
General mise mise_drifts mise.toml tool versions
General nvmrc nvmrc_drifts .nvmrc vs package.json engines (informational)
General package-manager package_manager_drifts packageManager field vs lockfile
General swift swift_drifts Swift Package.swift version pins
General taskfile taskfile_drifts Taskfile.yml tool versions
General tool-versions tool_versions_drifts .tool-versions asdf/mise
Git git-tag git_tag_drifts Latest git tag vs README version mentions
Go go go_drifts Go go.mod directive
Gradle gradle java_drifts Gradle build.gradle sourceCompatibility
Gradle gradle-catalog gradle_catalog_drifts Gradle Version Catalog (libs.versions.toml)
Helm helm helm_drifts Helm Chart.yaml/values.yaml
Helm helm-values helm_values_drifts Helm values.yaml vs environment-specific values
Java java-version java_version_drifts .java-version vs README
Kotlin kotlin kotlin_drifts Kotlin build.gradle.kts plugin version
Kotlin kotlin-multiplatform kmp_drifts Kotlin Multiplatform (KMP) version catalog drift
Kubernetes k8s k8s_drifts Kubernetes manifest image
Maven maven maven_drifts Maven pom.xml java.version
Node actions-node20 actions_drifts GitHub Actions Node 20 deprecation
Node bun bun_drifts Bun package.json engines.bun
Node node node_drifts Node.js package.json engines
Node node-version node_version_drifts .node-version vs README
Node npmrc npmrc_drifts .npmrc vs package.json settings
Node pnpm pnpm_workspace_drifts pnpm-workspace.yaml vs package.json workspaces
Node yarnrc yarnrc_drifts .yarnrc.yml Yarn version
PHP php php_drifts composer.json require.php
Python conda conda_drifts Conda environment.yml pinned versions
Python pipfile pipfile_drifts Pipfile vs Pipfile.lock version mismatches
Python poetry poetry_drifts Poetry pyproject.toml [tool.poetry] dependencies
Python python python_drifts Python pyproject.toml requires-python
Python python-version python_version_drifts .python-version vs README
Python requirements requirements_drifts requirements.txt package versions
Ruby ruby ruby_drifts Gemfile ruby directive
Ruby ruby-version ruby_version_drifts .ruby-version vs README
Rust rust-cargo rust_drifts Rust Cargo.toml rust-version
Rust rust-toolchain drifts Rust toolchain.toml channel
Security typosquat typosquat_drifts Typosquat detection in dependencies (informational)
AI Agents a2a a2a_drifts A2A agent card spec_version vs docs, capabilities, endpoints (informational)
Documentation changelog changelog_drifts CHANGELOG.md presence/content vs CONTRIBUTING.md policy (informational)
Terraform terraform terraform_drifts Terraform versions.tf provider
Terraform terraform-version terraform_version_drifts .terraform-version vs README

Sponsoring / Treasury

driftcheck is MIT licensed and maintained in the open. If it saves you time, you can support continued development through GitHub Sponsors or the Solana treasury below.

Funding details are declared in .github/FUNDING.yml, which is what GitHub reads to render the Sponsor button on this repository.

  • GitHub Sponsors: @yunaremaia
  • Solana: Eeztv1nCYUt1fwGWpzKC948gaWfjejYCAuLtUMgzDWbW

Use the Solana address only for intended donations. Anyone can generate a similar address, so verify the address against .github/FUNDING.yml before sending funds.

If this tool is useful to you, a star helps other people find it.

  • taintrace — trace and inspect AI agent execution
  • depscan — scan dependencies across multiple ecosystems
  • agentcost — track and attribute LLM spend per agent
  • mcp-guard — audit MCP servers for unsafe permissions

Part of a family of focused, single-purpose developer tools — each one does one thing and does it well.

Metadata

Release files for driftcheck-py 0.1.50

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for driftcheck-py 0.1.50
File Size Uploaded
driftcheck_py-0.1.50.tar.gz 231.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for driftcheck-py 0.1.50
File Interpreter ABI Platform
driftcheck_py-0.1.50-py3-none-any.whl Python 3 none any Details

Total release size: 414.0 kB

Release files / driftcheck_py-0.1.50.tar.gz

Download URL driftcheck_py-0.1.50.tar.gz
Size 231.6 kB
Tags Source
SHA-256 checksum
How to use checksums
38763d1fa79423354c468b0e4577c6dd267bb5fd3790ad2fd3d34cdaa020105a
BLAKE2b-256 checksum
How to use checksums
b842b5788fa62c7ce62f8bb762858abacd59e6ea31a8834fd1127ce935c18f97
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / driftcheck_py-0.1.50-py3-none-any.whl

Download URL driftcheck_py-0.1.50-py3-none-any.whl
Size 182.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5d4efbbd9fe229660d89a76858bea5918be272b21e6690c68e8b4828a9ca6bf3
BLAKE2b-256 checksum
How to use checksums
5ea6476dc312f91febc6c4dbfd8141dcb7de9269a211deab22a3798fd0f7fba7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.50 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page