A bunch of minor updates

This commit is contained in:
Corey Oordt 2019-07-24 07:34:24 -05:00
parent e89f611238
commit 54a9c168fb
33 changed files with 272 additions and 268 deletions

View File

@ -14,5 +14,5 @@ requirements:
- "requirements.txt" - "requirements.txt"
- "{{cookiecutter.project_slug}}/requirements/base.txt" - "{{cookiecutter.project_slug}}/requirements/base.txt"
- "{{cookiecutter.project_slug}}/requirements/dev.txt" - "{{cookiecutter.project_slug}}/requirements/dev.txt"
- "{{cookiecutter.project_slug}}/requirements/production.txt" - "{{cookiecutter.project_slug}}/requirements/prod.txt"
- "{{cookiecutter.project_slug}}/requirements/test.txt" - "{{cookiecutter.project_slug}}/requirements/test.txt"

View File

@ -5,7 +5,6 @@
"author_name": "Your Name", "author_name": "Your Name",
"domain_name": "example.com", "domain_name": "example.com",
"email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com", "email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com",
"version": "0.1.0",
"open_source_license": [ "open_source_license": [
"MIT", "MIT",
"BSD", "BSD",

View File

@ -18,6 +18,52 @@ HINT = "\x1b[3;33m"
SUCCESS = "\x1b[1;32m [SUCCESS]: " SUCCESS = "\x1b[1;32m [SUCCESS]: "
DEBUG_VALUE = "debug" DEBUG_VALUE = "debug"
PROJECT_DIRECTORY = os.path.realpath(os.path.curdir)
def initialize_git(project_directory):
"""
Initialize the git repo.
Args:
project_directory:
"""
import subprocess
print("Initializing git repo...")
result = subprocess.run(
["git", "init"], cwd=project_directory, encoding="utf8", capture_output=True
)
if result.returncode != 0:
print("Unable to initialize the git repo.")
print(result.stdout, result.stderr)
result = subprocess.run(
["git", "add", "."], cwd=project_directory, encoding="utf8", capture_output=True
)
if result.returncode != 0:
print("Unable to add all files into the git repo.")
print(result.stdout, result.stderr)
result = subprocess.run(
["git", "commit", '-m"Initial commit"'],
cwd=project_directory,
encoding="utf8",
capture_output=True,
)
if result.returncode != 0:
print("Unable to make the initial commit.")
print(result.stdout, result.stderr)
result = subprocess.run(
["git", "tag", "0.1.0"],
cwd=project_directory,
encoding="utf8",
capture_output=True,
)
if result.returncode != 0:
print("Unable to tag the initial commit.")
print(result.stdout, result.stderr)
def remove_open_source_files(): def remove_open_source_files():
@ -32,20 +78,10 @@ def remove_gplv3_files():
os.remove(file_name) 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(): def remove_docker_files():
shutil.rmtree("compose") shutil.rmtree("compose")
file_names = ["dev.yml", ".dockerignore"] file_names = ["local.yml", ".dockerignore"]
for file_name in file_names: for file_name in file_names:
os.remove(file_name) os.remove(file_name)
@ -146,20 +182,8 @@ def set_django_secret_key(file_path):
return django_secret_key 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(): def generate_random_user():
return generate_random_string(length=32, using_ascii_letters=True) return generate_random_string(length=10, using_ascii_letters=True)
def generate_postgres_user(debug=False): def generate_postgres_user(debug=False):
@ -209,36 +233,23 @@ def append_to_gitignore_file(s):
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False): def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
local_django_envs_path = os.path.join(".envs", "dev", "django")
production_django_envs_path = os.path.join(".envs", "prod", "django") production_django_envs_path = os.path.join(".envs", "prod", "django")
local_postgres_envs_path = os.path.join(".envs", "dev", "postgres") dev_postgres_env_path = os.path.join(".envs", "dev", "postgres")
pg_pass = set_postgres_password(dev_postgres_env_path)
set_flag(production_django_envs_path, "!!!SET POSTGRES_PASSWORD!!!", value=pg_pass)
set_django_secret_key(production_django_envs_path) 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_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_user(production_django_envs_path, value=celery_flower_user)
set_celery_flower_password( set_celery_flower_password(production_django_envs_path)
production_django_envs_path, value=DEBUG_VALUE if debug else None
)
def set_flags_in_settings_files(): def set_flags_in_settings_files():
set_django_secret_key(
os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev_template.py")
)
set_django_secret_key( set_django_secret_key(
os.path.join("{{ cookiecutter.project_slug }}", "settings", "test.py") os.path.join("{{ cookiecutter.project_slug }}", "settings", "test.py")
) )
def remove_storage():
os.remove(os.path.join("{{ cookiecutter.project_slug }}", "storage.py"))
def remove_envs_and_associated_files(): def remove_envs_and_associated_files():
shutil.rmtree(".envs") shutil.rmtree(".envs")
@ -250,6 +261,12 @@ def create_dev_settings():
os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev_template.py"), os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev_template.py"),
os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev.py"), os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev.py"),
) )
shutil.copy(os.path.join(".envs", "prod", "django"), ".env")
set_flag(
".env",
"DJANGO_SETTINGS_MODULE=test_project.settings.prod",
value="DJANGO_SETTINGS_MODULE=test_project.settings",
)
def main(): def main():
@ -263,14 +280,14 @@ def main():
if "{{ cookiecutter.open_source_license}}" != "GPLv3": if "{{ cookiecutter.open_source_license}}" != "GPLv3":
remove_gplv3_files() remove_gplv3_files()
append_to_gitignore_file(".env")
append_to_gitignore_file(".envs/*")
if "{{ cookiecutter.cloud_provider}}".lower() == "none": if "{{ cookiecutter.cloud_provider}}".lower() == "none":
print( print(
WARNING + "You chose not to use a cloud provider, " WARNING + "You chose not to use a cloud provider, "
"media files won't be served in production." + TERMINATOR "media files won't be served in production." + TERMINATOR
) )
remove_storage()
elif "{{ cookiecutter.cloud_provider}}".lower() == "GCP":
remove_storage()
if "{{ cookiecutter.use_celery }}".lower() == "n": if "{{ cookiecutter.use_celery }}".lower() == "n":
remove_celery_files() remove_celery_files()
@ -278,6 +295,8 @@ def main():
if "{{ cookiecutter.use_travisci }}".lower() == "n": if "{{ cookiecutter.use_travisci }}".lower() == "n":
remove_dottravisyml_file() remove_dottravisyml_file()
initialize_git(PROJECT_DIRECTORY)
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR) print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)

