mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2024-11-30 05:24:01 +03:00
b91c70d755
## Description Following up on @webyneter attempt in #1205, which is now getting outdated, I've tried to make Gulp task runner work with Docker. There is no documentation yet, but this seems to work locally with the custom bootstrap compilation... - [x] Add a node image for local developement - [x] Proxy the django image rather than localhost in Browser sync, pass header to keep hostname - [x] Don't call the runserver command from Gulp, let docker-compose handle - [x] Update package.json & gulpfile.js templates to reduce the number of unwanted empty lines - [x] Use [multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/) in production to make sure the static assets are produced - [x] Update documentation - [x] Verify that the previous issue with static assets missing from production isn't there ## Rationale Currently, the static build isn't working nicely with Docker, extra manual setup is required. Fixes #1762
336 lines
9.6 KiB
Python
336 lines
9.6 KiB
Python
"""
|
|
NOTE:
|
|
the below code is to be maintained Python 2.x-compatible
|
|
as the whole Cookiecutter Django project initialization
|
|
can potentially be run in Python 2.x environment
|
|
(at least so we presume in `pre_gen_project.py`).
|
|
|
|
TODO: ? restrict Cookiecutter Django project initialization to Python 3.x environments only
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import random
|
|
import shutil
|
|
import string
|
|
|
|
try:
|
|
# Inspired by
|
|
# https://github.com/django/django/blob/master/django/utils/crypto.py
|
|
random = random.SystemRandom()
|
|
using_sysrandom = True
|
|
except NotImplementedError:
|
|
using_sysrandom = False
|
|
|
|
TERMINATOR = "\x1b[0m"
|
|
WARNING = "\x1b[1;33m [WARNING]: "
|
|
INFO = "\x1b[1;33m [INFO]: "
|
|
HINT = "\x1b[3;33m"
|
|
SUCCESS = "\x1b[1;32m [SUCCESS]: "
|
|
|
|
DEBUG_VALUE = "debug"
|
|
|
|
|
|
def remove_open_source_files():
|
|
file_names = ["CONTRIBUTORS.txt", "LICENSE"]
|
|
for file_name in file_names:
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_gplv3_files():
|
|
file_names = ["COPYING"]
|
|
for file_name in file_names:
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_pycharm_files():
|
|
idea_dir_path = ".idea"
|
|
if os.path.exists(idea_dir_path):
|
|
shutil.rmtree(idea_dir_path)
|
|
|
|
docs_dir_path = os.path.join("docs", "pycharm")
|
|
if os.path.exists(docs_dir_path):
|
|
shutil.rmtree(docs_dir_path)
|
|
|
|
|
|
def remove_docker_files():
|
|
shutil.rmtree("compose")
|
|
|
|
file_names = ["local.yml", "production.yml", ".dockerignore"]
|
|
for file_name in file_names:
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_utility_files():
|
|
shutil.rmtree("utility")
|
|
|
|
|
|
def remove_heroku_files():
|
|
file_names = ["Procfile", "runtime.txt", "requirements.txt"]
|
|
for file_name in file_names:
|
|
if (
|
|
file_name == "requirements.txt"
|
|
and "{{ cookiecutter.use_travisci }}".lower() == "y"
|
|
):
|
|
# don't remove the file if we are using travisci but not using heroku
|
|
continue
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_gulp_files():
|
|
file_names = ["gulpfile.js"]
|
|
for file_name in file_names:
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_packagejson_file():
|
|
file_names = ["package.json"]
|
|
for file_name in file_names:
|
|
os.remove(file_name)
|
|
|
|
|
|
def remove_celery_app():
|
|
shutil.rmtree(os.path.join("{{ cookiecutter.project_slug }}", "taskapp"))
|
|
|
|
|
|
def remove_dottravisyml_file():
|
|
os.remove(".travis.yml")
|
|
|
|
|
|
def append_to_project_gitignore(path):
|
|
gitignore_file_path = ".gitignore"
|
|
with open(gitignore_file_path, "a") as gitignore_file:
|
|
gitignore_file.write(path)
|
|
gitignore_file.write(os.linesep)
|
|
|
|
|
|
def generate_random_string(
|
|
length, using_digits=False, using_ascii_letters=False, using_punctuation=False
|
|
):
|
|
"""
|
|
Example:
|
|
opting out for 50 symbol-long, [a-z][A-Z][0-9] string
|
|
would yield log_2((26+26+50)^50) ~= 334 bit strength.
|
|
"""
|
|
if not using_sysrandom:
|
|
return None
|
|
|
|
symbols = []
|
|
if using_digits:
|
|
symbols += string.digits
|
|
if using_ascii_letters:
|
|
symbols += string.ascii_letters
|
|
if using_punctuation:
|
|
all_punctuation = set(string.punctuation)
|
|
# These symbols can cause issues in environment variables
|
|
unsuitable = {"'", '"', "\\", "$"}
|
|
suitable = all_punctuation.difference(unsuitable)
|
|
symbols += "".join(suitable)
|
|
return "".join([random.choice(symbols) for _ in range(length)])
|
|
|
|
|
|
def set_flag(file_path, flag, value=None, formatted=None, *args, **kwargs):
|
|
if value is None:
|
|
random_string = generate_random_string(*args, **kwargs)
|
|
if random_string is None:
|
|
print(
|
|
"We couldn't find a secure pseudo-random number generator on your system. "
|
|
"Please, make sure to manually {} later.".format(flag)
|
|
)
|
|
random_string = flag
|
|
if formatted is not None:
|
|
random_string = formatted.format(random_string)
|
|
value = random_string
|
|
|
|
with open(file_path, "r+") as f:
|
|
file_contents = f.read().replace(flag, value)
|
|
f.seek(0)
|
|
f.write(file_contents)
|
|
f.truncate()
|
|
|
|
return value
|
|
|
|
|
|
def set_django_secret_key(file_path):
|
|
django_secret_key = set_flag(
|
|
file_path,
|
|
"!!!SET DJANGO_SECRET_KEY!!!",
|
|
length=64,
|
|
using_digits=True,
|
|
using_ascii_letters=True,
|
|
)
|
|
return django_secret_key
|
|
|
|
|
|
def set_django_admin_url(file_path):
|
|
django_admin_url = set_flag(
|
|
file_path,
|
|
"!!!SET DJANGO_ADMIN_URL!!!",
|
|
formatted="{}/",
|
|
length=32,
|
|
using_digits=True,
|
|
using_ascii_letters=True,
|
|
)
|
|
return django_admin_url
|
|
|
|
|
|
def generate_random_user():
|
|
return generate_random_string(length=32, using_ascii_letters=True)
|
|
|
|
|
|
def generate_postgres_user(debug=False):
|
|
return DEBUG_VALUE if debug else generate_random_user()
|
|
|
|
|
|
def set_postgres_user(file_path, value):
|
|
postgres_user = set_flag(file_path, "!!!SET POSTGRES_USER!!!", value=value)
|
|
return postgres_user
|
|
|
|
|
|
def set_postgres_password(file_path, value=None):
|
|
postgres_password = set_flag(
|
|
file_path,
|
|
"!!!SET POSTGRES_PASSWORD!!!",
|
|
value=value,
|
|
length=64,
|
|
using_digits=True,
|
|
using_ascii_letters=True,
|
|
)
|
|
return postgres_password
|
|
|
|
|
|
def set_celery_flower_user(file_path, value):
|
|
celery_flower_user = set_flag(
|
|
file_path, "!!!SET CELERY_FLOWER_USER!!!", value=value
|
|
)
|
|
return celery_flower_user
|
|
|
|
|
|
def set_celery_flower_password(file_path, value=None):
|
|
celery_flower_password = set_flag(
|
|
file_path,
|
|
"!!!SET CELERY_FLOWER_PASSWORD!!!",
|
|
value=value,
|
|
length=64,
|
|
using_digits=True,
|
|
using_ascii_letters=True,
|
|
)
|
|
return celery_flower_password
|
|
|
|
|
|
def append_to_gitignore_file(s):
|
|
with open(".gitignore", "a") as gitignore_file:
|
|
gitignore_file.write(s)
|
|
gitignore_file.write(os.linesep)
|
|
|
|
|
|
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
|
|
local_django_envs_path = os.path.join(".envs", ".local", ".django")
|
|
production_django_envs_path = os.path.join(".envs", ".production", ".django")
|
|
local_postgres_envs_path = os.path.join(".envs", ".local", ".postgres")
|
|
production_postgres_envs_path = os.path.join(".envs", ".production", ".postgres")
|
|
|
|
set_django_secret_key(production_django_envs_path)
|
|
set_django_admin_url(production_django_envs_path)
|
|
|
|
set_postgres_user(local_postgres_envs_path, value=postgres_user)
|
|
set_postgres_password(
|
|
local_postgres_envs_path, value=DEBUG_VALUE if debug else None
|
|
)
|
|
set_postgres_user(production_postgres_envs_path, value=postgres_user)
|
|
set_postgres_password(
|
|
production_postgres_envs_path, value=DEBUG_VALUE if debug else None
|
|
)
|
|
|
|
set_celery_flower_user(local_django_envs_path, value=celery_flower_user)
|
|
set_celery_flower_password(
|
|
local_django_envs_path, value=DEBUG_VALUE if debug else None
|
|
)
|
|
set_celery_flower_user(production_django_envs_path, value=celery_flower_user)
|
|
set_celery_flower_password(
|
|
production_django_envs_path, value=DEBUG_VALUE if debug else None
|
|
)
|
|
|
|
|
|
def set_flags_in_settings_files():
|
|
set_django_secret_key(os.path.join("config", "settings", "local.py"))
|
|
set_django_secret_key(os.path.join("config", "settings", "test.py"))
|
|
|
|
|
|
def remove_envs_and_associated_files():
|
|
shutil.rmtree(".envs")
|
|
os.remove("merge_production_dotenvs_in_dotenv.py")
|
|
|
|
|
|
def remove_celery_compose_dirs():
|
|
shutil.rmtree(os.path.join("compose", "local", "django", "celery"))
|
|
shutil.rmtree(os.path.join("compose", "production", "django", "celery"))
|
|
|
|
|
|
def remove_node_dockerfile():
|
|
shutil.rmtree(os.path.join("compose", "local", "node"))
|
|
|
|
|
|
def main():
|
|
debug = "{{ cookiecutter.debug }}".lower() == "y"
|
|
|
|
set_flags_in_envs(
|
|
DEBUG_VALUE if debug else generate_random_user(),
|
|
DEBUG_VALUE if debug else generate_random_user(),
|
|
debug=debug,
|
|
)
|
|
set_flags_in_settings_files()
|
|
|
|
if "{{ cookiecutter.open_source_license }}" == "Not open source":
|
|
remove_open_source_files()
|
|
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
|
|
remove_gplv3_files()
|
|
|
|
if "{{ cookiecutter.use_pycharm }}".lower() == "n":
|
|
remove_pycharm_files()
|
|
|
|
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
|
remove_utility_files()
|
|
else:
|
|
remove_docker_files()
|
|
|
|
if "{{ cookiecutter.use_heroku }}".lower() == "n":
|
|
remove_heroku_files()
|
|
|
|
if (
|
|
"{{ cookiecutter.use_docker }}".lower() == "n"
|
|
and "{{ cookiecutter.use_heroku }}".lower() == "n"
|
|
):
|
|
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
|
print(
|
|
INFO + ".env(s) are only utilized when Docker Compose and/or "
|
|
"Heroku support is enabled so keeping them does not "
|
|
"make sense given your current setup." + TERMINATOR
|
|
)
|
|
remove_envs_and_associated_files()
|
|
else:
|
|
append_to_gitignore_file(".env")
|
|
append_to_gitignore_file(".envs/*")
|
|
if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y":
|
|
append_to_gitignore_file("!.envs/.local/")
|
|
|
|
if "{{ cookiecutter.js_task_runner}}".lower() == "none":
|
|
remove_gulp_files()
|
|
remove_packagejson_file()
|
|
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
|
remove_node_dockerfile()
|
|
|
|
if "{{ cookiecutter.use_celery }}".lower() == "n":
|
|
remove_celery_app()
|
|
if "{{ cookiecutter.use_docker }}".lower() == "y":
|
|
remove_celery_compose_dirs()
|
|
|
|
if "{{ cookiecutter.use_travisci }}".lower() == "n":
|
|
remove_dottravisyml_file()
|
|
|
|
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|