Merge branch 'master' into traefik

# Conflicts:
#	{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile
This commit is contained in:
Bruno Alla 2019-03-12 22:05:39 +00:00
commit 43d2252aaa
27 changed files with 297 additions and 145 deletions

View File

@ -57,7 +57,9 @@ Listed in alphabetical order.
Andrew Mikhnevich `@zcho`_ Andrew Mikhnevich `@zcho`_
Andy Rose Andy Rose
Anna Callahan `@jazztpt`_ Anna Callahan `@jazztpt`_
Anna Sidwell `@takkaria`_
Antonia Blair `@antoniablair`_ @antoniablairart Antonia Blair `@antoniablair`_ @antoniablairart
Anuj Bansal `@ahhda`_
Arcuri Davide `@dadokkio`_ Arcuri Davide `@dadokkio`_
Areski Belaid `@areski`_ Areski Belaid `@areski`_
Ashley Camba Ashley Camba
@ -69,7 +71,9 @@ Listed in alphabetical order.
Bo Lopker `@blopker`_ Bo Lopker `@blopker`_
Bouke Haarsma Bouke Haarsma
Brent Payne `@brentpayne`_ @brentpayne Brent Payne `@brentpayne`_ @brentpayne
Bartek `@btknu`
Burhan Khalid            `@burhan`_                   @burhan Burhan Khalid            `@burhan`_                   @burhan
Carl Johnson `@carlmjohnson`_ @carlmjohnson
Catherine Devlin `@catherinedevlin`_ Catherine Devlin `@catherinedevlin`_
Cédric Gaspoz `@cgaspoz`_ Cédric Gaspoz `@cgaspoz`_
Charlie Smith `@chuckus`_ Charlie Smith `@chuckus`_
@ -78,6 +82,7 @@ Listed in alphabetical order.
Chris Franklin `@hairychris`_ Chris Franklin `@hairychris`_
Chris Pappalardo `@ChrisPappalardo`_ Chris Pappalardo `@ChrisPappalardo`_
Christopher Clarke `@chrisdev`_ Christopher Clarke `@chrisdev`_
Cole Mackenzie `@cmackenzie1`_
Collederas `@Collederas`_ Collederas `@Collederas`_
Cristian Vargas `@cdvv7788`_ Cristian Vargas `@cdvv7788`_
Cullen Rhodes `@c-rhodes`_ Cullen Rhodes `@c-rhodes`_
@ -121,6 +126,7 @@ Listed in alphabetical order.
Krzysztof Szumny `@noisy`_ Krzysztof Szumny `@noisy`_
Krzysztof Żuraw `@krzysztofzuraw`_ Krzysztof Żuraw `@krzysztofzuraw`_
Leonardo Jimenez `@xpostudio4`_ Leonardo Jimenez `@xpostudio4`_
Leo Zhou `@glasslion`_
Lin Xianyi `@iynaix`_ Lin Xianyi `@iynaix`_
Luis Nell `@originell`_ Luis Nell `@originell`_
Lukas Klein Lukas Klein
@ -142,6 +148,7 @@ Listed in alphabetical order.
Pablo `@oubiga`_ Pablo `@oubiga`_
Parbhat Puri `@parbhat`_ Parbhat Puri `@parbhat`_
Peter Bittner `@bittner`_ Peter Bittner `@bittner`_
Peter Coles `@mrcoles`_
Pierre Chiquet `@pchiquet`_ Pierre Chiquet `@pchiquet`_
Raphael Pierzina `@hackebrot`_ Raphael Pierzina `@hackebrot`_
Raony Guimarães Corrêa `@raonyguimaraes`_ Raony Guimarães Corrêa `@raonyguimaraes`_
@ -164,6 +171,7 @@ Listed in alphabetical order.
Tom Atkins `@knitatoms`_ Tom Atkins `@knitatoms`_
Tom Offermann Tom Offermann
Travis McNeill `@Travistock`_ @tavistock_esq Travis McNeill `@Travistock`_ @tavistock_esq
Tubo Shi `@Tubo`_
Umair Ashraf `@umrashrf`_ @fabumair Umair Ashraf `@umrashrf`_ @fabumair
Vitaly Babiy Vitaly Babiy
Vivian Guillen `@viviangb`_ Vivian Guillen `@viviangb`_
@ -171,6 +179,9 @@ Listed in alphabetical order.
William Archinal `@archinal`_ William Archinal `@archinal`_
Yaroslav Halchenko Yaroslav Halchenko
Denis Bobrov `@delneg`_ Denis Bobrov `@delneg`_
Philipp Matthies `@canonnervio`_
Vadim Iskuchekov `@Egregors`_ @egregors
Keith Bailey `@keithjeb`_
========================== ============================ ============== ========================== ============================ ==============
.. _@a7p: https://github.com/a7p .. _@a7p: https://github.com/a7p
@ -193,6 +204,7 @@ Listed in alphabetical order.
.. _@burhan: https://github.com/burhan .. _@burhan: https://github.com/burhan
.. _@c-rhodes: https://github.com/c-rhodes .. _@c-rhodes: https://github.com/c-rhodes
.. _@caffodian: https://github.com/caffodian .. _@caffodian: https://github.com/caffodian
.. _@carlmjohnson: https://github.com/carlmjohnson
.. _@catherinedevlin: https://github.com/catherinedevlin .. _@catherinedevlin: https://github.com/catherinedevlin
.. _@ccurvey: https://github.com/ccurvey .. _@ccurvey: https://github.com/ccurvey
.. _@cdvv7788: https://github.com/cdvv7788 .. _@cdvv7788: https://github.com/cdvv7788
@ -200,6 +212,7 @@ Listed in alphabetical order.
.. _@chrisdev: https://github.com/chrisdev .. _@chrisdev: https://github.com/chrisdev
.. _@ChrisPappalardo: https://github.com/ChrisPappalardo .. _@ChrisPappalardo: https://github.com/ChrisPappalardo
.. _@chuckus: https://github.com/chuckus .. _@chuckus: https://github.com/chuckus
.. _@cmackenzie1: https://github.com/cmackenzie1
.. _@Collederas: https://github.com/Collederas .. _@Collederas: https://github.com/Collederas
.. _@davitovmasyan: https://github.com/davitovmasyan .. _@davitovmasyan: https://github.com/davitovmasyan
.. _@ddiazpinto: https://github.com/ddiazpinto .. _@ddiazpinto: https://github.com/ddiazpinto
@ -217,6 +230,7 @@ Listed in alphabetical order.
.. _@garry-cairns: https://github.com/garry-cairns .. _@garry-cairns: https://github.com/garry-cairns
.. _@garrypolley: https://github.com/garrypolley .. _@garrypolley: https://github.com/garrypolley
.. _@goldhand: https://github.com/goldhand .. _@goldhand: https://github.com/goldhand
.. _@glasslion: https://github.com/glasslion
.. _@hackebrot: https://github.com/hackebrot .. _@hackebrot: https://github.com/hackebrot
.. _@hairychris: https://github.com/hairychris .. _@hairychris: https://github.com/hairychris
.. _@hendrikschneider: https://github.com/hendrikschneider .. _@hendrikschneider: https://github.com/hendrikschneider
@ -260,9 +274,11 @@ Listed in alphabetical order.
.. _@ssteinerX: https://github.com/ssteinerx .. _@ssteinerX: https://github.com/ssteinerx
.. _@stepmr: https://github.com/stepmr .. _@stepmr: https://github.com/stepmr
.. _@suledev: https://github.com/suledev .. _@suledev: https://github.com/suledev
.. _@takkaria: https://github.com/takkaria
.. _@timfreund: https://github.com/timfreund .. _@timfreund: https://github.com/timfreund
.. _@Travistock: https://github.com/Tavistock .. _@Travistock: https://github.com/Tavistock
.. _@trungdong: https://github.com/trungdong .. _@trungdong: https://github.com/trungdong
.. _@Tubo: https://github.com/tubo
.. _@viviangb: https://github.com/viviangb .. _@viviangb: https://github.com/viviangb
.. _@xpostudio4: https://github.com/xpostudio4 .. _@xpostudio4: https://github.com/xpostudio4
.. _@yunti: https://github.com/yunti .. _@yunti: https://github.com/yunti
@ -282,6 +298,9 @@ Listed in alphabetical order.
.. _@delneg: https://github.com/delneg .. _@delneg: https://github.com/delneg
.. _@purplediane: https://github.com/purplediane .. _@purplediane: https://github.com/purplediane
.. _@umrashrf: https://github.com/umrashrf .. _@umrashrf: https://github.com/umrashrf
.. _@ahhda: https://github.com/ahhda
.. _@keithjeb: https://github.com/keithjeb
.. _@btknu: https://github.com/btknu
Special Thanks Special Thanks
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~