View File

@ -1,17 +0,0 @@
# General
# ------------------------------------------------------------------------------
USE_DOCKER=yes
IPYTHONDIR=/app/.ipython
{%- if cookiecutter.use_celery == 'y' %}
# Redis
# ------------------------------------------------------------------------------
REDIS_URL=redis://redis:6379/0
# Celery
# ------------------------------------------------------------------------------
# Flower
CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!
{% endif %}

View File

@ -1,7 +1,6 @@
# PostgreSQL
# ------------------------------------------------------------------------------
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB={{ cookiecutter.project_slug }}
POSTGRES_USER=!!!SET POSTGRES_USER!!!
POSTGRES_PASSWORD=!!!SET POSTGRES_PASSWORD!!! POSTGRES_PASSWORD=!!!SET POSTGRES_PASSWORD!!!
# PGDATA=/var/lib/postgresql/data
# POSTGRES_INITDB_WALDIR=
# POSTGRES_INITDB_ARGS=
# POSTGRES_USER=postgres
# POSTGRES_DB=postgres

View File

@ -1,32 +1,47 @@
# General # General
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# DJANGO_READ_DOT_ENV_FILE=True DJANGO_SETTINGS_MODULE={{ cookiecutter.project_slug }}.settings.prod
DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY=!!!SET DJANGO_SECRET_KEY!!! DJANGO_SECRET_KEY=!!!SET DJANGO_SECRET_KEY!!!
DJANGO_ADMIN_URL=!!!SET DJANGO_ADMIN_URL!!! DJANGO_ADMIN_URL=admin/
DJANGO_ALLOWED_HOSTS=.{{ cookiecutter.domain_name }} DJANGO_ALLOWED_HOSTS=.{{ cookiecutter.domain_name }}
DJANGO_DEBUG=False
DATABASE_URL=postgres://postgres:!!!SET POSTGRES_PASSWORD!!!@localhost/{{ cookiecutter.project_slug }}
# Security # Security
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# TIP: better off using DNS, however, redirect is OK too # TIP: better off using DNS, however, redirect is OK too
DJANGO_SECURE_SSL_REDIRECT=False DJANGO_SECURE_SSL_REDIRECT=False
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True
DJANGO_SECURE_HSTS_PRELOAD=True
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True
# Email # Email
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MAILGUN_API_KEY=
DJANGO_SERVER_EMAIL= DJANGO_SERVER_EMAIL=
MAILGUN_DOMAIN= DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=localhost
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
EMAIL_PORT=25
DJANGO_EMAIL_SUBJECT_PREFIX="[{{cookiecutter.project_name}}]"
{% if cookiecutter.cloud_provider == 'AWS' %} {% if cookiecutter.cloud_provider == 'AWS' %}
# AWS # AWS
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
DJANGO_AWS_ACCESS_KEY_ID= DJANGO_AWS_ACCESS_KEY_ID=
DJANGO_AWS_SECRET_ACCESS_KEY= DJANGO_AWS_SECRET_ACCESS_KEY=
DJANGO_AWS_STORAGE_BUCKET_NAME= DJANGO_AWS_STORAGE_BUCKET_NAME=
DJANGO_AWS_S3_REGION_NAME=
{% elif cookiecutter.cloud_provider == 'GCP' %} {% elif cookiecutter.cloud_provider == 'GCP' %}
# GCP # GCP
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
GOOGLE_APPLICATION_CREDENTIALS= GOOGLE_APPLICATION_CREDENTIALS=
DJANGO_GCP_STORAGE_BUCKET_NAME= DJANGO_GCP_STORAGE_BUCKET_NAME=
{% elif cookiecutter.cloud_provider == 'Azure' %}
DJANGO_AZURE_ACCOUNT_NAME=
DJANGO_AZURE_ACCOUNT_KEY=
DJANGO_AZURE_CONTAINER=
{% endif %} {% endif %}
# django-allauth # django-allauth
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -43,15 +58,16 @@ WEB_CONCURRENCY=4
# Sentry # Sentry
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
SENTRY_DSN= SENTRY_DSN=
# SENTRY_LOG_LEVEL=
{% endif %} {% endif %}
# Redis # Redis
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
REDIS_URL=redis://redis:6379/0 REDIS_URL=redis://localhost:6379/0
{% if cookiecutter.use_celery == 'y' %} {% if cookiecutter.use_celery == 'y' %}
# Celery # Celery
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
CELERY_BROKER_URL=redis://localhost:6379/0
# Flower # Flower
CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!! CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!! CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!

View File

@ -12,7 +12,7 @@
<option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" /> <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
<option name="SCRIPT_NAME" value="merge_production_dotenvs_in_dotenv.py" /> <option name="SCRIPT_NAME" value="./bin/merge_production_dotenvs_in_dotenv.py" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" /> <option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" /> <option name="EMULATE_TERMINAL" value="false" />

View File

@ -5,7 +5,7 @@
<option name="PARENT_ENVS" value="true" /> <option name="PARENT_ENVS" value="true" />
<envs> <envs>
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="config.settings.local" /> <env name="DJANGO_SETTINGS_MODULE" value="{{ cookiecutter.project_slug }}.settings" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />

View File

@ -5,7 +5,7 @@
<option name="PARENT_ENVS" value="true" /> <option name="PARENT_ENVS" value="true" />
<envs> <envs>
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="config.settings.local" /> <env name="DJANGO_SETTINGS_MODULE" value="{{ cookiecutter.project_slug }}.settings" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />

View File

@ -5,7 +5,7 @@
<option name="PARENT_ENVS" value="true" /> <option name="PARENT_ENVS" value="true" />
<envs> <envs>
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="config.settings.local" /> <env name="DJANGO_SETTINGS_MODULE" value="{{ cookiecutter.project_slug }}.settings" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />

