driftcheck
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, causingdriftcheckto exit with code1. 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.tomlchannelandCargo.tomlrust-versionvsREADME.md/docs/README*.md/CONTRIBUTING*.md— minor-aware (patch differences ignored) - Cargo features:
Cargo.toml[features]vs explicitfeatures = [...]lists and--featuresflags in README - npm workspaces: same dependency declared at different ranges across workspace
package.jsonfiles - Node:
package.jsonengines.nodevs README - Bun:
package.jsonengines.bunvs README — major.minor comparison - Package version:
package.jsonversionvs explicit npm badge URLs, install commands, and changelog headers in README/docs - Python:
pyproject.tomlrequires-pythonvs README - Python tool targets:
[tool.ruff],[tool.black],[tool.mypy],[tool.pyright]vsrequires-python - Python (legacy setup):
setup.py/setup.cfgpython_requiresand explicitinstall_requirespins vs README/CONTRIBUTING - Go:
go.modgodirective vs README - PHP:
composer.jsonrequire.phpvs README — major.minor comparison - Ruby:
Gemfileruby "x.y.z"directive vs README — major.minor comparison - .NET/C#:
*.csproj<TargetFramework>vs README — handles multi-targeting - Elixir:
mix.exselixir:version vs README - Kotlin:
build.gradle.ktsplugin version vs README - Swift:
Package.swiftswift-tools-versionand dependency pins vs README - Dart/Flutter:
pubspec.yamlenvironment.sdkconstraint vs README - Deno:
deno.jsonversion pins vs README - Python Version:
.python-versionvspyproject.tomlrequires-pythonfloor
Package managers & lockfiles:
- Pipfile:
PipfilevsPipfile.lockversion mismatches - Conda:
environment.ymlunpinned packages - Gradle Version Catalog:
libs.versions.tomlvs README - Kotlin Multiplatform (KMP):
gradle/libs.versions.tomlKMP 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.locknixpkgs pins vs README mentions - Engines:
package.jsonenginesfield consistency across package managers - Requirements:
requirements.txtunpinned 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.ymlimage tags vs README - CircleCI:
.circleci/config.ymldocker image tags vs README - Jenkins:
Jenkinsfiletool versions (nodejs,python,docker.image) vs README - CI OS: deprecated GitHub Actions runners (ubuntu-18.04, macos-11, windows-2016)
Infrastructure:
- Docker:
DockerfileFROM <image>:<tag>vs README - Docker Bases: floating/unpinned Dockerfile base images
- Docker Multistage: multi-stage Dockerfile
FROMconsistency across stages - Docker Compose:
docker-compose.yml/compose.yamlimage tags vs README - Kubernetes: image tags in manifests vs README
- Helm:
Chart.yaml/values.yamlimage tags vs README - Terraform:
versions.tfrequired_providersblockversionvs README - Environment drift:
.env.examplevs.env,docker-compose.ymlvsdocker-compose.prod.yml,values.yamlvsvalues.prod.yaml
Build tools:
- Bazel:
.bazelversion,MODULE.bazelbazel_dep, andWORKSPACE.bazelhttp_archivepins vs README - Makefile: tool version variables (
GCC_VERSION,CMAKE_VERSION,GO_VERSION, etc.) - CMake:
CMakeLists.txtcmake_minimum_requiredversion vs README - Maven:
pom.xmljava.version,maven.compiler.source,maven.compiler.target,releasevs README - Java/Gradle:
build.gradlesourceCompatibility,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"orpython = {version = "3.12"}) - Version files:
.ruby-version,.python-version,.node-version,.java-version,.terraform-versionvs README - EditorConfig:
.editorconfigindent_size/indent_stylevs project convention - Devcontainer:
.devcontainer/devcontainer.jsonimage/tags vs README - Renovate:
renovate.jsonconfiguration drift vs README - NVMRC:
.nvmrcvspackage.jsonengines.node (informational) - Dependabot: ecosystems used but not covered by
.github/dependabot.yml(informational) - SARIF output:
driftcheck --sarifgenerates 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=lfin.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:
.npmrcregistry vs README mentions - Yarn RC:
.ymlYarn version vs README mentions - PNPM workspace:
pnpm-workspace.yamlpackages vspackage.jsonworkspaces - Git Tag: latest git tag vs README version mentions — detects when README references a stale version
- Pre-commit:
.pre-commit-config.yamlrev: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.
Related tools
- 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.49
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| driftcheck_py-0.1.49.tar.gz | 228.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| driftcheck_py-0.1.49-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 410.6 kB
Release files / driftcheck_py-0.1.49.tar.gz
| Download URL | driftcheck_py-0.1.49.tar.gz |
|---|---|
| Size | 228.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
40a90e3cd386ac2f370788bc4a457d49a1bc7a3bd6e76f4ad17a43a641e95f5a
|
|
BLAKE2b-256 checksum How to use checksums |
ac2c4387876907b3eb850ee8ce2795655d877c29c09921a9f108368538c03323
|
| 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 logRelease files / driftcheck_py-0.1.49-py3-none-any.whl
| Download URL | driftcheck_py-0.1.49-py3-none-any.whl |
|---|---|
| Size | 182.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
efc223609ace0425427bef119aa79158888e7aaeb7476632c497a774443ce95f
|
|
BLAKE2b-256 checksum How to use checksums |
e4205ac862feb166294bef6b3361f33290f0f372e7207eb2968b399b4e8fc30b
|
| 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