Skip to content

Add cookieplone update command to refresh projects against newer template versions #176

@ericof

Description

@ericof

Summary

Add a cookieplone update command that refreshes an already-generated project against the latest version of the template it was created from, preserving the user's local changes and surfacing conflicts for manual review.

Today, once a user runs cookieplone and scaffolds a project, the generated tree is frozen in time. Any improvement that later lands in the upstream template — bugfixes, new CI steps, dependency bumps, new boilerplate files, tightened security defaults — is invisible to existing projects unless the user manually diffs upstream and copies edits over. That is both tedious and error-prone, and in practice means real projects drift further and further away from the templates that spawned them.

A first-class update command would let template authors ship improvements knowing downstream consumers can actually adopt them, and let downstream users benefit from ecosystem-wide fixes with a single command.

User experience

From within a previously generated codebase:

cookieplone update

Cookieplone should:

  1. Discover which template the project was generated from (via the __template__ key persisted in .cookieplone.json, see write_answers() does not persist __template__ key in .cookieplone_answers JSON file #168).
  2. Re-resolve the upstream repository at the current default branch or tag.
  3. Replay the original answers from the answers file against the latest template.
  4. Compute a three-way diff between:
    • the old rendered template (the snapshot the project was born from),
    • the new rendered template (what the current upstream would produce with the same answers),
    • the current project on disk (including the user's local edits).
  5. Apply the new-vs-old diff to the working tree, leaving conflict markers where a hunk overlaps user changes — the same mental model as git merge.
  6. Report a summary: files added, files removed, files updated cleanly, files with conflicts.

Additional flags to consider:

  • --answers <path> — override the answers file location (default: the .cookieplone_answers_*.json in the project root).
  • --tag <ref> — update against a specific tag/branch/commit of the template repository instead of the default.
  • --dry-run — print the diff without writing anything.
  • --skip <glob> — never touch matching paths (useful for generated lockfiles, .env, etc.).
  • --interactive — prompt per-file before applying changes.

Prior art

This is the same problem that cruft solves on top of plain Cookiecutter. The cruft model (store a reference to the template + the exact commit/version + the answers, and use a three-way merge on update) works well in practice and is worth studying as a reference implementation.

Cookieplone has two advantages over a generic cruft integration:

Proposed scope

In scope (first cut)

  • A new cookieplone update subcommand.
  • Discovery of the source template from the answers file written by the original run.
  • Re-rendering with the original answers against the latest template.
  • Three-way merge of the old render, the new render, and the working tree.
  • Conflict marker output (git-style) for hunks the merge cannot apply cleanly.
  • Summary report at the end of the run.
  • Support for the --tag, --dry-run, and --skip flags described above.

Out of scope (track separately)

  • Re-prompting the user for new schema fields that did not exist in the original run. (Start by assuming every new field uses the schema's default; add an interactive prompt pass later.)
  • Automated answer migration (for example, renaming a question between template versions).
  • Updating sub-templates separately from the parent project.
  • Cross-template updates (switching from one template to another).
  • A cookieplone status command that reports how far a project has drifted from its template without applying any changes. (Nice as a follow-up.)

Open questions

  1. Answers file naming. Today the answers file lives as .cookieplone_answers_<folder_name>.json. Should cookieplone update assume exactly one such file at the project root, or should it accept a path explicitly?
  2. Template pinning. Should the answers file also record the exact template commit/tag at generation time, so update knows the "old" side of the three-way merge without having to guess? This would be a small addition to write_answers() and makes the three-way merge reliable.
  3. Binary and generated files. How should the merge handle binary files, lock files, and files flagged in _copy_without_render?
  4. Integration with extends (Implement the repository extension mechanism (extends) #175). Once the repository extension mechanism lands, update has to handle the case where a project was generated from a downstream repo that inherits from an upstream — updates should walk both layers.

Acceptance criteria

  • cookieplone update works end-to-end against a project generated with the current version of Cookieplone.
  • A three-way merge produces conflict markers for hunks that overlap user edits.
  • --dry-run shows the planned changes without writing anything.
  • --tag lets the caller update to a specific template ref.
  • --skip honours user-provided globs.
  • The command reports a structured summary (added / removed / updated / conflicted files).
  • Integration tests cover: clean update, update with user edits, update with conflicts, update after a new schema field was added upstream, and update with --tag pinning.
  • Documentation:
    • New how-to guide: "Update a project against a newer template version".
    • Reference entry for the new subcommand in docs/src/reference/cli.md.
    • Concept page explaining the three-way merge model and conflict handling.
  • News fragment under news/ (type: feature).

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions