How To Create a Template Based on an Existing Project
- Take a copy of your project, because obviously you don't want to lose your work :D
-
To create a template, cookiecutter needs two things:
- The folder/file structure
- a cookiecutter.json file which stores the configuration of the project. To achieve this, rename the project root folder to “{{cookiecutter.project}}” and create “cookiecutter.json” in the folder.
-
Configure: cookiecutter.json example:
Feel free to add any additional variables if you like. You will be prompted to enter the above fields when you try to create a repository from the template that you're building now -
Folder structure: {{cookiecutter.project}}
- inside the {{cookiecutter.project}} folder, create the following files:
README.md, pyproject.toml
README.mdexamplepyproject.tomlexample- Push the template to your Bitbucket/github repository for future use
- Example command to download and use your template
cookiecutter git@bitbucket.org:jacksonlaboratory/software-templates.git --directory="python-poetry-cli" - Additionally if you want to change a folder name in the project use jinja2 templating i.e., name the folder {{cookiecutter.variable_name}} and add the default variable_name to the cookiecutter.json file.
- You can also use the cookiecutter variables in the python files if you want to configure any content
example__init__.py:
# -*- coding: utf-8 -*-
__author__ = '{{cookiecutter.full_name}}'
__email__ = '{{cookiecutter.email}}'
__version__ = '{{cookiecutter.version}}'
Using pre & post hooks
You can have Python or Shell scripts that run before and/or after your project is generated.
Put them in hooks folder like this:
├── {{cookiecutter.project}}/
├── hooks
│ ├── pre_gen_project.py
│ └── post_gen_project.py
└── cookiecutter.json
Pre generate hook example:
import re
import sys
MODULE_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]+$'
module_name = '{{ cookiecutter.module_name }}'
if not re.match(MODULE_REGEX, module_name):
print('ERROR: %s is not a valid Python module name!' % module_name)
# exits with status 1 to indicate failure
sys.exit(1)
The above example validates the module name using the MODULE_REGEX before generating the project
Post generate hook example (with jinja2 if conditions)
import os
import sys
REMOVE_PATHS = [
'{% if cookiecutter.packaging != "pip" %} requirements.txt {% endif %}',
'{% if cookiecutter.packaging != "poetry" %} poetry.lock {% endif %}',
]
for path in REMOVE_PATHS:
path = path.strip()
if path and os.path.exists(path):
if os.path.isdir(path):
os.rmdir(path)
else:
os.unlink(path)
The above example deletes any unwanted files if they are in the project folder after the cookiecutter generates the project
Note: When the hook scripts are run, their current working directory is the root of the generated project