-
-
Notifications
You must be signed in to change notification settings - Fork 240
feat: Add postrender tasks phase #2389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Add new `Phase.POSTRENDER` that executes after template rendering but before diff calculation during updates. This enables template authors to define tasks that transform rendered output (e.g., Java package refactoring) without needing `.jinja` extensions on every file, while maintaining correct 3-way merge semantics. Key changes: - Add `Phase.POSTRENDER` enum to execution phases - Templates declare tasks in `copier.yml` using `_postrender_tasks` with support for conditional execution and custom working directories - Execute postrender tasks in copy and update flows (on old_copy, new_copy, and destination) - Add `_update_stage` variable to distinguish between previous, current, and new rendering contexts during updates - Add test coverage and documentation
|
Sorry for the delay, @rcoup! 🫣 Before getting into code review: Have you considered avoiding the |
|
Yeah, I saw that option. I'm already using a mix of verbatim files & Jinja templates by the time I have config & CI & docs and other bits. I think dropping the explicit |
|
And why do you consider bulk rename/replace better (simpler, more manageable, ...?) than templating directory and package names? I'd argue that the latter is more robust / less error-prone and involves less "magic" than maintaining a bulk rename/replace script? I mean, I imagine such a script needs to walk the file tree, and the non-Jinja placeholders need to be unique to avoid false matches. 🤔 |
|
Thanks for getting back to me :-) For my setup, there's a mix of:
My initial implementation was to have While this technically works, it's unwieldy to make changes to the template package — every new My substitution postrender task is really simple - it renames the two Solving it with |
|
Thanks for elaborating on your scenario so clearly! 🥇 I understand your point that the Java syntax will be broken by the Jinja markup and that linters etc. won't work. I might not consider it as big a problem as you, but I acknowledge that this is probably a matter of opinion and your position seems valid, too. I've finally found some time to think about this feature request and PR more thoroughly – sorry for the delay. I think the current behavior of tasks is (almost) what you need, as tasks are executed right after template rendering. Copier's current update algorithm generates a fresh copy in a temporary location from the old template version using a regular If the new update algorithm with regular tasks offers a solution for your use case, it still means you're dependent on tasks which is an unsafe feature, so template users need to pass the package:
type: str
_middleware: |
{%- if _copier.middleware.event == 'create_path' -%}
{%- if _copier.middleware.path.match('src/*/java/com/example/boilerplate') -%}
{{ _copier.middlware.path.parent / package }}
{%- endif -%}
{%- endif -%}We'd need to define the available events, context data, and expected "return" values of such a middleware. WDYT? |
|
@sisp thanks for your detailed reply
My task script checks for directories existing/not, but yes, this is a gotcha of sorts. "Don't do that" is ok for internal uses, but I agree isn't perfect.
Interesting — I'll have a read of this and do some experimenting, then get back to you. |
Problem
A Copier template for Java applications requires every Java file to use
.jinjaextensions solely to template package names fromcom.example.boilerplatetocom.example.{{package}}. Ideally we'd have a way to keep most template files as plain.javawith a fixedcom.example.boilerplatepackage (src/{test,main}/java/com/example/boilerplate/), then execute a simple refactoring script at copy/update time to rename package directories (eg:boilerplate/→myservice/) and bulk-replace across files (eg:com.example.boilerplate.tocom.example.myservice.).Copier's existing task system runs after the diff calculation during updates, which breaks 3-way merge semantics. The diff compares
boilerplatein the template vsmyservicein the user's project, making changes undetectable.Solution
Solving this in a generic way (since directory-per-package isn't anything Java-specific), add a new
Phase.POSTRENDERthat executes after template rendering but before diff calculation during updates. This enables template authors to define tasks that transform rendered output without needing.jinjaextensions on every file, while maintaining the 3-way merge semantics.Key changes
Phase.POSTRENDERenum to execution phasescopier.ymlusing_postrender_taskswith support for conditional execution and custom working directories_update_stagevariable to distinguish between previous, current, and new rendering contexts during updatesComments/suggestions most welcome!