Automate the creation of repositories

One thing that comes across as particularly cumbersome when you are at the very early start of creating a new python project is to create the structure of the repository.

It always looks the same and never is the most interesting part of the project:

.
├── Makefile
├── README.md
├── your_project_name_in_snake_case
│   ├── __init__.py
│   └── main.py
├── poetry.toml
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_dummy.py

The good news is that it can be automated!

The objective is to create the structure using a bash script, wrapped up into a bash command accessible globally.

I am showing you how.

Objective

Whenever you want to init the structure for a new python project, you want the solution to work as follow:

  1. Move to the root of the new project;

  2. There, hitting a create repo kind of command that creates the whole structure (README.md, Makefile, .gitignore, etc.) for you when triggered.

Workflow

(1) Create the .gitignore files and templates for the files to be created:

E.g. you can find a template for the .gitignore on the Internet (you can even ask ChatGPT).

(2) Create a bash script to coordinate the creation of the different elements:

I called mine .create-poetry-repo.sh. It contains a set of usual instructions.

For instance, it creates a tests/ folder:

mkdir tests/
touch tests/__init__.py

and populates it with a minimal test suit:

cat > tests/test_dummy.py << EOF
def test_dummy():
    assert True
EOF

(3) The script is configurable, it asks the user via a prompt for the project’s name:

echo -n "project name (camel-case): "
read project_name_camel_case

This collected variable is then reused across the script e.g. to create the README.md.

cat > README.md << EOF
# $project_name_camel_case
EOF

For some use cases, the variable must be converted into snake case first:

project_name_snake_case=${project_name_camel_case//-/_}

It can the be used – e.g. to create the Makefile:

mkdir $project_name_snake_case
touch $project_name_snake_case/__init__.py

(4) The logic (bash script and template files such as the .gitignore) are then stored on a single source of truth i.e. a Github repository;

More can be done. Once you are satisfied with the logic you have encapsulated, you can upload your .sh script and your templates (e.g. the .gitignore file) on a Github repository.

Usage

Now, whenever you want to create a new python project:

(1) Download the documents (e.g. .gitignore) and bash script from the remote Github repository using the curl command:

    zsh> curl -OL <local_filename> https://raw.githubusercontent.com/<user>/<project>/<branch>/<path_to_remote_filename>

Notes:

  • after the .sh script has been downloaded, you must change the set of permissions to execute it. chmod +x <executable_filename> usually does the job.
  • if the visibility of your github repo is set on private, you will have to use a personal token after you have created it from the developer settings: github.com/settings/tokens; then use it in the arguments of the curl command:
curl -H "Authorization: token $GITHUB_API_REPO_TOKEN" \
     -H "Accept: application/vnd.github.v3.raw" \
     -O \
     -L https://raw.githubusercontent.com/<user>/<project>/<branch>/<path_to_remote_filename>
  • the token will be globally accessible from your zsh terminals if stored in the ~/.zshrc file:
export GITHUB_API_REPO_TOKEN="your_github_token"

(2) Execute the bash script:

    zsh> chmod +x .create-poetry-repo.sh
    zsh> ./.create-poetry-repo.sh

(3) To make it even more flexible, wrap the logic within a globally reusable command stored on ~/.zshrc:

    alias create_poetry_project='curl -H "Authorization: token $GITHUB_API_REPO_TOKEN" \
      -H "Accept: application/vnd.github.v3.raw" \
      -O \
      -L https://raw.githubusercontent.com/<user>/<repo>/<branch>/.create-poetry-repo.sh && chmod +x .create-poetry-repo.sh && ./.create-poetry-repo.sh'
    alias cpp="create_poetry_project"

(4) Do not forget to apply the changes via:

    zsh> source ~/.zshrc

Now, whenever I am in the root of a new project, I just have to execute:

zsh> cpp
Project name (camel-case):

…for the structure to be automatically created!