View File

@ -1,6 +1,6 @@
{ {
"project_name": "My Awesome Project", "project_name": "My Awesome Project",
"project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_') }}", "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_')|replace('.', '_')|trim() }}",
"description": "Behold My Awesome Project!", "description": "Behold My Awesome Project!",
"author_name": "Daniel Roy Greenfeld", "author_name": "Daniel Roy Greenfeld",
"domain_name": "example.com", "domain_name": "example.com",

View File

@ -10,6 +10,8 @@ Run these commands to deploy the project to Heroku:
heroku create --buildpack https://github.com/heroku/heroku-buildpack-python heroku create --buildpack https://github.com/heroku/heroku-buildpack-python
heroku addons:create heroku-postgresql:hobby-dev heroku addons:create heroku-postgresql:hobby-dev
# On Windows use double quotes for the time zone, e.g.
# heroku pg:backups schedule --at "02:00 America/Los_Angeles" DATABASE_URL
heroku pg:backups schedule --at '02:00 America/Los_Angeles' DATABASE_URL heroku pg:backups schedule --at '02:00 America/Los_Angeles' DATABASE_URL
heroku pg:promote DATABASE_URL heroku pg:promote DATABASE_URL
@ -45,7 +47,6 @@ Run these commands to deploy the project to Heroku:
git push heroku master git push heroku master
heroku run python manage.py migrate
heroku run python manage.py createsuperuser heroku run python manage.py createsuperuser
heroku run python manage.py collectstatic --no-input heroku run python manage.py collectstatic --no-input

View File

@ -59,7 +59,7 @@ SSL (Secure Sockets Layer) is a standard security technology for establishing an
It is always better to deploy a site behind HTTPS and will become crucial as the web services extend to the IoT (Internet of Things). For this reason, we have set up a number of security defaults to help make your website secure: It is always better to deploy a site behind HTTPS and will become crucial as the web services extend to the IoT (Internet of Things). For this reason, we have set up a number of security defaults to help make your website secure:
* If you are not using a subdomain of the domain name set in the project, then remember to put the your staging/production IP address in the ``DJANGO_ALLOWED_HOSTS`` environment variable (see :ref:`settings`) before you deploy your website. Failure to do this will mean you will not have access to your website through the HTTP protocol. * If you are not using a subdomain of the domain name set in the project, then remember to put your staging/production IP address in the ``DJANGO_ALLOWED_HOSTS`` environment variable (see :ref:`settings`) before you deploy your website. Failure to do this will mean you will not have access to your website through the HTTP protocol.
* Access to the Django admin is set up by default to require HTTPS in production or once *live*. * Access to the Django admin is set up by default to require HTTPS in production or once *live*.

View File

@ -171,6 +171,16 @@ When developing locally you can go with MailHog_ for email testing provided ``us
.. _Mailhog: https://github.com/mailhog/MailHog/ .. _Mailhog: https://github.com/mailhog/MailHog/
.. _`CeleryTasks`:
Celery tasks in local development
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When not using docker Celery tasks are set to run in Eager mode, so that a full stack is not needed. When using docker the task
scheduler will be used by default.
If you need tasks to be executed on the main thread during development set CELERY_TASK_ALWAYS_EAGER = True in config/settings/local.py.
Possible uses could be for testing, or ease of profiling with DJDT.
.. _`CeleryFlower`: .. _`CeleryFlower`:

View File

@ -29,7 +29,7 @@ First things first.
#. Create a new PostgreSQL database using createdb_: :: #. Create a new PostgreSQL database using createdb_: ::
$ createdb <what you've entered as the project_slug at setup stage> $ createdb <what you have entered as the project_slug at setup stage> -U postgres --password <password>
.. note:: .. note::
@ -118,6 +118,16 @@ In production, we have Mailgun_ configured to have your back!
.. _Mailgun: https://www.mailgun.com/ .. _Mailgun: https://www.mailgun.com/
Celery
------
If the project is configured to use Celery as a task scheduler then by default tasks are set to run on the main thread
when developing locally. If you have the appropriate setup on your local machine then set
CELERY_TASK_ALWAYS_EAGER = False
in /config/settings/local.py
Sass Compilation & Live Reloading Sass Compilation & Live Reloading
--------------------------------- ---------------------------------

View File

@ -17,6 +17,7 @@ Contents:
developing-locally-docker developing-locally-docker
settings settings
linters linters
testing
deployment-on-pythonanywhere deployment-on-pythonanywhere
deployment-on-heroku deployment-on-heroku
deployment-with-docker deployment-with-docker

56
docs/testing.rst Normal file
View File

@ -0,0 +1,56 @@
.. _testing:
Testing
========
We encourage users to build application tests. As best practice, this should be done immediately after documentation of the application being built, before starting on any coding.
Pytest
------
This project uses the Pytest_, a framework for easily building simple and scalable tests.
After you have set up to `develop locally`_, run the following commands to make sure the testing environment is ready: ::
$ pytest
You will get a readout of the `users` app that has already been set up with tests. If you do not want to run the `pytest` on the entire project, you can target a particular app by typing in its location: ::
$ pytest <path-to-app-in-project/app>
If you set up your project to `develop locally with docker`_, run the following command: ::
$ docker-compose -f local.yml run django pytest
Targetting particular apps for testing in ``docker`` follows a similar pattern as previously shown above.
Coverage
--------
You should build your tests to provide the highest level of **code coverage**. You can run the ``pytest`` with code ``coverage`` by typing in the following command: ::
$ docker-compose -f local.yml run django coverage run -m pytest
Once the tests are complete, in order to see the code coverage, run the following command: ::
$ docker-compose -f local.yml run django coverage report
.. note::
At the root of the project folder, you will find the `pytest.ini` file. You can use this to customize_ the ``pytest`` to your liking.
There is also the `.coveragerc`. This is the configuration file for the ``coverage`` tool. You can find out more about `configuring`_ ``coverage``.
.. seealso::
For unit tests, run: ::
$ python manage.py test
Since this is a fresh install, and there are no tests built using the Python `unittest`_ library yet, you should get feedback that says there were no tests carried out.
.. _Pytest: https://docs.pytest.org/en/latest/example/simple.html
.. _develop locally: ../developing-locally.rst
.. _develop locally with docker: ..../developing-locally-docker.rst
.. _customize: https://docs.pytest.org/en/latest/customize.html
.. _unittest: https://docs.python.org/3/library/unittest.html#module-unittest
.. _configuring: https://coverage.readthedocs.io/en/v4.5.x/config.html

View File

@ -9,4 +9,7 @@ This page contains some advice about errors and problems commonly encountered du
#. Internal server error on user registration: make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain #. Internal server error on user registration: make sure you have configured the mail backend (e.g. Mailgun) by adding the API key and sender domain
#. New apps not getting created in project root: This is the expected behavior, because cookiecutter-django does not change the way that django startapp works, you'll have to fix this manually (see `#1725`_)
.. _#528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373 .. _#528: https://github.com/pydanny/cookiecutter-django/issues/528#issuecomment-212650373
.. _#1725: https://github.com/pydanny/cookiecutter-django/issues/1725#issuecomment-407493176

View File

@ -71,6 +71,9 @@ def remove_utility_files():
def remove_heroku_files(): def remove_heroku_files():
file_names = ["Procfile", "runtime.txt", "requirements.txt"] file_names = ["Procfile", "runtime.txt", "requirements.txt"]
for file_name in file_names: 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) os.remove(file_name)

View File

@ -4,10 +4,11 @@ binaryornot==0.4.4
# Code quality # Code quality
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
flake8==3.6.0 flake8==3.7.6
# Testing # Testing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
tox==3.5.3 tox==3.6.1
pytest==3.9.3 pytest==4.3.0
pytest-cookies==0.3.0 pytest-cookies==0.3.0
pyyaml==3.13

View File

@ -1,6 +1,7 @@
import os import os
import re import re
import sh import sh
import yaml
import pytest import pytest
from binaryornot.check import is_binary from binaryornot.check import is_binary
@ -85,3 +86,19 @@ def test_flake8_compliance(cookies):
sh.flake8(str(result.project)) sh.flake8(str(result.project))
except sh.ErrorReturnCode as e: except sh.ErrorReturnCode as e:
pytest.fail(e) pytest.fail(e)
def test_travis_invokes_pytest(cookies, context):
context.update({"use_travisci": "y"})
result = cookies.bake(extra_context=context)
assert result.exit_code == 0
assert result.exception is None
assert result.project.basename == context["project_slug"]
assert result.project.isdir()
with open(f'{result.project}/.travis.yml', 'r') as travis_yml:
try:
assert yaml.load(travis_yml)['script'] == ['pytest']
except yaml.YAMLError as e:
pytest.fail(e)

View File

@ -3,10 +3,11 @@
USE_DOCKER=yes USE_DOCKER=yes
IPYTHONDIR=/app/.ipython IPYTHONDIR=/app/.ipython
{%- if cookiecutter.use_celery == 'y' %}
# Redis # Redis
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
REDIS_URL=redis://redis:6379/0 REDIS_URL=redis://redis:6379/0
{% if cookiecutter.use_celery == 'y' %}
# Celery # Celery
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -9,3 +9,7 @@ before_install:
language: python language: python
python: python:
- "3.6" - "3.6"
install:
- pip install -r requirements/local.txt
script:
- "pytest"

View File

@ -1,3 +1,4 @@
release: python manage.py migrate
web: gunicorn config.wsgi:application web: gunicorn config.wsgi:application
{% if cookiecutter.use_celery == "y" -%} {% if cookiecutter.use_celery == "y" -%}
worker: celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info worker: celery worker --app={{cookiecutter.project_slug}}.taskapp --loglevel=info

View File

@ -5,8 +5,10 @@ set -o pipefail
set -o nounset set -o nounset
{% if cookiecutter.use_celery == 'y' %}
# N.B. If only .env files supported variable expansion... # N.B. If only .env files supported variable expansion...
export CELERY_BROKER_URL="${REDIS_URL}" export CELERY_BROKER_URL="${REDIS_URL}"
{% endif %}
if [ -z "${POSTGRES_USER}" ]; then if [ -z "${POSTGRES_USER}" ]; then
base_postgres_image_default_user='postgres' base_postgres_image_default_user='postgres'

View File

@ -209,6 +209,17 @@ FIXTURE_DIRS = (
str(APPS_DIR.path('fixtures')), str(APPS_DIR.path('fixtures')),
) )
# SECURITY
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
SESSION_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
CSRF_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
SECURE_BROWSER_XSS_FILTER = True
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
X_FRAME_OPTIONS = 'DENY'
# EMAIL # EMAIL
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend

