Mixed file management (migrations cmds vs jinja) #2612
Replies: 1 comment 2 replies
|
I think your expectation is valid, and your implementation using a pre-update migration looks right, too. I've created a test case to debug this scenario: def test_update_migrate_poetry_group_to_dependency_groups(
tmp_path_factory: pytest.TempPathFactory,
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
src / "copier.yml": "_subdirectory: template/",
src / "template" / "{{ _copier_conf.answers_file }}.jinja": (
"{{ _copier_answers|to_yaml }}"
),
src / "template" / "pyproject.toml.jinja": (
"""\
[tool.poetry.group.dev.dependencies]
pytest = "*"
"""
),
}
)
git_save(src, tag="v1")
build_file_tree(
{
src / "copier.yml": (
"""\
_subdirectory: template/
_migrations:
- version: v2
when: "{{ _stage == 'before' }}"
command: "{{ _copier_python }} {{ _copier_conf.src_path / 'migrate_pep735.py' }}"
"""
),
src / "template" / "pyproject.toml.jinja": (
"""\
[dependency-groups]
dev = ["pytest"]
"""
),
src / "migrate_pep735.py": (
"""\
from pathlib import Path
import tomlkit
pyproject = Path("pyproject.toml")
doc = tomlkit.parse(pyproject.read_bytes())
deps = sorted(doc["tool"]["poetry"]["group"]["dev"]["dependencies"])
del doc["tool"]["poetry"]["group"]["dev"]
dep_groups = tomlkit.table()
dep_groups.add("dev", deps)
doc.add("dependency-groups", dep_groups)
pyproject.write_text(tomlkit.dumps(doc).lstrip())
"""
),
}
)
git_save(src, tag="v2")
run_copy(str(src), dst, vcs_ref="v1")
git_save(dst, "init")
pyproject = dst / "pyproject.toml"
pyproject.write_text(pyproject.read_text() + 'mypy = "*"\n')
git_save(dst, "add mypy in poetry group")
run_update(dst, overwrite=True, unsafe=True)
assert pyproject.read_text() == snapshot(
"""\
[dependency-groups]
dev = ["mypy", "pytest"]
"""
)On the current + <<<<<<< before updating
+ [tool.poetry.group.dev.dependencies]
+ pytest = "*"
+ mypy = "*"
+ =======
[dependency-groups]
- dev = ["mypy", "pytest"]
? --------
+ dev = ["pytest"]
+ >>>>>>> after updatingThis is indeed more than I'd expect. Adding [dependency-groups]
+ <<<<<<< before updating
dev = ["mypy", "pytest"]
+ =======
+ dev = ["pytest"]
+ >>>>>>> after updatingI think this conflict cannot be avoided because the same line in That said, I think it shouldn't be necessary to run [dependency-groups]
+ <<<<<<< HEAD
dev = ["mypy", "pytest"]
+ =======
+ dev = ["pytest"]
+ >>>>>>> copier/after-updatingDoes this analysis make sense to you, @OverkillGuy? |
Uh oh!
There was an error while loading. Please reload this page.
I can't seem to find out how to mix
_migrationscmds (used during major upgrade), while keeping the regular upgrade/reconciliation loop of other non-major updates.I worry I'd have to
_excludethe entire file to migrate, loosing the latter, in order for the former to work.Example
I'm using copier to manage the lifecycle of my
pyproject.tomlfile, at theroot of a python project. In v1, this file was a poetry file. Template's v2
"upgrades" this to use uv instead. Same file different syntax/tool.
The nicest way to upgrade from v1 to v2 (poetry -> uv) is to run
uvx migrate-to-uv, so what I want to have is:v2, use the normal expansionSo I wrote it as a migration task:
But this poses a few problems to me (may be because I misunderstand the tool?):
cmd-result conflicts with jinja-expand
The result of v2 template expansion is clashing with the result of v1->v2 migration tasks, making a merge conflict where the "before" is result of migration task, and "after" is the result of from-scratch v2 template expansion of the file.
The result is this pointless merge-conflict exercise where I have to teach the user to blindly "take ours" to fix this conflict but ONLY in case of v2, ONLY for this one file. It's a bad user experience.
Any chance I am just misunderstanding the tool/a flag?
And the second issue is a consequence of me trying to work around this:
Disabling the update altogether via _exclude means missing on all minor updates
From what I see of the docs, it seems like the solution to avoid this merge conflict, is to make the file
_excluded, like this:But then it means that updates like
v1.3 -> v1.4, orv2.1 -> v2.2would completely ignore the minor, non-cmd, templatedpyproject.tomlchanges.This feels wrong, like I'm giving up a major part of copier's power just to get migrations to work?
The current track I was exploring while reporting here was maybe update the Jinja filter to say something like "if operation is update and major before = v1 and major after = v2: exclude pyproject.toml"?
But this still feels hacky?
So, in summary:
How is mixed (migration cmds vs jinja) file lifecycle management supposed to work?
I'm sure others have bumped into it, and figured this out, so how do y'all manage the lifecycle of files that mostly work from regular jinja upgrades, but major bumps require broader commands that need to ignore jinja for just this one round?
I tried going over the docs all over again, and this kind of workflow wasn't clearly outlined (beyond
_migrationsand_excludebeing listed individually).If it's unclear to others too, I'd absolutely volunteer to document this workflow once I understand it better, if it helps anyone else.
All reactions