Inspiration

Every team has a requirements file full of pins nobody dares touch. Patch bumps are automated. Major versions sit for months, because a breaking upgrade means reading changelogs, rewriting call sites and hoping the tests notice. That's exactly the work that should happen after code: maintenance nobody wants, done safely.

What it does

Semver Surgeon is a GitLab Duo custom flow that carries a breaking upgrade from detection to merge to production: • Dep-watch: a CI job reads the manifest, finds the most valuable major upgrade and fires the flow's trigger. • Triage: a Duo agent checks for duplicates and opens an issue explaining why the upgrade breaks. • Migrator: a Duo agent rewrites the code and pins, installs the new version, runs the project's tests in its own environment and opens a merge request. • Verifier: a Duo agent checks that the named CI jobs passed and labels the MR ready. • Merge gate: a deterministic CI job, not the model, decides. It checks the pipeline, the ready label and that every commit came from the flow's service account, then arms auto-merge with its own token. The agent can never merge. • Deploy: the merged change ships through stage, smoke test and promote to a live Oracle Cloud server. • Reporter: a Duo agent writes a migration ledger entry.

Proof on a real open-source codebase: on a public mirror of satnogs-db (Libre Space Foundation, Django), the flow found django-csp 3.8 to 4.0. That upgrade is dangerous: with the old settings, 4.0 serves every page with no Content-Security-Policy header, and no error or warning. The flow opened MR !2 on its own, rewrote six flat CSP_* settings into the new CONTENT_SECURITY_POLICY dict, moved all three pin files, and ran 56 tests on django-csp 4.0. The gate first REFUSED it (job 17486287107) because of a real identity bug, and nothing merged. After the fix it APPROVED (job 17487504727): merged as 6d74afa2, issue #1 closed, and the post-merge pipeline was green on django-csp 4.0. No human edited the code.

Proof all the way to production: on its own repo, MR !6 upgraded uvicorn 0.34.0 to 0.54.0, was auto-merged by the gate (merge 2bc77b05) and deployed in pipeline 2953088693. The app is live at https://158.180.57.167.sslip.io/healthz.

How we built it

• GitLab Duo Agent Platform: a custom flow registered in the AI Catalog, started by a pipeline-event trigger, with agent tooling in .gitlab/duo/agent-config.yml • GitLab CI/CD: dep-watch, unit tests, lint, SAST, dependency scanning, secret detection, the merge gate and the Oracle deploy chain • Python, with 273 tests that also check the CI rules themselves • Oracle Cloud Always Free ARM VM, Docker and Caddy for HTTPS • A CI/CD component template (templates/semver-surgeon.yml) with spec:inputs for the watched file, an allowlist, test and lint commands, stages and a deploy switch

Challenges we ran into

• Catalog flow schema limits: we had to drop several keywords and routers to get the flow accepted. • Composite identity: the MR is authored by the user while commits come from the flow's service account, whose address suffix varies. The first live gate run refused on exactly that, so we moved to an anchored pattern match. • Gate integrity: the gate now runs the target branch's script, so a merge request can't rewrite the gate that judges it. • Trigger semantics: only human-started pipelines fire flows, so the flow ignores push-triggered sessions and reuses a blocked issue on retry instead of deduplicating forever. • Testing inside the agent: we gave the agent PyPI access so it installs the new version and runs the real tests, with a CI-first fallback. • Deploying to an ARM VM over a locked-down network.

Accomplishments that we're proud of

• A real migration on a real open-source codebase, merged through the gate with green CI before and after • A gate that refused when something was wrong, a refusal that was a decision, not an error • Autonomous merges on our own repo (MR !2, then MR !6) that went on to production • Deduplication proven: re-running the flow doesn't open a second issue for the same upgrade • Finding the silent CSP regression in django-csp 4.0 that only manage.py check catches

What we learned

Agents are good at the migration. Policy should still live in code they can't touch, and a gate that says no is worth as much as one that says yes.

What's next for Semver Surgeon

• Publish the component to the CI/CD Catalog and prove one-include adoption on a fresh project • Offer the satnogs-db django-csp migration upstream • Let the gate post its decision as an MR note, not just in the job log • Django 5.2 as the next migration target

Built With

Share this project:

Updates

Submission history