View File

@ -76,8 +76,10 @@ INSTALLED_APPS += ['django_extensions'] # noqa F405
# Celery # Celery
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
{% if cookiecutter.use_docker == 'n' -%}
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-always-eager # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-always-eager
CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_ALWAYS_EAGER = True
{%- endif %}
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-eager-propagates # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-eager-propagates
CELERY_TASK_EAGER_PROPAGATES = True CELERY_TASK_EAGER_PROPAGATES = True

View File

@ -41,12 +41,8 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True)
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
SESSION_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure
CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
CSRF_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/dev/topics/security/#ssl-https # https://docs.djangoproject.com/en/dev/topics/security/#ssl-https
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds
# TODO: set this to 60 seconds first and then to 518400 once you prove the former works # TODO: set this to 60 seconds first and then to 518400 once you prove the former works
@ -57,10 +53,6 @@ SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS
SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True) SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True)
# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff # https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
SECURE_CONTENT_TYPE_NOSNIFF = env.bool('DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) SECURE_CONTENT_TYPE_NOSNIFF = env.bool('DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True)
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
SECURE_BROWSER_XSS_FILTER = True
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
X_FRAME_OPTIONS = 'DENY'
# STORAGES # STORAGES
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -1,63 +1,70 @@
////////////////////////////////
// Setup
////////////////////////////////
//////////////////////////////// // Gulp and package
//Setup// const { src, dest, parallel, series, watch } = require('gulp')
//////////////////////////////// const pjson = require('./package.json')
// Plugins // Plugins
var gulp = require('gulp'), const autoprefixer = require('autoprefixer')
pjson = require('./package.json'), const browserSync = require('browser-sync').create()
gutil = require('gulp-util'), {% if cookiecutter.custom_bootstrap_compilation == 'y' %}
sass = require('gulp-sass'), const concat = require('gulp-concat')
autoprefixer = require('gulp-autoprefixer'), {% endif %}
cssnano = require('gulp-cssnano'), const cssnano = require ('cssnano')
{% if cookiecutter.custom_bootstrap_compilation == 'y' %} const imagemin = require('gulp-imagemin')
concat = require('gulp-concat'), const pixrem = require('pixrem')
{% endif %} const plumber = require('gulp-plumber')
rename = require('gulp-rename'), const postcss = require('gulp-postcss')
del = require('del'), const reload = browserSync.reload
plumber = require('gulp-plumber'), const rename = require('gulp-rename')
pixrem = require('gulp-pixrem'), const sass = require('gulp-sass')
uglify = require('gulp-uglify'), const spawn = require('child_process').spawn
imagemin = require('gulp-imagemin'), const uglify = require('gulp-uglify-es').default
spawn = require('child_process').spawn,
runSequence = require('run-sequence'),
browserSync = require('browser-sync').create(),
reload = browserSync.reload;
// Relative paths function // Relative paths function
var pathsConfig = function (appName) { function pathsConfig(appName) {
this.app = "./" + (appName || pjson.name); this.app = `./${pjson.name}`
var vendorsRoot = 'node_modules/'; const vendorsRoot = 'node_modules'
return { return {
{% if cookiecutter.custom_bootstrap_compilation == 'y' %} {% if cookiecutter.custom_bootstrap_compilation == 'y' %}
bootstrapSass: vendorsRoot + '/bootstrap/scss', bootstrapSass: `${vendorsRoot}/bootstrap/scss`,
vendorsJs: [ vendorsJs: [
vendorsRoot + 'jquery/dist/jquery.slim.js', `${vendorsRoot}/jquery/dist/jquery.slim.js`,
vendorsRoot + 'popper.js/dist/umd/popper.js', `${vendorsRoot}/popper.js/dist/umd/popper.js`,
vendorsRoot + 'bootstrap/dist/js/bootstrap.js' `${vendorsRoot}/bootstrap/dist/js/bootstrap.js`,
], ],
{% endif %} {% endif %}
app: this.app, app: this.app,
templates: this.app + '/templates', templates: `${this.app}/templates`,
css: this.app + '/static/css', css: `${this.app}/static/css`,
sass: this.app + '/static/sass', sass: `${this.app}/static/sass`,
fonts: this.app + '/static/fonts', fonts: `${this.app}/static/fonts`,
images: this.app + '/static/images', images: `${this.app}/static/images`,
js: this.app + '/static/js' js: `${this.app}/static/js`,
} }
}; }
var paths = pathsConfig(); var paths = pathsConfig()
//////////////////////////////// ////////////////////////////////
//Tasks// // Tasks
//////////////////////////////// ////////////////////////////////
// Styles autoprefixing and minification // Styles autoprefixing and minification
gulp.task('styles', function() { function styles() {
return gulp.src(paths.sass + '/project.scss') var processCss = [
autoprefixer(), // adds vendor prefixes
pixrem(), // add fallbacks for rem units
]
var minifyCss = [
cssnano({ preset: 'default' }) // minify result
]
return src(`${paths.sass}/project.scss`)
.pipe(sass({ .pipe(sass({
includePaths: [ includePaths: [
{% if cookiecutter.custom_bootstrap_compilation == 'y' %} {% if cookiecutter.custom_bootstrap_compilation == 'y' %}
@ -67,72 +74,86 @@ gulp.task('styles', function() {
] ]
}).on('error', sass.logError)) }).on('error', sass.logError))
.pipe(plumber()) // Checks for errors .pipe(plumber()) // Checks for errors
.pipe(autoprefixer({browsers: ['last 2 versions']})) // Adds vendor prefixes .pipe(postcss(processCss))
.pipe(pixrem()) // add fallbacks for rem units .pipe(dest(paths.css))
.pipe(gulp.dest(paths.css))
.pipe(rename({ suffix: '.min' })) .pipe(rename({ suffix: '.min' }))
.pipe(cssnano()) // Minifies the result .pipe(postcss(minifyCss)) // Minifies the result
.pipe(gulp.dest(paths.css)); .pipe(dest(paths.css))
}); }
// Javascript minification // Javascript minification
gulp.task('scripts', function() { function scripts() {
return gulp.src(paths.js + '/project.js') return src(`${paths.js}/project.js`)
.pipe(plumber()) // Checks for errors .pipe(plumber()) // Checks for errors
.pipe(uglify()) // Minifies the js .pipe(uglify()) // Minifies the js
.pipe(rename({ suffix: '.min' })) .pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest(paths.js)); .pipe(dest(paths.js))
}); }
{% if cookiecutter.custom_bootstrap_compilation == 'y' %} {% if cookiecutter.custom_bootstrap_compilation == 'y' %}
// Vendor Javascript minification // Vendor Javascript minification
gulp.task('vendor-scripts', function() { function vendorScripts() {
return gulp.src(paths.vendorsJs) return src(paths.vendorsJs)
.pipe(concat('vendors.js')) .pipe(concat('vendors.js'))
.pipe(gulp.dest(paths.js)) .pipe(dest(paths.js))
.pipe(plumber()) // Checks for errors .pipe(plumber()) // Checks for errors
.pipe(uglify()) // Minifies the js .pipe(uglify()) // Minifies the js
.pipe(rename({ suffix: '.min' })) .pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest(paths.js)); .pipe(dest(paths.js))
}); }
{% endif %} {% endif %}
// Image compression // Image compression
gulp.task('imgCompression', function(){ function imgCompression() {
return gulp.src(paths.images + '/*') return src(`${paths.images}/*`)
.pipe(imagemin()) // Compresses PNG, JPEG, GIF and SVG images .pipe(imagemin()) // Compresses PNG, JPEG, GIF and SVG images
.pipe(gulp.dest(paths.images)) .pipe(dest(paths.images))
}); }
// Run django server // Run django server
gulp.task('runServer', function(cb) { function runServer(cb) {
var cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'}); var cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'})
cmd.on('close', function(code) { cmd.on('close', function(code) {
console.log('runServer exited with code ' + code); console.log('runServer exited with code ' + code)
cb(code); cb(code)
}); })
}); }
// Browser sync server for live reload // Browser sync server for live reload
gulp.task('browserSync', function() { function initBrowserSync() {
browserSync.init( browserSync.init(
[paths.css + "/*.css", paths.js + "*.js", paths.templates + '*.html'], { [
proxy: "localhost:8000" `${paths.css}/*.css`,
}); `${paths.js}/*.js`,
}); `${paths.templates}/*.html`
], {
proxy: "localhost:8000"
}
)
}
// Watch // Watch
gulp.task('watch', function() { function watchPaths() {
watch(`${paths.sass}/*.scss`, styles)
watch(`${paths.templates}/**/*.html`).on("change", reload)
watch([`${paths.js}/*.js`, `!${paths.js}/*.min.js`], scripts).on("change", reload)
}
gulp.watch(paths.sass + '/*.scss', ['styles']); // Generate all assets
gulp.watch(paths.js + '/*.js', ['scripts']).on("change", reload); const generateAssets = parallel(
gulp.watch(paths.images + '/*', ['imgCompression']); styles,
gulp.watch(paths.templates + '/**/*.html').on("change", reload); scripts,
{% if cookiecutter.custom_bootstrap_compilation == 'y' %}vendorScripts,{% endif %}
imgCompression
)
}); // Set up dev environment
const dev = parallel(
runServer,
initBrowserSync,
watchPaths
)
// Default task exports.default = series(generateAssets, dev)
gulp.task('default', function() { exports["generate-assets"] = generateAssets
runSequence(['styles', 'scripts', {% if cookiecutter.custom_bootstrap_compilation == 'y' %}'vendor-scripts', {% endif %}'imgCompression'], ['runServer', 'browserSync', 'watch']); exports["dev"] = dev
});

View File

@ -6,32 +6,29 @@
{% if cookiecutter.js_task_runner == 'Gulp' -%} {% if cookiecutter.js_task_runner == 'Gulp' -%}
{% if cookiecutter.custom_bootstrap_compilation == 'y' -%} {% if cookiecutter.custom_bootstrap_compilation == 'y' -%}
"bootstrap": "4.1.1", "bootstrap": "4.1.1",
{% endif -%}
"browser-sync": "^2.14.0",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^5.0.0",
{% if cookiecutter.custom_bootstrap_compilation == 'y' -%}
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
{% endif -%}
"gulp-cssnano": "^2.1.2",
"gulp-imagemin": "^4.1.0",
"gulp-pixrem": "^1.0.0",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^3.0.0",
"gulp-util": "^3.0.7",
{% if cookiecutter.custom_bootstrap_compilation == 'y' -%}
"jquery": "3.3.1", "jquery": "3.3.1",
"popper.js": "1.14.3", "popper.js": "1.14.3",
{% endif -%} {% endif -%}
"run-sequence": "^2.1.1" "autoprefixer": "^9.4.7",
"browser-sync": "^2.14.0",
"cssnano": "^4.1.10",
"gulp": "^4.0.0",
"gulp-imagemin": "^5.0.3",
"gulp-plumber": "^1.2.1",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^4.0.2",
"gulp-uglify-es": "^1.0.4",
"pixrem": "^5.0.0"
{%- endif %} {%- endif %}
}, },
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=8"
}, },
"browserslist": [
"last 2 versions"
],
"scripts": { "scripts": {
{% if cookiecutter.js_task_runner == 'Gulp' -%} {% if cookiecutter.js_task_runner == 'Gulp' -%}
"dev": "gulp" "dev": "gulp"

View File

@ -1,16 +1,16 @@
pytz==2018.7 # https://github.com/stub42/pytz pytz==2018.9 # https://github.com/stub42/pytz
python-slugify==1.2.6 # https://github.com/un33k/python-slugify python-slugify==2.0.1 # https://github.com/un33k/python-slugify
Pillow==5.3.0 # https://github.com/python-pillow/Pillow Pillow==5.4.1 # https://github.com/python-pillow/Pillow
{%- if cookiecutter.use_compressor == "y" %} {%- if cookiecutter.use_compressor == "y" %}
rcssmin==1.0.6{% if cookiecutter.windows == 'y' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin rcssmin==1.0.6{% if cookiecutter.windows == 'y' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin
{%- endif %} {%- endif %}
argon2-cffi==18.3.0 # https://github.com/hynek/argon2_cffi argon2-cffi==19.1.0 # https://github.com/hynek/argon2_cffi
{%- if cookiecutter.use_whitenoise == 'y' %} {%- if cookiecutter.use_whitenoise == 'y' %}
whitenoise==4.1 # https://github.com/evansd/whitenoise whitenoise==4.1.2 # https://github.com/evansd/whitenoise
{%- endif %} {%- endif %}
redis>=2.10.5 # https://github.com/antirez/redis redis>=2.10.6, < 3 # pyup: < 3 # https://github.com/antirez/redis
{%- if cookiecutter.use_celery == "y" %} {%- if cookiecutter.use_celery == "y" %}
celery==4.2.1 # pyup: <5.0 # https://github.com/celery/celery celery==4.2.1 # pyup: < 5.0 # https://github.com/celery/celery
{%- if cookiecutter.use_docker == 'y' %} {%- if cookiecutter.use_docker == 'y' %}
flower==0.9.2 # https://github.com/mher/flower flower==0.9.2 # https://github.com/mher/flower
{%- endif %} {%- endif %}
@ -18,7 +18,7 @@ flower==0.9.2 # https://github.com/mher/flower
# Django # Django
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
django==2.0.9 # pyup: < 2.1 # https://www.djangoproject.com/ django==2.0.13 # pyup: < 2.1 # https://www.djangoproject.com/
django-environ==0.4.5 # https://github.com/joke2k/django-environ django-environ==0.4.5 # https://github.com/joke2k/django-environ
django-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils django-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils
django-allauth==0.38.0 # https://github.com/pennersr/django-allauth django-allauth==0.38.0 # https://github.com/pennersr/django-allauth
@ -26,8 +26,8 @@ django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-cris
{%- if cookiecutter.use_compressor == "y" %} {%- if cookiecutter.use_compressor == "y" %}
django-compressor==2.2 # https://github.com/django-compressor/django-compressor django-compressor==2.2 # https://github.com/django-compressor/django-compressor
{%- endif %} {%- endif %}
django-redis==4.9.0 # https://github.com/niwinz/django-redis django-redis==4.10.0 # https://github.com/niwinz/django-redis
# Django REST Framework # Django REST Framework
djangorestframework==3.9.0 # https://github.com/encode/django-rest-framework djangorestframework==3.9.1 # 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

View File

@ -2,29 +2,29 @@
Werkzeug==0.14.1 # https://github.com/pallets/werkzeug Werkzeug==0.14.1 # https://github.com/pallets/werkzeug
ipdb==0.11 # https://github.com/gotcha/ipdb ipdb==0.11 # https://github.com/gotcha/ipdb
Sphinx==1.8.1 # https://github.com/sphinx-doc/sphinx Sphinx==1.8.4 # https://github.com/sphinx-doc/sphinx
{%- if cookiecutter.use_docker == 'y' %} {%- if cookiecutter.use_docker == 'y' %}
psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
{%- else %} {%- else %}
psycopg2-binary==2.7.5 # https://github.com/psycopg/psycopg2 psycopg2-binary==2.7.7 # https://github.com/psycopg/psycopg2
{%- endif %} {%- endif %}
# Testing # Testing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
mypy==0.641 # https://github.com/python/mypy mypy==0.670 # https://github.com/python/mypy
pytest==3.9.3 # https://github.com/pytest-dev/pytest pytest==4.2.0 # https://github.com/pytest-dev/pytest
pytest-sugar==0.9.1 # https://github.com/Frozenball/pytest-sugar pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar
# Code quality # Code quality
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
flake8==3.6.0 # https://github.com/PyCQA/flake8 flake8==3.7.5 # https://github.com/PyCQA/flake8
coverage==4.5.1 # https://github.com/nedbat/coveragepy coverage==4.5.2 # https://github.com/nedbat/coveragepy
# Django # Django
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
factory-boy==2.11.1 # https://github.com/FactoryBoy/factory_boy factory-boy==2.11.1 # https://github.com/FactoryBoy/factory_boy
django-debug-toolbar==1.10.1 # https://github.com/jazzband/django-debug-toolbar django-debug-toolbar==1.11 # https://github.com/jazzband/django-debug-toolbar
django-extensions==2.1.3 # https://github.com/django-extensions/django-extensions django-extensions==2.1.5 # https://github.com/django-extensions/django-extensions
django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin django-coverage-plugin==1.6.0 # https://github.com/nedbat/django_coverage_plugin
pytest-django==3.4.3 # https://github.com/pytest-dev/pytest-django pytest-django==3.4.7 # https://github.com/pytest-dev/pytest-django

View File

@ -8,10 +8,10 @@ psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
Collectfast==0.6.2 # https://github.com/antonagestam/collectfast Collectfast==0.6.2 # https://github.com/antonagestam/collectfast
{%- endif %} {%- endif %}
{%- if cookiecutter.use_sentry == "y" %} {%- if cookiecutter.use_sentry == "y" %}
raven==6.9.0 # https://github.com/getsentry/raven-python raven==6.10.0 # https://github.com/getsentry/raven-python
{%- endif %} {%- endif %}
# Django # Django
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
django-anymail[mailgun]==4.3 # https://github.com/anymail/django-anymail django-anymail[mailgun]==5.0 # https://github.com/anymail/django-anymail

View File

@ -1 +1 @@
python-3.6.6 python-3.6.8

View File

@ -11,6 +11,11 @@ if not settings.configured:
app = Celery('{{cookiecutter.project_slug}}') app = Celery('{{cookiecutter.project_slug}}')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
class CeleryAppConfig(AppConfig): class CeleryAppConfig(AppConfig):
@ -18,11 +23,6 @@ class CeleryAppConfig(AppConfig):
verbose_name = 'Celery Config' verbose_name = 'Celery Config'
def ready(self): def ready(self):
# Using a string here means the worker will not have to
# pickle the object when using Windows.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')
installed_apps = [app_config.name for app_config in apps.get_app_configs()] installed_apps = [app_config.name for app_config in apps.get_app_configs()]
app.autodiscover_tasks(lambda: installed_apps, force=True) app.autodiscover_tasks(lambda: installed_apps, force=True)

View File

@ -36,7 +36,7 @@
<body> <body>
<div class="m-b-1"> <div class="mb-1">
<nav class="navbar navbar-expand-md navbar-light bg-light"> <nav class="navbar navbar-expand-md navbar-light bg-light">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>