Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

COBOL → Python Modernization Demo (Manufacturing Plant Stores)

A repeatable, end-to-end demo for mainframe modernization: a legacy IBM COBOL + DB2 program is migrated to a modern Python FastAPI + Vue app, driven by a CLI that embeds the GitHub Copilot CLI, follows Test-Driven Development, swaps DB2 → Azure SQL, and deploys to Azure (Web App + Function App).

The sample keeps database configuration external to the application. It can use an existing Azure SQL database through an application setting, or SQLite for local development and automated tests, without changing application code.

What's in the box

legacy/            Legacy mainframe source (the input)
  STOREOPS.cbl       COBOL program: stock enquiry / issue / receive / add / reorder
  copybook/          PARTREC.cpy record layout
  db2/schema.sql     Original DB2 DDL (PART_MASTER, STOCK_TXN)

migrator/          The migration CLI that embeds GitHub Copilot CLI
  cobol_migrator/    analyze | migrate (TDD) | test | deploy | all

app/               The modernized application (migration output)
  backend/           FastAPI + SQLAlchemy 2.0 + Pydantic v2, pytest (TDD)
  functions/         Azure Functions (timer + HTTP) reorder batch
  frontend/          Vue 3 + Vite SPA (built into backend/app/static)

infra/             Bicep (App Service, Function App, optional Azure SQL, App Insights)
azure.yaml         Azure Developer CLI (azd) manifest

How the migration maps COBOL → FastAPI

COBOL paragraph Modern module
2000-STOCK-ENQUIRY GET /api/parts/{part_number}
3000-ISSUE-PART (insufficient-stock guard) POST /api/parts/{n}/issue
4000-RECEIVE-PART POST /api/parts/{n}/receive
5000-ADD-PART (duplicate guard) POST /api/parts
6000-REORDER-REPORT GET /api/parts/reorder-report + Function App
7000-WRITE-TRANSACTION repository._write_txn (STOCK_TXN insert)
COMMIT / ROLLBACK SQLAlchemy session commit/rollback

Test-Driven Development (the core of this project)

The hardest question in mainframe modernization is not "can we rewrite it?" — it's "how do we prove the new system behaves exactly like the old one?" This project answers that with strict TDD: the tests are the migration contract. We capture each COBOL business rule (and every edge case) as an executable test first, then build the Python to satisfy it. Green tests = the rule survived.

Red → Green → Refactor

  1. RED — write a test that states a rule; run it; it fails (no code yet). A failing test first proves the test actually checks something.
  2. GREEN — write the minimum code to make it pass. No production code exists unless a failing test demanded it.
  3. REFACTOR — clean up with the tests still green.

The migration is driven by migrator/cobol_migrator/prompts.py, whose prompt forces Copilot through these phases and ships an explicit TEST MATRIX of edge cases (boundaries, validation, and rollback paths).

Two layers of tests

Layer File Purpose
API / behaviour app/backend/tests/test_store.py Full HTTP contract: status code, response body, and resulting DB state
Business rules (unit) app/backend/tests/test_repository.py (generated for new migrations) Pure rule checks on the repository

The rules under test (each cites its COBOL paragraph)

Business rule COBOL Test asserts
Look up a part 2000-STOCK-ENQUIRY 200 + record; unknown → 404
Issue reduces stock 3000-ISSUE-PART stock −qty, one STOCK_TXN (I) written
Can't oversell 3000-ISSUE-PART qty > on‑hand → 409, stock unchanged, no txn written
Boundary: qty == on‑hand 3000-ISSUE-PART allowed, drives stock to exactly 0
Bad quantity validation 0 / negative / missing → 422
Receive adds stock 4000-RECEIVE-PART stock +qty, one STOCK_TXN (R) written
No duplicate parts 5000-ADD-PART existing number → 409, no overwrite
Field limits validation part≤10 / desc≤40 / loc≤8 → 422; status defaults to A
Reorder filter 6000-REORDER-REPORT on‑hand ≤ point and active only
Boundary: on‑hand == point 6000-REORDER-REPORT included; point+1 excluded
Obsolete excluded 6000-REORDER-REPORT O/H parts excluded even when low
Transactional integrity 9000-COMMIT-AND-EXIT a failed op rolls back part and txn together

Run the tests

cd app/backend
pip install -r requirements.txt -r requirements-dev.txt
pytest                       # the TDD contract, runs in < 5s, no Azure needed
pytest --cov=app --cov-report=term-missing   # coverage report

Tests are hermetic — they run against an isolated SQLite database per test (see tests/conftest.py), so there is no dependency on Azure SQL and the suite is fast and deterministic for CI. The shipped suite covers the app package at ~88%; the migration prompt enforces a ≥95% gate on the suites it generates.

Why a reviewer should care

  • Faithful by construction — behaviour is pinned by tests, not by hope.
  • Edge cases are first-class — boundaries, validation, and rollback are encoded, not left to manual QA.
  • Model-agnostic — the same TDD prompt works with any model via --model.
  • CI-ready — the migration prompt sets a coverage gate (fail-under=95) so generated suites block regressions.

The CLI (embeds GitHub Copilot)

pip install -e migrator

# Read-only modernization brief of the COBOL
cobol-migrate analyze --model claude-sonnet-4.5

# TDD migration: Copilot writes failing tests first, then the implementation
cobol-migrate migrate --model claude-sonnet-4.5

# See the exact Copilot command + prompt without calling the model
cobol-migrate migrate --dry-run

# Run the migrated backend's pytest suite
cobol-migrate test

# Provision + deploy to Azure
cobol-migrate deploy

# migrate -> test -> deploy
cobol-migrate all --model claude-sonnet-4.5

Under the hood it runs the Copilot CLI non-interactively: copilot -C <repo> --model <model> --allow-all-tools -p "<TDD migration prompt>". Swap --model for any model (e.g. gpt-5, auto) — "a model of our choice".

Run locally

# Backend + tests
cd app/backend
pip install -r requirements.txt -r requirements-dev.txt
pytest                      # 9 passing (the TDD contract)
python -m app.seed          # sample parts
uvicorn app.main:app --reload

# Frontend (dev, proxies /api to :8000)
cd app/frontend
npm install
npm run dev

The DB2 → Azure SQL connection swap

app/backend/app/config.py resolves the database in this order:

  1. DATABASE_URL (full SQLAlchemy URL) — used by CI/tests.
  2. AZURE_SQL_CONNECTIONSTRING (ODBC) — the swap; used in Azure. Set this app setting to an Azure SQL connection string and the app targets Azure SQL with no code change.
  3. Local SQLite fallback — so everything runs with zero infrastructure.

Provision a demo Azure SQL server with the infra by setting DEPLOY_AZURE_SQL=true (azd env set DEPLOY_AZURE_SQL true) in a subscription whose policy allows it. The App Service startup.sh installs the ODBC Driver 18 only when the connection string is present.

Deploy it yourself

azd auth login
azd env new storeops-dev
azd env set AZURE_SQL_ADMIN_PASSWORD <StrongPassword>
azd up

About

Copilot-powered COBOL/DB2 to Python FastAPI/Vue modernization CLI with TDD and Azure deployment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages