Skip to content

Commit 4619347

Browse files
authored
Support migrating and updating existing projects (#69)
- Fix outdated documentation - Overwrite existing files when renaming - Fix typo in README - Copy the replay file into the repo - Add instructions to migrate and update existing projects
2 parents 0556d15 + 887323d commit 4619347

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

README.md

+71-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ If offers:
1313

1414
[Cookiecutter]: https://cookiecutter.readthedocs.io/en/stable
1515

16-
## Start a new projects
16+
## Start a new project
1717

1818
To start a new project you should first [install
1919
Cookiecutter](https://cookiecutter.readthedocs.io/en/stable/installation.html).
@@ -55,3 +55,73 @@ python3 -m venv .venv
5555
pip install .[dev-noxfile]
5656
nox
5757
```
58+
59+
## Migrating an existing project
60+
61+
The easiest way to migrate an existing project is to just generate a new one
62+
basing all the inputs in the current project metadata and then overwritting the
63+
existing files.
64+
65+
It is recommended to commit all changes before doing this, so you can then use
66+
`git` to look at the changes.
67+
68+
If you generate the new repo in a temporary directory, you can easily overwrite
69+
the files in your existing project by using `rsync` or similar tools:
70+
71+
```sh
72+
cd /tmp
73+
cookiecutter gh:frequenz-floss/frequenz-repo-config-python --directory=cookiecutter
74+
rsync -r new-project/ /path/to/existing/project
75+
cd /path/to/existing/project
76+
git diff
77+
# Fix all the `TODO`s and cleanup the generated files
78+
git commit -a
79+
```
80+
81+
!!! warning
82+
83+
The trailing slash in `new-project/` and the lack of it in
84+
`/path/to/existing/project` are meaningful to `rsync`.
85+
86+
## Update an existing project
87+
88+
To update an existing project you can use the [Cookiecutter *replay
89+
file*](https://cookiecutter.readthedocs.io/en/stable/advanced/replay.html) that
90+
was saved during the project generation. The file is saved in
91+
`.cookiecutter-replay.json`. Using this file you can re-run Cookiecutter
92+
without having to enter all the inputs again.
93+
94+
!!! warning
95+
96+
Don't forget to commit all changes in your repository before doing this!
97+
Files will be overwritten!
98+
99+
```sh
100+
git commit -a # commit all changes
101+
cd ..
102+
cookiecutter gh:frequenz-floss/frequenz-repo-config-python \
103+
--directory=cookiecutter \
104+
--force \
105+
--replay \
106+
--replay-file project-directory/.cookiecutter-replay.json
107+
```
108+
109+
This will create a new commit with all the changes to the overwritten files.
110+
Bear in mind that all the `TODO`s will come back, so there will be quite a bit
111+
of cleanup to do. You can easily check what was changed using `git show`, and
112+
you can use `git commit --amend` to amend the previous commit with the template
113+
updates, or create a new commit with the fixes. You can also use `git citool`
114+
or `git gui` to easily add, remove or even discard (revert) changes in the
115+
templates update commit.
116+
117+
!!! note
118+
119+
The `project-directory` is the directory of your previously generated
120+
project. If you renamed it, then the files will be generated in a new
121+
directory with the original name. You can update the target directory in
122+
the replay file.
123+
124+
!!! note
125+
126+
Please remember to keep your replay file up to date if you change any
127+
metadata in the project.

cookiecutter/hooks/post_gen_project.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def finish_setup() -> None:
9292
"""
9393
was_repo_initialized = initialize_git_repo()
9494

95+
copy_replay_file()
96+
9597
remove_unneeded_files()
9698

9799
match cookiecutter.type:
@@ -110,6 +112,22 @@ def finish_setup() -> None:
110112
commit_git_changes(first_commit=was_repo_initialized)
111113

112114

115+
def copy_replay_file() -> None:
116+
"""Copy the replay file to the project root."""
117+
src = _pathlib.Path("~/.cookiecutter_replay/cookiecutter.json").expanduser()
118+
dst = _pathlib.Path(".cookiecutter-replay.json")
119+
if not src.exists():
120+
print(f"WARNING: No replay file found in {src}. Skipping...")
121+
122+
try:
123+
_shutil.copyfile(src, dst)
124+
except (OSError, IOError) as error:
125+
print(
126+
f"WARNING: Error copying the replay file {src} -> {dst} ({error}). "
127+
"Skipping..."
128+
)
129+
130+
113131
def initialize_git_submodules() -> bool:
114132
"""Initialize git submodules.
115133
@@ -357,12 +375,13 @@ def finish_api_setup() -> None:
357375
358376
* Rename `src` to `py`
359377
* Rename `tests` to `pytests`
360-
* Create `submodules` folder
361-
* Create `.gitmodules` file
362-
* Initialize submodules
363378
"""
364-
_pathlib.Path("src").rename("py")
365-
_pathlib.Path("tests").rename("pytests")
379+
# We can't do a simple rename because the target directory might not be empty if
380+
# overwriting an existing project.
381+
_shutil.copytree("src", "py", dirs_exist_ok=True)
382+
_shutil.rmtree("src")
383+
_shutil.copytree("tests", "pytests", dirs_exist_ok=True)
384+
_shutil.rmtree("tests")
366385

367386

368387
def finish_app_setup() -> None:

0 commit comments

Comments
 (0)