Multi-step upgrades¶
When a project is multiple versions behind, the upgrade engine chains all intermediate upgrades and collapses redundant operations.
Defining a chain¶
Each Upgrade specifies from_version and to_version. The planner walks the chain automatically.
upgrades = [
Upgrade(from_version="0.1.0", to_version="0.2.0", description="v0.2.0", operations=[
ReplaceFile(path="Dockerfile", template="dockerfile_v2.jinja", description="v2"),
BumpDependency(name="fastapi", old_spec=">=0.115", new_spec=">=0.118"),
AddDependency(name="httpx", spec=">=0.25"),
]),
Upgrade(from_version="0.2.0", to_version="0.3.0", description="v0.3.0", operations=[
ReplaceFile(path="Dockerfile", template="dockerfile_v3.jinja", description="v3"),
BumpDependency(name="fastapi", old_spec=">=0.118", new_spec=">=0.120"),
BumpDependency(name="httpx", old_spec=">=0.25", new_spec=">=0.27"),
]),
Upgrade(from_version="0.3.0", to_version="0.4.0", description="v0.4.0", operations=[
ReplaceFile(path="Dockerfile", template="dockerfile_v4.jinja", description="v4"),
BumpDependency(name="fastapi", old_spec=">=0.120", new_spec=">=0.125"),
]),
]
# Project is at 0.1.0, target is 0.4.0
plan = plan_upgrade(dotfile, upgrades, target="0.4.0")
What gets collapsed¶
The planner collects all operations from the chain and collapses them:
- 3 ReplaceFile("Dockerfile") → 1 (keeps the last template:
dockerfile_v4.jinja) - 3 BumpDependency("fastapi") → 1 (first
old_specto lastnew_spec:>=0.115→>=0.125) - AddDependency("httpx") + BumpDependency("httpx") → 1 AddDependency with final spec (
>=0.27)
Result: 3 operations instead of 8.
Error handling¶
The planner raises UpgradeError if:
- No path exists from the current version to the target
- The version chain contains a cycle
- The current version matches the target (nothing to do)