Skip to content

✨ Feature: Template Upgrade System for Blake #32

@matt-goldman

Description

@matt-goldman

Overview (TL;DR)

Upgrading templates in most SSGs is manual and brittle. Blake should make it safe, predictable, and boring to stay current: no DSLs, no arcane scripts. Template authors ship small, declarative "hops" (A→B). The CLI finds your current version, walks the hops sequentially to the target version, applies simple text/file operations, and creates a branch and generates an upgrade report. If there’s no supported upgrade, the CLI tells you so gracefully.

Why this sets Blake apart: users can actually upgrade without fear, and template authors can evolve templates confidently.


Principles

  • No DSLs or scripting: A small, fixed set of declarative operations
  • Work with existing tools: Use the project’s existing .csproj for template identity (i.e. no lock files or other bespoke solutions)
  • Safety first: Branch by default, dry-run support, stash user-diverged files instead of overwriting.
  • Idempotent where possible: Re-running an upgrade shouldn’t duplicate changes.

High-level idea

  • Template metadata lives in the site’s .csproj when a site is created:

    • BlakeTemplate.Id (e.g., blake/docs@github:owner/repo)
    • BlakeTemplate.Version (semantic version)
    • BlakeTemplate.Commit (optional, for traceability)
  • Hop files live in the template repo under .blakeupgrade/ and describe a single version step {from}→{to}.

  • Operations supported in hop files (keep it tiny but useful):

    1. addText (anchor-based insert, before/after/start/end)
    2. replaceText (string or regex match replacement)
    3. addFile (copy in if missing)
    4. replaceFile (whole-file replace, with guard hash and conflict strategy)

CLI behavior

blake upgrade [--to <version>] [--dry-run] [--no-branch]

  1. Detect current template ID/version from .csproj.
  2. Discover latest (or requested) target version in the template repo.
  3. Build a sequential path of hops (e.g., 0.4→0.5, 0.5→0.6, 0.6→0.7).
  4. Create a branch (e.g., blake-upgrade-v0.7.0) unless --no-branch.
  5. Apply ops hop-by-hop. On conflicts, stash user files and continue.
  6. Emit a human-readable report in .blake/upgrade_reports/….

When no upgrade path exists

  • If hop files for the required path are missing, show:

    • clear message: “No supported upgrade path from vX to vY for template Z”
    • suggest: check template repo or upgrade manually
    • non-error exit code (unless --strict), so it fails gracefully

Dry-run

  • Print the plan and a per-op preview diff where possible. No files changed.

Template metadata (in .csproj)

Authors of site templates ensure sites record template identity on creation:

<PropertyGroup>
  <BlakeTemplate_Id>github:owner/repo</BlakeTemplate_Id>
  <BlakeTemplate_Version>0.4.0</BlakeTemplate_Version>
  <BlakeTemplate_Commit>1a2b3c4</BlakeTemplate_Commit>
</PropertyGroup>

We deliberately don’t introduce a lock file; we stay in the ecosystem’s existing project file.


Hop file shape (example)

/.blakeupgrade/0.4.0→0.5.0.json

{
  "from": "0.4.0",
  "to": "0.5.0",
  "ops": [
    {
      "op": "addText",
      "file": "src/_Imports.razor",
      "anchor": "using Markdig;",
      "position": "after",
      "text": "using Blake.Docs;",
      "ensureOnce": true
    },
    {
      "op": "replaceText",
      "file": "tailwind.config.cjs",
      "match": "content: \\[.*?\\]",
      "regex": true,
      "with": "content: [\"**/*.razor\", \"**/*.md\"]",
      "fallback": "skip"
    },
    {
      "op": "replaceFile",
      "fromTemplate": "files/_Layout.razor",
      "to": "Pages/_Layout.razor",
      "guards": {
        "expectedBaseVersionHash": "sha256:abc123…",
        "onMismatch": "stash"   
      }
    }
  ]
}

Notes

  • ensureOnce avoids duplicate inserts across re-runs.
  • fallback can be skip or error if a regex doesn’t match.
  • expectedBaseVersionHash detects divergence from the old template file and triggers stashing.

Safety defaults

  • Branch-first: create a working branch by default.
  • Stash on mismatch: for replaceFile, move user-diverged files to .blake/upgrade_stash/... and report.
  • No deletes: hop files never remove user files; authors can document removals as manual steps.
  • Fail-fast with context: stop on hard errors; show how to resume.

Future enhancements (optional, not required for MVP)

  • GitHub Action that auto-generates hop files on template release (diff previous tag → current tag) and opens a PR.
  • Structured ops (opt-in): JSON Patch for configs, XML node setters for .csproj.
  • Interactive mode: prompt per op when ambiguity is detected.

MVP scope & steps

  1. Define and publish the JSON schema for hop files (docs, examples).
  2. Build the op engine in the Build Tools (parser, validator, executor, report).
  3. Expose the upgrade command in the CLI
  4. Read template identity from .csproj; resolve hops and apply sequentially.
  5. Implement --dry-run, branch creation, stash and report.
  6. Document authoring guidance for template maintainers.

Out of scope (for now)

  • Automated file deletions or mass renames.
  • Arbitrary shell/script execution from hop files.
  • Handling binary merges.

Success criteria

  • A site created from template Z v0.4.0 can run blake upgrade --to 0.7.0 and:

    • ends with updated files matching template intent;
    • preserves (or stashes) user changes safely;
    • produces a clear, human‑readable report;
    • exits non‑fatally with guidance if no hop path exists.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions