Getting started¶
1. Install¶
2. Create a .bluefox dotfile¶
The .bluefox file tracks which files the upgrade engine manages and what version the project is on.
from bluefox_upgrade import create_dotfile, full_tracked, structured_tracked, sections_tracked
tracked = dict([
full_tracked("Dockerfile"),
sections_tracked("app/__init__.py", ["bluefox:routers"]),
structured_tracked("pyproject.toml"),
])
dotfile = create_dotfile(project_dir, version="0.1.0", tracked=tracked)
Tracking strategies¶
| Managed | What it tracks | Conflict detection |
|---|---|---|
full | Entire file | SHA-256 hash of full contents |
sections | Marked sections only | SHA-256 hash of full file |
structured | Logical fields (e.g. dependencies) | No hash — always safe to update |
3. Define an upgrade¶
from bluefox_upgrade import Upgrade, ReplaceFile, BumpDependency, AddDependency
upgrade = Upgrade(
from_version="0.1.0",
to_version="0.2.0",
description="Upgrade to v0.2.0",
operations=[
ReplaceFile(path="Dockerfile", template="dockerfile_v2.jinja", description="update base image"),
BumpDependency(name="fastapi", old_spec=">=0.115", new_spec=">=0.120"),
AddDependency(name="httpx", spec=">=0.27"),
],
)
4. Plan and execute¶
from bluefox_upgrade import plan_upgrade, check_conflicts, execute_upgrade
# Plan: builds the operation list, collapses redundant ops
plan = plan_upgrade(dotfile, [upgrade], target="0.2.0")
# Check: detect files the user has modified since last upgrade
conflicts = check_conflicts(plan, project_dir, dotfile)
if conflicts:
for c in conflicts:
print(f"Conflict: {c.conflict.message}")
# Execute: apply operations
result = execute_upgrade(plan, project_dir, dotfile, render_template)
print(f"Applied {len(result.applied)} operations")
5. Conflict modes¶
The executor supports three modes when it encounters user-modified files:
# Default: abort on first conflict
result = execute_upgrade(plan, project_dir, dotfile, render_template)
# Skip: preserve user edits, skip conflicting operations
result = execute_upgrade(plan, project_dir, dotfile, render_template, skip_conflicts=True)
# Force: backup originals, then overwrite
result = execute_upgrade(plan, project_dir, dotfile, render_template, force=True)
In force mode, originals are saved to .bluefox-backups/<timestamp>/.
6. Template rendering¶
The executor takes a render_template callable. You provide the implementation:
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates"))
def render_template(template_name: str) -> str:
return env.get_template(template_name).render()
result = execute_upgrade(plan, project_dir, dotfile, render_template)
Next steps¶
- Operations reference — all seven operation types
- Multi-step upgrades — chaining and collapsing
- Conflict resolution — handling user edits