Driftfix documentation
Driftfix rewrites code that an API provider's breaking change has broken, checks the result with your own tests, and writes a pull request a reviewer can approve in a minute. Everything on this page runs locally; nothing is uploaded.
Install
Driftfix needs Python 3.10 or newer. Node.js is only needed to syntax-check JavaScript.
pip install git+https://github.com/Shashank4868/driftfix driftfix --version
For development, clone the repository and install it in editable mode with the test extras:
git clone https://github.com/Shashank4868/driftfix cd driftfix python -m venv .venv && . .venv/bin/activate pip install -e ".[dev]" pytest -q
Quick start
- See what's affected.
driftfix scan path/to/repolists every line a known breaking change touches. It edits nothing and exits with 1 when it finds something, so it works as a CI check. - Fix and verify.
driftfix run path/to/reporuns your tests, applies the edits and raises dependency pins, then runs the tests again. If anything gets worse, every file is restored. - Open the pull request. Add
--open-prto push the branch and open the PR with the GitHub CLI, or read the description Driftfix wrote toDRIFTFIX_PR.md.
$ driftfix run . ▸ Change OpenAI Python SDK 0.x -> >=1.0 ▸ Scan 3 Python files, 1 affected, 8 edits ▸ Tests before 3 failed APIRemovedInV1: v0 call removed in openai>=1.0 ▸ Patch app.py +8 -7 ▸ Verify checked 1 file(s), tests 3 passed ▸ Commit d0d3c0f on driftfix/openai-python-v1 ▸ Pull request ready DRIFTFIX_PR.md
Commands
driftfix changes
Lists the breaking changes Driftfix can migrate, with versions, release dates and changelog links. Add --json for machine-readable output.
driftfix scan [PATH]
| Option | What it does |
|---|---|
--change ID | Pick a migration. Default auto detects it from the code. |
--json | Every edit and review note as JSON, for scripts. |
--markdown | A Markdown report, for example for a CI job summary. |
driftfix run [PATH]
| Option | What it does |
|---|---|
--change ID | Pick a migration instead of detecting it. |
--skip-tests | Verify with syntax checks only. |
--no-deps | Leave dependency pins and lockfiles alone. |
--no-branch | Edit files in place without creating a branch or commit. |
--branch NAME | Branch name. Default driftfix/<change-id>. |
--open-pr | Push the branch and open the pull request with gh. |
--pr-out FILE | Where to write the pull request description. |
--python EXE | Interpreter for the repository's tests. Default: its .venv, else Driftfix's own. |
--json | A JSON report: status, edits, test results before and after, branch, commit, PR URL. |
Exit codes: 0 verified or nothing to do, 1 rolled back because verification failed.
GitHub Action
Report affected code on every pull request, with a table in the job summary:
name: Driftfix
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Shashank4868/driftfix@main
with:
fail-on-findings: true
Or let it open the fix on a schedule:
on:
schedule: [{ cron: "0 6 * * 1" }]
jobs:
fix:
runs-on: ubuntu-latest
permissions: { contents: write, pull-requests: write }
steps:
- uses: actions/checkout@v4
- uses: Shashank4868/driftfix@main
with:
mode: fix
| Input | Default | Meaning |
|---|---|---|
path | . | Directory to scan. |
change | auto | Migration id, or detect it. |
mode | scan | scan reports; fix patches, verifies and opens a PR. |
fail-on-findings | false | Fail the job when affected code is found. |
github-token | github.token | Token for pushing the branch and opening the PR. |
Outputs: edits (number of edits found) and pr-url (in fix mode).
Verification and rollback
- Syntax: every changed Python file is compiled, JavaScript passes
node --check, and TypeScript is checked for balanced brackets with strings and comments masked. - Tests:
pytestfor Python,npm testfor Node when dependencies are installed. They run before and after the patch. - Rollback: if the suite passed before and fails after, or gets more failures than it had, every edited file, dependency file and lockfile is restored and no pull request is written.
- Already broken code: a suite that was failing because of the removed API (for example
APIRemovedInV1) is expected to go green after the patch, and the PR says so.
The pull request
Each pull request has the same sections, so reviewers know where to look:
- What changed: one row per edit with file, line, rule, before and after.
- Dependencies: pins raised and how the lockfile was refreshed.
- Verification: syntax checks and test results before and after.
- Needs a human look: everything Driftfix found but would not guess at, with file and line.
OpenAI Python SDK 0.x → 1.x
Change id openai-python-v1. In openai 1.0.0 (November 2023) module-level calls were removed and raise APIRemovedInV1. Official migration guide.
| Before | After |
|---|---|
openai.ChatCompletion.create(...) | client.chat.completions.create(...) |
openai.ChatCompletion.acreate(...) | async_client.chat.completions.create(...) |
openai.Completion / Embedding / Moderation | client.completions / embeddings / moderations |
openai.Image.create | client.images.generate |
openai.Audio.transcribe / translate | client.audio.transcriptions / translations.create |
openai.api_key = ..., api_base, organization | client = OpenAI(api_key=..., base_url=..., organization=...) |
r["choices"][0]["message"]["content"] | r.choices[0].message.content |
openai.error.InvalidRequestError | openai.BadRequestError |
request_timeout= | timeout= |
openai==0.28.1 in requirements, pyproject, setup, Pipfile | openai>=1.0 |
Left for a human, with a note in the PR: .get() on a response, Azure's engine=, settings assigned inside a function, and files that import openai inside a function.
OpenAI Node.js SDK 3.x → 4.x
Change id openai-node-v4, for .js .mjs .cjs .jsx .ts .mts .cts .tsx. v4 replaced the axios-based OpenAIApi and Configuration classes with a single client and returns payloads directly. Official migration guide.
| Before | After |
|---|---|
import { Configuration, OpenAIApi } from 'openai' | import OpenAI from 'openai' (quotes and semicolons kept) |
new OpenAIApi(new Configuration({ apiKey })) | new OpenAI({ apiKey }) |
basePath: | baseURL: |
openai.createChatCompletion(...) | openai.chat.completions.create(...) |
createCompletion / createEmbedding / createModeration | completions / embeddings / moderations.create |
createImage / listModels / retrieveModel | images.generate / models.list / models.retrieve |
res.data.choices, const { data } = await ... | res.choices, const data = await ... |
let client: OpenAIApi | let client: OpenAI |
"openai": "^3.3.0" | "openai": "^4.104.0", lockfile refreshed with npm, pnpm or yarn |
Left for a human: streaming with responseType: "stream", a second axios options argument, error.response.status error handling, createTranscription, createFile and createFineTune, and v3 type imports such as ChatCompletionRequestMessage.
FAQ
Does my code leave my machine?
No. The CLI and the Action run where you run them. The website playground runs the same rules in your browser.
Why not just ask an LLM to migrate it?
Rules are deterministic, so the same input gives the same diff, every edit can be listed and reviewed, and anything uncertain is flagged instead of guessed. A model is a good fallback for the flagged cases, not for the bulk of the rewrite.
What if my repository has no tests?
Driftfix still syntax-checks every changed file and re-scans for leftover calls, and the PR says plainly that no test suite ran.
Which migration comes next?
Stripe API version upgrades and Google Ads API sunsets. Open an issue to ask for one.
Changelog
0.2.0 2026-11-03
- New migration: OpenAI Node.js SDK 3.x → 4.x for JavaScript and TypeScript.
- Dependency pins raised with the code; npm, pnpm and yarn lockfiles refreshed and restored on rollback.
- Import style kept, unwrapped constructors re-indented,
OpenAIApitypes renamed. - Function-local
import openaiis left for a human instead of hoisting a client. scan --markdownand the GitHub Action.
0.1.0
- OpenAI Python SDK 0.x → 1.x with libcst rules, tests before and after, rollback, branch, commit and PR description.
- Browser engine for the website, kept identical to the Python rules by a parity test.