Repository: cookiecutter/cookiecutter-django
Pull request: Add justfile for use with docker
<!-- Thank you for helping us out: your efforts mean a great deal to the project and the community as a whole! -->

## Description

Set up [just](https://github.com/casey/just) to simplify calling docker commands.

Checklist:

- [x] I've made sure that tests are updated accordingly (especially if adding or updating a template option)
- [x] I've updated the documentation or confirm that my change doesn't require any updates

## Rationale

It helps to run frequent commands quicker. This [PR](https://github.com/cookiecutter/cookiecutter-django/pull/1879) attempted to do this via MakeFile. However, using `just` seems like a better approach, especially since it is a cross-platform tool (can be installed via pip), as mentioned in this [comment](https://github.com/cookiecutter/cookiecutter-django/pull/1879#issuecomment-2454038873) as well.

Closes #1879 
Fix #3318 

Problem statement:
Convenient script for rapid development
## Description

This ticket is a suggestion (since I personally follow and I have seen in some non-Django based projects. 

While working on some projects, using Django cookie-cutter, I had to refer to the cookie-cutter documentation for a number of commands (especially when working with docker). Hence I suggest a convenient script to cover all major functionalities with intuitive command names (example: python manage.py showmigrations => ./app showmigrations) and then the end-user can further extend this as needed.

## Rationale

This could assist the developer in rapid development. Not only they would have the short commands (easy to remember) but also they can extend it further by clubbing 2 or more commands together. Example, taking database backup and uploading to AWS:

Initially we have 3 commands:
```
docker-compose -f local.yml exec postgres backup
docker cp "$(docker-compose -f local.yml ps -q postgres)":/backups ./backups
docker-compose -f production.yml run --rm awscli upload
```

Code in our convenience script:

```
backup_db() {
  docker-compose -f local.yml exec postgres backup
  docker cp "$(docker-compose -f local.yml ps -q postgres)":/backups ./backups
}

upload_backup_db_s3() {
  docker-compose -f production.yml run --rm awscli upload
}
```

End-user would get simpler commands:
```
./app backup_db
./app upload_backup_bd_s3
```

Hints from issue discussion:
Your suggestion sounds like #1879, is that correct? 
@foarsitter Correct, the only difference is I am using shell scripts instead of makefiles.
Since many of us use some such scripts, the suggestion is to let the core developers finalize it.

For example, for a new project, I generally start with something like this:


```
#!/bin/sh

# Arguments
c1=$1  # Environment
c2=$2  # Command
c3=$3  # Command argument (optional)


# Help command
help() {
  echo "Usage: <environment> <command> [<command argument>]"
  echo "environment:"
  echo "* local"
  echo "* production"
  echo ""
  echo "command:"
  echo "* generate_certificates"
  echo "* build"
  echo "* init"
  echo "* up"
  echo "* down"
  echo "* logs"
  echo "* showmigrations"
  echo "* makemigrations"
  echo "* migrate"
  echo "* createsuperuser"
  echo "* backup_db"
  echo "* list_backup_db"
  echo "* restore_db"
  echo "* upload_backup_db_s3"
  echo "* download_backup_db_s3"
  echo "* pycodestyle"
  echo "* flake8"
  echo "* pylint"
  echo "* pytest"
  echo "* coverage"
  echo "* unittest"
  echo "* run_all_checks_and_tests"
}


# App commands
generate_certificates() {
  if [ "$c1" = "local" ]
  then
    mkdir certs && docker-compose -f "$c1".yml run django openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out certs/ngo.support.local.crt -keyout certs/ngo.support.local.key -subj "/C=IN/ST=Uttar Pradesh/L=Noida/O=Infroid/OU=IT/CN=ngo.support.local" && echo "Certificates generated successfully."
  else
    echo "Operation aborted. Certificates can only be generated for 'local' environment."
  fi
}

build() {
  docker-compose -f "$c1".yml build
}

init() {
  echo "Do you confirm that the following are installed: docker, docker-compose, git, openssl and pre-commit (y/n)? "
  read -r answer
  if [ "$answer" = "y" ]
  then
    build
    generate_ssl_certificates
    git init
    pre-commit install
  else
    echo "Operation aborted. Please install docker, docker-compose, git, openssl and pre-commit."
  fi
}

up() {
  docker-compose -f "$c1".yml up
}

down() {
  docker-compose -f "$c1".yml down
}

logs() {
  docker-compose -f "$c1".yml logs -t
}


# Django commands
showmigrations() {
  docker-compose -f "$c1".yml run --rm django python manage.py showmigrations
}

makemigrations() {
  docker-compose -f "$c1".yml run --rm django python manage.py makemigrations
}

migrate() {
  docker-compose -f "$c1".yml run --rm django python manage.py migrate
}

createsuperuser() {
  docker-compose -f "$c1".yml run --rm django python manage.py createsuperuser
}


# Postgres commands
backup_db() {
  docker-compose -f local.yml exec postgres backup
  docker cp "$(docker-compose -f local.yml ps -q postgres)":/backups ./backups
}

list_backup_db() {
  docker-compose -f local.yml exec postgres backups
}

restore_db() {
  docker-compose -f local.yml exec postgres restore "$c3"
}

upload_backup_db_s3() {
  docker-compose -f production.yml run --rm awscli upload
}

download_backup_db_s3() {
  docker-compose -f production.yml run --rm awscli download "$c3"
}


# Code check commands
pycodestyle() {
  echo "PYCODESTYLE" && docker-compose -f "$c1".yml run --rm django pycodestyle ./ngo_support_core
}

flake8() {
  echo "FLAKE8" && docker-compose -f "$c1".yml run --rm django flake8
}

pylint() {
  echo "PYLINT" && docker-compose -f "$c1".yml run --rm django pylint ./ngo_support_core
}

pytest() {
  docker-compose -f "$c1".yml run --rm django pytest
}

coverage() {
  docker-compose -f local.yml run --rm django coverage run -m pytest
  docker-compose -f local.yml run --rm django coverage report
}

unittest() {
  docker-compose -f "$c1".yml run --rm django python manage.py test
}

run_all_checks_and_tests() {
  pycodestyle && echo "pycodestyle - No issue found"
  flake8 && echo "flake8 - No issue found"
  pylint && echo "pylint - No issue found"
  coverage && echo "coverage - No issue found"
  unittest && echo "unittest - No issue found"
}

case "$c1" in
  "local"|"production")
    $c2;;
  *)
    help;;
esac

```
This is a really good start.  I have something I built like this as well but you've got more of the common commands already built out.  

Adding the $3 arg on to the end of commands like logs/up/down/etc.. would would fill in some holes as well.

Also a run bash and pythion manage.py shell are needed.

Commit message: Add justfile for use with docker

Patch to review:
diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py
index ca1ffad329..1eee7ef6de 100644
--- a/hooks/post_gen_project.py
+++ b/hooks/post_gen_project.py
@@ -82,6 +82,7 @@ def remove_docker_files():
         "docker-compose.local.yml",
         "docker-compose.production.yml",
         ".dockerignore",
+        "justfile",
     ]
     for file_name in file_names:
         os.remove(file_name)
diff --git a/{{cookiecutter.project_slug}}/justfile b/{{cookiecutter.project_slug}}/justfile
new file mode 100644
index 0000000000..a20df231ca
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/justfile
@@ -0,0 +1,33 @@
+export COMPOSE_FILE := "docker-compose.local.yml"
+
+# Default command to list all available commands.
+default:
+    @just --list
+
+# build: Build python image.
+build:
+    @echo "Building python image..."
+    @docker-compose build
+
+# up: Start up containers.
+up:
+    @echo "Starting up containers..."
+    @docker-compose up -d --remove-orphans
+
+# down: Stop containers.
+down:
+    @echo "Stopping containers..."
+    @docker-compose down
+
+# prune: Remove containers and their volumes.
+prune *args:
+    @echo "Killing containers and removing volumes..."
+    @docker-compose down -v {{ "{{args}}" }}
+
+# logs: View container logs
+logs *args:
+    @docker-compose logs -f {{ "{{args}}" }}
+
+# manage: Executes `manage.py` command.
+manage +args:
+    @docker-compose run --rm django python ./manage.py {{ "{{args}}" }}


Code review comments:
diff_hunk: @@ -0,0 +1,33 @@
+export COMPOSE_FILE := "docker-compose.local.yml"
+
+# Default command to list all available commands.
+default:
+    @just --list
+
+# build: Build python image.
+build:
+    @echo "Building python image..."
+    @docker-compose build
path: {{cookiecutter.project_slug}}/justfile
text: @user2:
[We moved](https://github.com/cookiecutter/cookiecutter-django/pull/4463) to `docker compose` a while back:

```suggestion
    @user1 compose build
```

The standalone `docker-compose` started to break as the compose Docker plugin gained adoption: https://github.com/cookiecutter/cookiecutter-django/issues/3980

@author:
I see. I have updated it to use `docker compose`