View File

@ -25,41 +25,15 @@ RUN addgroup -S django \
# Requirements are installed here to ensure they will be cached. # Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements COPY ./requirements /requirements
RUN pip install --no-cache-dir -r /requirements/production.txt \ RUN pip install --no-cache-dir -r /requirements/prod.txt \
&& rm -rf /requirements && rm -rf /requirements
COPY ./compose/production/django/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint
RUN chown django /entrypoint
COPY ./compose/production/django/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
RUN chown django /start
{%- if cookiecutter.use_celery == "y" %}
COPY ./compose/production/django/celery/worker/start /start-celeryworker
RUN sed -i 's/\r$//g' /start-celeryworker
RUN chmod +x /start-celeryworker
RUN chown django /start-celeryworker
COPY ./compose/production/django/celery/beat/start /start-celerybeat
RUN sed -i 's/\r$//g' /start-celerybeat
RUN chmod +x /start-celerybeat
RUN chown django /start-celerybeat
COPY ./compose/production/django/celery/flower/start /start-flower
RUN sed -i 's/\r$//g' /start-flower
RUN chmod +x /start-flower
{%- endif %}
COPY --from=client-builder /app /app COPY --from=client-builder /app /app
RUN chown -R django /app RUN chown -R django /app
RUN chmod +x /app/bin/*
USER django USER django
WORKDIR /app WORKDIR /app
ENTRYPOINT ["/entrypoint"] ENTRYPOINT ["/app/bin/entrypoint"]

View File

@ -79,7 +79,7 @@ To run a celery worker:
.. code-block:: bash .. code-block:: bash
cd {{cookiecutter.project_slug}} cd {{cookiecutter.project_slug}}
celery -A config.celery_app worker -l info celery -A {{ cookiecutter.project_slug }}.celery_app worker -l info
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right. Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.

15
{{cookiecutter.project_slug}}/bin/entrypoint Normal file → Executable file
View File

@ -10,12 +10,13 @@ set -o nounset
export CELERY_BROKER_URL="${REDIS_URL}" export CELERY_BROKER_URL="${REDIS_URL}"
{% endif %} {% endif %}
if [ -z "${POSTGRES_USER}" ]; then if [ -z "${DATABASE_URL}" ]; then
if [ -z "${POSTGRES_USER}" ]; then
base_postgres_image_default_user='postgres' base_postgres_image_default_user='postgres'
export POSTGRES_USER="${base_postgres_image_default_user}" export POSTGRES_USER="${base_postgres_image_default_user}"
fi
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
fi fi
export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
postgres_ready() { postgres_ready() {
python << END python << END
import sys import sys
@ -23,13 +24,7 @@ import sys
import psycopg2 import psycopg2
try: try:
psycopg2.connect( psycopg2.connect("${DATABASE_URL}")
dbname="${POSTGRES_DB}",
user="${POSTGRES_USER}",
password="${POSTGRES_PASSWORD}",
host="${POSTGRES_HOST}",
port="${POSTGRES_PORT}",
)
except psycopg2.OperationalError: except psycopg2.OperationalError:
sys.exit(-1) sys.exit(-1)
sys.exit(0) sys.exit(0)

View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
PROJECT_PATH=`pwd`
ENV_PATH="${PROJECT_PATH}/.envs/prod/django"
SETTINGS_PATH="${PROJECT_PATH}/{{ cookiecutter.project_slug }}/settings/dev_template.py"
echo "Creating local .env file"
cp ${ENV_PATH} "${PROJECT_PATH}/.env"
echo "Creating local dev.py settings"
cp ${SETTINGS_PATH} "${PROJECT_PATH}/{{ cookiecutter.project_slug }}/settings/dev.py"

2
{{cookiecutter.project_slug}}/bin/start-celery-beat Normal file → Executable file
View File

@ -5,4 +5,4 @@ set -o pipefail
set -o nounset set -o nounset
celery -A config.celery_app beat -l INFO celery -A {{ cookiecutter.project_slug }}.celery_app beat -l INFO

2
{{cookiecutter.project_slug}}/bin/start-celery-worker Normal file → Executable file
View File

@ -5,4 +5,4 @@ set -o pipefail
set -o nounset set -o nounset
celery -A config.celery_app worker -l INFO celery -A {{ cookiecutter.project_slug }}.celery_app worker -l INFO

2
{{cookiecutter.project_slug}}/bin/start-django Normal file → Executable file
View File

@ -6,4 +6,4 @@ set -o nounset
python /app/manage.py collectstatic --noinput python /app/manage.py collectstatic --noinput
/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:8000 --chdir=/app /usr/local/bin/gunicorn {{ cookiecutter.project_slug }}.wsgi --bind 0.0.0.0:8000 --chdir=/app

0
{{cookiecutter.project_slug}}/bin/start-django-dev Normal file → Executable file
View File

2
{{cookiecutter.project_slug}}/bin/start-flower Normal file → Executable file
View File

@ -5,6 +5,6 @@ set -o nounset
celery flower \ celery flower \
--app=config.celery_app \ --app={{ cookiecutter.project_slug }}.celery_app \
--broker="${CELERY_BROKER_URL}" \ --broker="${CELERY_BROKER_URL}" \
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"

View File

@ -8,8 +8,8 @@ services:
django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %} django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %}
build: build:
context: . context: .
dockerfile: ./compose/dev/django/Dockerfile dockerfile: ./Dockerfile
image: {{ cookiecutter.project_slug }}_local_django image: {{ cookiecutter.project_slug }}_django
depends_on: depends_on:
- postgres - postgres
{%- if cookiecutter.use_mailhog == 'y' %} {%- if cookiecutter.use_mailhog == 'y' %}
@ -18,22 +18,20 @@ services:
volumes: volumes:
- .:/app - .:/app
env_file: env_file:
- ./.envs/dev/django - .env
- ./.envs/dev/postgres
ports: ports:
- "8000:8000" - "8000:8000"
command: /start command: /app/bin/start-django-dev
postgres: postgres:
build: image: postgres:11-alpine
context: .
dockerfile: postgres:11-alpine
image: {{ cookiecutter.project_slug }}_postgres
volumes: volumes:
- local_postgres_data:/var/lib/postgresql/data - local_postgres_data:/var/lib/postgresql/data
- local_postgres_data_backups:/backups - local_postgres_data_backups:/backups
env_file: env_file:
- .envs/dev/postgres - .env
ports:
- "5432:5432"
{%- if cookiecutter.use_mailhog == 'y' %} {%- if cookiecutter.use_mailhog == 'y' %}
mailhog: mailhog:
@ -56,7 +54,7 @@ services:
- mailhog - mailhog
{%- endif %} {%- endif %}
ports: [] ports: []
command: /start-celeryworker command: /app/bin/start-celeryworker
celerybeat: celerybeat:
<<: *django <<: *django
@ -68,22 +66,19 @@ services:
- mailhog - mailhog
{%- endif %} {%- endif %}
ports: [] ports: []
command: /start-celerybeat command: /app/bin/start-celerybeat
flower: flower:
<<: *django <<: *django
image: {{ cookiecutter.project_slug }}_flower image: {{ cookiecutter.project_slug }}_flower
ports: ports:
- "5555:5555" - "5555:5555"
command: /start-flower command: /app/bin/start-flower
{%- endif %} {%- endif %}
node: node:
build: image: node:10-stretch-slim
context: .
dockerfile: node:10-stretch-slim
image: {{ cookiecutter.project_slug }}_node
depends_on: depends_on:
- django - django
volumes: volumes:

View File

@ -1,6 +1,6 @@
{ {
"name": "{{cookiecutter.project_slug}}", "name": "{{cookiecutter.project_slug}}",
"version": "{{ cookiecutter.version }}", "version": "1.0.0",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"autoprefixer": "^9.4.7", "autoprefixer": "^9.4.7",

View File

@ -1,3 +1,3 @@
[pytest] [pytest]
addopts = --ds=config.settings.test addopts = --ds={{ cookiecutter.project_slug }}.settings.test
norecursedirs = node_modules norecursedirs = node_modules

View File

@ -29,3 +29,10 @@ django-redis==4.10.0 # https://github.com/niwinz/django-redis
# Django REST Framework # Django REST Framework
djangorestframework==3.10.0 # https://github.com/encode/django-rest-framework djangorestframework==3.10.0 # https://github.com/encode/django-rest-framework
coreapi==2.3.3 # https://github.com/core-api/python-client coreapi==2.3.3 # https://github.com/core-api/python-client
{%- if cookiecutter.cloud_provider == 'AWS' %}
django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'GCP' %}
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'Azure' %}
django-storages[azure]==1.7.1 # https://github.com/jschneier/django-storages
{%- endif %}

View File

@ -0,0 +1,9 @@
# PRECAUTION: avoid production dependencies that aren't in development
-r ./base.txt
gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==0.10.2 # https://github.com/getsentry/sentry-python
{%- endif %}

View File

@ -1,20 +0,0 @@
# PRECAUTION: avoid production dependencies that aren't in development
-r ./base.txt
gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- if cookiecutter.use_sentry == "y" %}
sentry-sdk==0.10.2 # https://github.com/getsentry/sentry-python
{%- endif %}
# Django
# ------------------------------------------------------------------------------
{%- if cookiecutter.cloud_provider == 'AWS' %}
django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'GCP' %}
django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
{%- elif cookiecutter.cloud_provider == 'Azure' %}
django-storages[azure]==1.7.1 # https://github.com/jschneier/django-storages
{%- endif %}
django-anymail[mailgun]==6.1.0 # https://github.com/anymail/django-anymail

View File

@ -1,4 +1,4 @@
__version__ = "{{ cookiecutter.version }}" __version__ = ""
__version_info__ = tuple( __version_info__ = tuple(
[ [
int(num) if num.isdigit() else num int(num) if num.isdigit() else num

View File

@ -1,21 +1,6 @@
""" """
Base settings to build other settings files upon. Base settings to build other settings files upon.
""" """
{% if cookiecutter.use_sentry == 'y' -%}
import logging
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
{%- if cookiecutter.use_celery == 'y' %}
from sentry_sdk.integrations.celery import CeleryIntegration
{% endif %}{% endif -%}
{% if cookiecutter.cloud_provider == 'AWS' %}
from storages.backends.s3boto3 import S3Boto3Storage
{% elif cookiecutter.cloud_provider == 'Azure' %}
from storages.backends.azure_storage import AzureStorage
{%- endif %}
import environ import environ
ROOT_DIR = ( ROOT_DIR = (
@ -32,7 +17,7 @@ env.read_env(str(ROOT_DIR.path(".env")))
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
DEBUG = env.bool("DJANGO_DEBUG", False) DEBUG = env.bool("DJANGO_DEBUG", False)
SITE_ID = 1 SITE_ID = 1
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domain_name }}"]) ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=[".{{ cookiecutter.domain_name }}"])
# DATABASES # DATABASES
@ -234,28 +219,13 @@ STATICFILES_FINDERS = [
{%- endif %} {%- endif %}
] ]
{%- if cookiecutter.cloud_provider == 'AWS' %} {%- if cookiecutter.cloud_provider == 'AWS' %}
STATICFILES_STORAGE = "{{ cookiecutter.project_slug }}.storage.StaticRootS3Boto3Storage"
class StaticRootS3Boto3Storage(S3Boto3Storage):
location = "static"
default_acl = "public-read"
STATICFILES_STORAGE = "settings.base.StaticRootS3Boto3Storage"
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/" STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
{% elif cookiecutter.cloud_provider == 'GCP' %} {% elif cookiecutter.cloud_provider == 'GCP' %}
STATICFILES_STORAGE = "storages.backends.gcloud.GoogleCloudStorage" STATICFILES_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/" STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
{% elif cookiecutter.cloud_provider == 'Azure' %} {% elif cookiecutter.cloud_provider == 'Azure' %}
STATICFILES_STORAGE = "{{ cookiecutter.project_slug }}.storage.PublicAzureStorage"
class PublicAzureStorage(AzureStorage):
account_name = AZURE_ACCOUNT_NAME
account_key = AZURE_ACCOUNT_KEY
azure_container = AZURE_CONTAINER
expiration_secs = None
STATICFILES_STORAGE = "settings.base.PublicAzureStorage"
{% elif cookiecutter.use_whitenoise == 'y' %} {% elif cookiecutter.use_whitenoise == 'y' %}
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
{% else %} {% else %}
@ -273,14 +243,7 @@ COMPRESS_URL = STATIC_URL
# https://docs.djangoproject.com/en/2.2/ref/settings/#file-uploads # https://docs.djangoproject.com/en/2.2/ref/settings/#file-uploads
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
{%- if cookiecutter.cloud_provider == 'AWS' %} {%- if cookiecutter.cloud_provider == 'AWS' %}
DEFAULT_FILE_STORAGE = "{{ cookiecutter.project_slug }}.storage.MediaRootS3Boto3Storage"
class MediaRootS3Boto3Storage(S3Boto3Storage):
location = "media"
file_overwrite = False
DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/" MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
{% elif cookiecutter.cloud_provider == 'GCP' %} {% elif cookiecutter.cloud_provider == 'GCP' %}
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage" DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
@ -384,34 +347,8 @@ LOGGING = {
"handlers": ["console"], "handlers": ["console"],
"propagate": False, "propagate": False,
}, },
{%- if cookiecutter.use_sentry == 'y' %}
"sentry_sdk": { # Errors logged by Sentry's SDK itself
"level": "ERROR",
"handlers": ["console"],
"propagate": False,
},
{%- endif %}
}, },
} }
{%- if cookiecutter.use_sentry == 'y' %}
# Sentry
# ------------------------------------------------------------------------------
SENTRY_DSN = env("SENTRY_DSN")
SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
sentry_logging = LoggingIntegration(
level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
event_level=logging.ERROR, # Send errors as events
)
{%- if cookiecutter.use_celery == 'y' %}
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[sentry_logging, DjangoIntegration(), CeleryIntegration()],
)
{%- else %}
sentry_sdk.init(dsn=SENTRY_DSN, integrations=[sentry_logging, DjangoIntegration()])
{% endif -%}{%- endif %}
{%- if cookiecutter.use_celery == 'y' %} {%- if cookiecutter.use_celery == 'y' %}

View File

@ -3,19 +3,11 @@ from .base import env
# GENERAL # GENERAL
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = True DEBUG = True
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = env(
"DJANGO_SECRET_KEY",
default="!!!SET DJANGO_SECRET_KEY!!!",
)
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
# CACHES # CACHES
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "BACKEND": "django.core.cache.backends.locmem.LocMemCache",

View File

@ -1,5 +1,17 @@
from .base import * # noqa from .base import * # noqa
from .base import env # noqa
{% if cookiecutter.use_sentry == 'y' -%}
import logging
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
{% if cookiecutter.use_celery == 'y' -%}
from sentry_sdk.integrations.celery import CeleryIntegration
{% endif %}{% endif %}
DEBUG = False # Forcible override to turn DEBUG off in production
# TEMPLATES # TEMPLATES
# https://docs.djangoproject.com/en/dev/ref/settings/#templates # https://docs.djangoproject.com/en/dev/ref/settings/#templates
@ -13,3 +25,66 @@ TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
], ],
) )
] ]
# LOGGING
# https://docs.djangoproject.com/en/dev/ref/settings/#logging
# ------------------------------------------------------------------------------
# See https://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "%(levelname)s %(asctime)s %(module)s "
"%(process)d %(thread)d %(message)s"
}
},
"handlers": {
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "verbose",
}
},
"root": {"level": "INFO", "handlers": ["console"]},
"loggers": {
"django.db.backends": {
"level": "ERROR",
"handlers": ["console"],
"propagate": False,
},
"django.security.DisallowedHost": {
"level": "ERROR",
"handlers": ["console"],
"propagate": False,
},
{%- if cookiecutter.use_sentry == 'y' %}
"sentry_sdk": { # Errors logged by Sentry's SDK itself
"level": "ERROR",
"handlers": ["console"],
"propagate": False,
},
{%- endif %}
},
}
{%- if cookiecutter.use_sentry == 'y' %}
# Sentry
# ------------------------------------------------------------------------------
SENTRY_DSN = env("SENTRY_DSN")
SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
sentry_logging = LoggingIntegration(
level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
event_level=logging.ERROR, # Send errors as events
)
{%- if cookiecutter.use_celery == 'y' %}
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[sentry_logging, DjangoIntegration(), CeleryIntegration()],
)
{%- else %}
sentry_sdk.init(dsn=SENTRY_DSN, integrations=[sentry_logging, DjangoIntegration()])
{%- endif %}
{%- endif %}

View File

@ -7,19 +7,15 @@ from .base import env
# GENERAL # GENERAL
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = False DEBUG = False
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = env( SECRET_KEY = env(
"DJANGO_SECRET_KEY", "DJANGO_SECRET_KEY",
default="!!!SET DJANGO_SECRET_KEY!!!", default="!!!SET DJANGO_SECRET_KEY!!!",
) )
# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
TEST_RUNNER = "django.test.runner.DiscoverRunner" TEST_RUNNER = "django.test.runner.DiscoverRunner"
# CACHES # CACHES
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
@ -29,7 +25,6 @@ CACHES = {
# PASSWORDS # PASSWORDS
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
# TEMPLATES # TEMPLATES
@ -46,7 +41,6 @@ TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
# EMAIL # EMAIL
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
# Your stuff... # Your stuff...

View File

@ -0,0 +1,19 @@
{%- if cookiecutter.cloud_provider == 'AWS' -%}
from storages.backends.s3boto3 import S3Boto3Storage
class StaticRootS3Boto3Storage(S3Boto3Storage):
location = "static"
default_acl = "public-read"
class MediaRootS3Boto3Storage(S3Boto3Storage):
location = "media"
file_overwrite = False
{%- elif cookiecutter.cloud_provider == 'Azure' %}
from storages.backends.azure_storage import AzureStorage
class PublicAzureStorage(AzureStorage):
expiration_secs = None
{%- endif %}

View File

@ -27,8 +27,8 @@ sys.path.append(os.path.join(app_path, "{{ cookiecutter.project_slug }}"))
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use # if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use # mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production" # os.environ["DJANGO_SETTINGS_MODULE"] = "{{ cookiecutter.project_slug }}.settings.prod"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ cookiecutter.project_slug }}.settings.prod")
# This application object is used by any WSGI server configured to use this # This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION # file. This includes Django's development server, if the WSGI_APPLICATION