diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index c17c951ef..0de1077e6 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -57,7 +57,9 @@ Listed in alphabetical order. Andrew Mikhnevich `@zcho`_ Andy Rose Anna Callahan `@jazztpt`_ + Anna Sidwell `@takkaria`_ Antonia Blair `@antoniablair`_ @antoniablairart + Anuj Bansal `@ahhda`_ Arcuri Davide `@dadokkio`_ Areski Belaid `@areski`_ Ashley Camba @@ -70,6 +72,7 @@ Listed in alphabetical order. Bouke Haarsma Brent Payne `@brentpayne`_ @brentpayne Burhan Khalid            `@burhan`_                   @burhan + Carl Johnson `@carlmjohnson`_ @carlmjohnson Catherine Devlin `@catherinedevlin`_ Cédric Gaspoz `@cgaspoz`_ Charlie Smith `@chuckus`_ @@ -78,6 +81,7 @@ Listed in alphabetical order. Chris Franklin `@hairychris`_ Chris Pappalardo `@ChrisPappalardo`_ Christopher Clarke `@chrisdev`_ + Cole Mackenzie `@cmackenzie1`_ Collederas `@Collederas`_ Cristian Vargas `@cdvv7788`_ Cullen Rhodes `@c-rhodes`_ @@ -85,6 +89,7 @@ Listed in alphabetical order. Daniel Hepper `@dhepper`_ @danielhepper Daniele Tricoli `@eriol`_ David Díaz `@ddiazpinto`_ @DavidDiazPinto + Davit Tovmasyan `@davitovmasyan`_ Davur Clementsen `@dsclementsen`_ @davur Delio Castillo `@jangeador`_ @jangeador Denis Orehovsky `@apirobot`_ @@ -106,6 +111,7 @@ Listed in alphabetical order. Irfan Ahmad `@erfaan`_ @erfaan Jan Van Bruggen `@jvanbrug`_ Jens Nilsson `@phiberjenz`_ + Jerome Leclanche `@jleclanche`_ @Adys Jimmy Gitonga `@afrowave`_ @afrowave John Cass `@jcass77`_ @cass_john Julien Almarcha `@sladinji`_ @@ -119,6 +125,7 @@ Listed in alphabetical order. Krzysztof Szumny `@noisy`_ Krzysztof Żuraw `@krzysztofzuraw`_ Leonardo Jimenez `@xpostudio4`_ + Leo Zhou `@glasslion`_ Lin Xianyi `@iynaix`_ Luis Nell `@originell`_ Lukas Klein @@ -140,6 +147,7 @@ Listed in alphabetical order. Pablo `@oubiga`_ Parbhat Puri `@parbhat`_ Peter Bittner `@bittner`_ + Peter Coles `@mrcoles`_ Pierre Chiquet `@pchiquet`_ Raphael Pierzina `@hackebrot`_ Raony Guimarães Corrêa `@raonyguimaraes`_ @@ -162,6 +170,7 @@ Listed in alphabetical order. Tom Atkins `@knitatoms`_ Tom Offermann Travis McNeill `@Travistock`_ @tavistock_esq + Tubo Shi `@Tubo`_ Umair Ashraf `@umrashrf`_ @fabumair Vitaly Babiy Vivian Guillen `@viviangb`_ @@ -169,6 +178,9 @@ Listed in alphabetical order. William Archinal `@archinal`_ Yaroslav Halchenko Denis Bobrov `@delneg`_ + Philipp Matthies `@canonnervio`_ + Vadim Iskuchekov `@Egregors`_ @egregors + Keith Bailey `@keithjeb`_ ========================== ============================ ============== .. _@a7p: https://github.com/a7p @@ -191,6 +203,7 @@ Listed in alphabetical order. .. _@burhan: https://github.com/burhan .. _@c-rhodes: https://github.com/c-rhodes .. _@caffodian: https://github.com/caffodian +.. _@carlmjohnson: https://github.com/carlmjohnson .. _@catherinedevlin: https://github.com/catherinedevlin .. _@ccurvey: https://github.com/ccurvey .. _@cdvv7788: https://github.com/cdvv7788 @@ -198,7 +211,9 @@ Listed in alphabetical order. .. _@chrisdev: https://github.com/chrisdev .. _@ChrisPappalardo: https://github.com/ChrisPappalardo .. _@chuckus: https://github.com/chuckus +.. _@cmackenzie1: https://github.com/cmackenzie1 .. _@Collederas: https://github.com/Collederas +.. _@davitovmasyan: https://github.com/davitovmasyan .. _@ddiazpinto: https://github.com/ddiazpinto .. _@dezoito: https://github.com/dezoito .. _@dhepper: https://github.com/dhepper @@ -214,14 +229,16 @@ Listed in alphabetical order. .. _@garry-cairns: https://github.com/garry-cairns .. _@garrypolley: https://github.com/garrypolley .. _@goldhand: https://github.com/goldhand +.. _@glasslion: https://github.com/glasslion .. _@hackebrot: https://github.com/hackebrot .. _@hairychris: https://github.com/hairychris -.. _@hendrikschneider https://github.com/hendrikschneider +.. _@hendrikschneider: https://github.com/hendrikschneider .. _@hjwp: https://github.com/hjwp .. _@IanLee1521: https://github.com/IanLee1521 .. _@ikkebr: https://github.com/ikkebr .. _@iynaix: https://github.com/iynaix .. _@jazztpt: https://github.com/jazztpt +.. _@jleclanche: https://github.com/jleclanche .. _@juliocc: https://github.com/juliocc .. _@jvanbrug: https://github.com/jvanbrug .. _@ka7eh: https://github.com/ka7eh @@ -256,9 +273,11 @@ Listed in alphabetical order. .. _@ssteinerX: https://github.com/ssteinerx .. _@stepmr: https://github.com/stepmr .. _@suledev: https://github.com/suledev +.. _@takkaria: https://github.com/takkaria .. _@timfreund: https://github.com/timfreund .. _@Travistock: https://github.com/Tavistock .. _@trungdong: https://github.com/trungdong +.. _@Tubo: https://github.com/tubo .. _@viviangb: https://github.com/viviangb .. _@xpostudio4: https://github.com/xpostudio4 .. _@yunti: https://github.com/yunti @@ -278,6 +297,8 @@ Listed in alphabetical order. .. _@delneg: https://github.com/delneg .. _@purplediane: https://github.com/purplediane .. _@umrashrf: https://github.com/umrashrf +.. _@ahhda: https://github.com/ahhda +.. _@keithjeb: https://github.com/keithjeb Special Thanks ~~~~~~~~~~~~~~ diff --git a/cookiecutter.json b/cookiecutter.json index 21a639a3a..b5dda0c70 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,6 +1,6 @@ { "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!", "author_name": "Daniel Roy Greenfeld", "domain_name": "example.com", @@ -18,6 +18,7 @@ "use_pycharm": "n", "use_docker": "n", "postgresql_version": [ + "10.5", "10.4", "10.3", "10.2", diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/docs/deployment-on-heroku.rst b/docs/deployment-on-heroku.rst index d7d95184d..f753aa5a4 100644 --- a/docs/deployment-on-heroku.rst +++ b/docs/deployment-on-heroku.rst @@ -10,6 +10,8 @@ Run these commands to deploy the project to Heroku: heroku create --buildpack https://github.com/heroku/heroku-buildpack-python 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:promote DATABASE_URL diff --git a/docs/deployment-with-docker.rst b/docs/deployment-with-docker.rst index 41a4b454a..0997ef98c 100644 --- a/docs/deployment-with-docker.rst +++ b/docs/deployment-with-docker.rst @@ -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: -* 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*. diff --git a/docs/developing-locally-docker.rst b/docs/developing-locally-docker.rst index 08b25f3bc..895140f9c 100644 --- a/docs/developing-locally-docker.rst +++ b/docs/developing-locally-docker.rst @@ -171,6 +171,16 @@ When developing locally you can go with MailHog_ for email testing provided ``us .. _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`: diff --git a/docs/developing-locally.rst b/docs/developing-locally.rst index 59079453c..3434f68b6 100644 --- a/docs/developing-locally.rst +++ b/docs/developing-locally.rst @@ -9,23 +9,54 @@ Setting Up Development Environment Make sure to have the following on your host: -* virtualenv_; -* pip; -* PostgreSQL. +* Python 3.6 +* PostgreSQL_. +* Redis_, if using Celery First things first. -#. `Create a virtualenv`_. +#. Create a virtualenv: :: -#. Activate the virtualenv you have just created. + $ python3.6 -m venv + +#. Activate the virtualenv you have just created: :: + + $ source /bin/activate #. Install development requirements: :: $ pip install -r requirements/local.txt -#. Create a new PostgreSQL database (note: if this is the first time a database is created on your machine you might need to alter a localhost-related entry in your ``pg_hba.conf`` so as to utilize ``trust`` policy): :: +#. Create a new PostgreSQL database using createdb_: :: - $ createdb + $ createdb -U postgres --password + + .. note:: + + if this is the first time a database is created on your machine you might need an + `initial PostgreSQL set up`_ to allow local connections & set a password for + the ``postgres`` user. The `postgres documentation`_ explains the syntax of the config file + that you need to change. + + +#. Set the environment variables for your database(s): :: + + $ export DATABASE_URL=postgres://postgres:@127.0.0.1:5432/ + # Optional: set broker URL if using Celery + $ export CELERY_BROKER_URL=redis://localhost:6379/0 + + .. note:: + + Check out the :ref:`settings` page for a comprehensive list of the environments variables. + + .. seealso:: + + To help setting up your environment variables, you have a few options: + + * create an ``.env`` file in the root of your project and define all the variables you need in it. + Then you just need to have ``DJANGO_READ_DOT_ENV_FILE=True`` in your machine and all the variables + will be read. + * Use a local environment manager like `direnv`_ #. Apply migrations: :: @@ -35,8 +66,12 @@ First things first. $ python manage.py runserver 0.0.0.0:8000 -.. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/ -.. _`Create a virtualenv`: https://virtualenv.pypa.io/en/stable/userguide/ +.. _PostgreSQL: https://www.postgresql.org/download/ +.. _Redis: https://redis.io/download +.. _createdb: https://www.postgresql.org/docs/current/static/app-createdb.html +.. _initial PostgreSQL set up: http://suite.opengeo.org/docs/latest/dataadmin/pgGettingStarted/firstconnect.html +.. _postgres documentation: https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html +.. _direnv: https://direnv.net/ Setup Email Backend @@ -69,9 +104,7 @@ For instance, one of the packages we depend upon, ``django-allauth`` sends verif Now you have your own mail server running locally, ready to receive whatever you send it. -.. _`Download the latest MailHog release`: https://github.com/mailhog/MailHog/releases -.. _`properly configured`: https://docs.djangoproject.com/en/dev/topics/email/#smtp-backend - +.. _`Download the latest MailHog release`: https://github.com/mailhog/MailHog Console ~~~~~~~ @@ -85,14 +118,21 @@ In production, we have Mailgun_ configured to have your back! .. _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 --------------------------------- -If you’d like to take advantage of live reloading and Sass compilation you can do so with a little bit of preparation_. - - -.. _preparation: https://cookiecutter-django.readthedocs.io/en/latest/live-reloading-and-sass-compilation.html - +If you’d like to take advantage of live reloading and Sass compilation you can do so with a little +bit of preparation, see :ref:`sass-compilation-live-reload`. Summary ------- diff --git a/docs/index.rst b/docs/index.rst index c9f70ab14..5cb07b4b0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,7 +17,7 @@ Contents: developing-locally-docker settings linters - live-reloading-and-sass-compilation + testing deployment-on-pythonanywhere deployment-on-heroku deployment-with-docker diff --git a/docs/installing_postgres.rst b/docs/installing_postgres.rst deleted file mode 100644 index 3b37e8196..000000000 --- a/docs/installing_postgres.rst +++ /dev/null @@ -1,17 +0,0 @@ -PostgreSQL Installation Basics -============================== - -.. index:: pip, virtualenv, PostgreSQL - -The steps below will get you up and running with PostgreSQL. This assumes you have pip and virtualenv_ installed. - -.. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/ - -On Mac - -Install PostgreSQLapp_ from the browser and move PostGresSQL into your applications folder. Then install PostgreSQL from HomeBrew_. - - $ brew install postgres - -.. _PostgreSQLapp: http://postgresapp.com/ -.. _HomeBrew: http://brew.sh/ \ No newline at end of file diff --git a/docs/linters.rst b/docs/linters.rst index feb59d037..e59ff0df7 100644 --- a/docs/linters.rst +++ b/docs/linters.rst @@ -5,9 +5,9 @@ Linters flake8 -------- +------ -To run flake8: +To run flake8: :: $ flake8 @@ -19,7 +19,7 @@ The config for flake8 is located in setup.cfg. It specifies: pylint ------ -This is included in flake8's checks, but you can also run it separately to see a more detailed report: +This is included in flake8's checks, but you can also run it separately to see a more detailed report: :: $ pylint @@ -31,9 +31,9 @@ The config for pylint is located in .pylintrc. It specifies: * max-parents=13 pycodestyle ------ +----------- -This is included in flake8's checks, but you can also run it separately to see a more detailed report: +This is included in flake8's checks, but you can also run it separately to see a more detailed report: :: $ pycodestyle diff --git a/docs/live-reloading-and-sass-compilation.rst b/docs/live-reloading-and-sass-compilation.rst index db8d681f2..a55b4fd8c 100644 --- a/docs/live-reloading-and-sass-compilation.rst +++ b/docs/live-reloading-and-sass-compilation.rst @@ -1,3 +1,5 @@ +.. _sass-compilation-live-reload: + Sass Compilation & Live Reloading ================================= diff --git a/docs/settings.rst b/docs/settings.rst index 6e71a5151..26b161a09 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1,7 +1,7 @@ .. _settings: Settings -========== +======== This project relies extensively on environment settings which **will not work with Apache/mod_wsgi setups**. It has been deployed successfully with both Gunicorn/Nginx and even uWSGI/Nginx. @@ -18,11 +18,10 @@ DJANGO_READ_DOT_ENV_FILE READ_DOT_ENV_FILE False ======================================= =========================== ============================================== ====================================================================== Environment Variable Django Setting Development Default Production Default ======================================= =========================== ============================================== ====================================================================== +DATABASE_URL DATABASES auto w/ Docker; postgres://project_slug w/o raises error DJANGO_ADMIN_URL n/a 'admin/' raises error -DJANGO_CACHES CACHES (default) locmem redis -DJANGO_DATABASES DATABASES (default) See code See code DJANGO_DEBUG DEBUG True False -DJANGO_SECRET_KEY SECRET_KEY !!!SET DJANGO_SECRET_KEY!!! raises error +DJANGO_SECRET_KEY SECRET_KEY auto-generated raises error DJANGO_SECURE_BROWSER_XSS_FILTER SECURE_BROWSER_XSS_FILTER n/a True DJANGO_SECURE_SSL_REDIRECT SECURE_SSL_REDIRECT n/a True DJANGO_SECURE_CONTENT_TYPE_NOSNIFF SECURE_CONTENT_TYPE_NOSNIFF n/a True @@ -41,6 +40,7 @@ The following table lists settings and their defaults for third-party applicatio ======================================= =========================== ============================================== ====================================================================== Environment Variable Django Setting Development Default Production Default ======================================= =========================== ============================================== ====================================================================== +CELERY_BROKER_URL CELERY_BROKER_URL auto w/ Docker; raises error w/o raises error DJANGO_AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID n/a raises error DJANGO_AWS_SECRET_ACCESS_KEY AWS_SECRET_ACCESS_KEY n/a raises error DJANGO_AWS_STORAGE_BUCKET_NAME AWS_STORAGE_BUCKET_NAME n/a raises error @@ -49,8 +49,6 @@ DJANGO_SENTRY_CLIENT SENTRY_CLIENT n/a DJANGO_SENTRY_LOG_LEVEL SENTRY_LOG_LEVEL n/a logging.INFO MAILGUN_API_KEY MAILGUN_ACCESS_KEY n/a raises error MAILGUN_DOMAIN MAILGUN_SENDER_DOMAIN n/a raises error -NEW_RELIC_APP_NAME NEW_RELIC_APP_NAME n/a raises error -NEW_RELIC_LICENSE_KEY NEW_RELIC_LICENSE_KEY n/a raises error ======================================= =========================== ============================================== ====================================================================== -------------------------- diff --git a/docs/testing.rst b/docs/testing.rst new file mode 100644 index 000000000..6ca213883 --- /dev/null +++ b/docs/testing.rst @@ -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 + +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 \ No newline at end of file diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index 0c827acf4..d0c0ba434 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -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 +#. 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 +.. _#1725: https://github.com/pydanny/cookiecutter-django/issues/1725#issuecomment-407493176 diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 33fb79f1b..8480f4363 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -71,6 +71,9 @@ def remove_utility_files(): 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) @@ -118,9 +121,11 @@ def generate_random_string( if using_ascii_letters: symbols += string.ascii_letters if using_punctuation: - symbols += string.punctuation.replace('"', "").replace("'", "").replace( - "\\", "" - ) + 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)]) diff --git a/requirements.txt b/requirements.txt index 7cf49ef6a..dabb5dd91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,10 +4,10 @@ binaryornot==0.4.4 # Code quality # ------------------------------------------------------------------------------ -flake8==3.5.0 +flake8==3.7.6 # Testing # ------------------------------------------------------------------------------ -tox==3.2.1 -pytest==3.8.0 +tox==3.6.1 +pytest==4.3.0 pytest-cookies==0.3.0 diff --git a/{{cookiecutter.project_slug}}/.envs/.local/.django b/{{cookiecutter.project_slug}}/.envs/.local/.django index d94a17e59..2ed5fbf3d 100644 --- a/{{cookiecutter.project_slug}}/.envs/.local/.django +++ b/{{cookiecutter.project_slug}}/.envs/.local/.django @@ -1,6 +1,7 @@ # General # ------------------------------------------------------------------------------ USE_DOCKER=yes +IPYTHONDIR=/app/.ipython # Redis # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 679fb9026..1874e9d92 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -333,6 +333,7 @@ tags [Ss]cripts pyvenv.cfg pip-selfcheck.json +.env {% endif %} ### Project template @@ -342,3 +343,7 @@ MailHog {{ cookiecutter.project_slug }}/media/ .pytest_cache/ + +{% if cookiecutter.use_docker == 'y' %} +.ipython/ +{%- endif %} diff --git a/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile b/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile index c2bf241c7..323e43922 100644 --- a/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile +++ b/{{cookiecutter.project_slug}}/compose/production/caddy/Caddyfile @@ -7,6 +7,7 @@ www.{% raw %}{$DOMAIN_NAME}{% endraw %} { header_upstream Host {host} header_upstream X-Real-IP {remote} header_upstream X-Forwarded-Proto {scheme} + header_upstream X-CSRFToken {~csrftoken} } log stdout errors stdout diff --git a/{{cookiecutter.project_slug}}/config/settings/base.py b/{{cookiecutter.project_slug}}/config/settings/base.py index e4ab28844..950b9ed7f 100644 --- a/{{cookiecutter.project_slug}}/config/settings/base.py +++ b/{{cookiecutter.project_slug}}/config/settings/base.py @@ -209,6 +209,17 @@ FIXTURE_DIRS = ( 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 # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend diff --git a/{{cookiecutter.project_slug}}/config/settings/local.py b/{{cookiecutter.project_slug}}/config/settings/local.py index cbc59bc02..53f7f5e4b 100644 --- a/{{cookiecutter.project_slug}}/config/settings/local.py +++ b/{{cookiecutter.project_slug}}/config/settings/local.py @@ -79,8 +79,10 @@ INSTALLED_APPS += ['django_extensions'] # noqa F405 # Celery # ------------------------------------------------------------------------------ +{% if cookiecutter.use_docker == 'n' -%} # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-always-eager CELERY_TASK_ALWAYS_EAGER = True +{%- endif %} # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-eager-propagates CELERY_TASK_EAGER_PROPAGATES = True diff --git a/{{cookiecutter.project_slug}}/config/settings/production.py b/{{cookiecutter.project_slug}}/config/settings/production.py index 87753b552..e77d4304c 100644 --- a/{{cookiecutter.project_slug}}/config/settings/production.py +++ b/{{cookiecutter.project_slug}}/config/settings/production.py @@ -41,12 +41,8 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure 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 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/ref/settings/#secure-hsts-seconds # 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) # 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) -# 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 # ------------------------------------------------------------------------------ diff --git a/{{cookiecutter.project_slug}}/gulpfile.js b/{{cookiecutter.project_slug}}/gulpfile.js index d2fd36d18..d92678ed2 100644 --- a/{{cookiecutter.project_slug}}/gulpfile.js +++ b/{{cookiecutter.project_slug}}/gulpfile.js @@ -1,173 +1,159 @@ +//////////////////////////////// +// Setup +//////////////////////////////// -//////////////////////////////// - //Setup// -//////////////////////////////// +// Gulp and package +const { src, dest, parallel, series, watch } = require('gulp') +const pjson = require('./package.json') // Plugins -var gulp = require('gulp'), - pjson = require('./package.json'), - gutil = require('gulp-util'), - sass = require('gulp-sass'), - autoprefixer = require('gulp-autoprefixer'), - cssnano = require('gulp-cssnano'), - {% if cookiecutter.custom_bootstrap_compilation == 'y' %} - concat = require('gulp-concat'), - {% endif %} - rename = require('gulp-rename'), - del = require('del'), - plumber = require('gulp-plumber'), - pixrem = require('gulp-pixrem'), - uglify = require('gulp-uglify'), - imagemin = require('gulp-imagemin'), - spawn = require('child_process').spawn, - runSequence = require('run-sequence'), - browserSync = require('browser-sync').create(), - reload = browserSync.reload; - +const autoprefixer = require('autoprefixer') +const browserSync = require('browser-sync').create() +{% if cookiecutter.custom_bootstrap_compilation == 'y' %} +const concat = require('gulp-concat') +{% endif %} +const cssnano = require ('cssnano') +const imagemin = require('gulp-imagemin') +const pixrem = require('pixrem') +const plumber = require('gulp-plumber') +const postcss = require('gulp-postcss') +const reload = browserSync.reload +const rename = require('gulp-rename') +const sass = require('gulp-sass') +const spawn = require('child_process').spawn +const uglify = require('gulp-uglify-es').default // Relative paths function -var pathsConfig = function (appName) { - this.app = "./" + (appName || pjson.name); - var vendorsRoot = 'node_modules/'; +function pathsConfig(appName) { + this.app = `./${pjson.name}` + const vendorsRoot = 'node_modules' return { {% if cookiecutter.custom_bootstrap_compilation == 'y' %} - bootstrapSass: vendorsRoot + '/bootstrap/scss', + bootstrapSass: `${vendorsRoot}/bootstrap/scss`, vendorsJs: [ - vendorsRoot + 'jquery/dist/jquery.slim.js', - vendorsRoot + 'popper.js/dist/umd/popper.js', - vendorsRoot + 'bootstrap/dist/js/bootstrap.js' + `${vendorsRoot}/jquery/dist/jquery.slim.js`, + `${vendorsRoot}/popper.js/dist/umd/popper.js`, + `${vendorsRoot}/bootstrap/dist/js/bootstrap.js`, ], {% endif %} app: this.app, - templates: this.app + '/templates', - css: this.app + '/static/css', - sass: this.app + '/static/sass', - fonts: this.app + '/static/fonts', - images: this.app + '/static/images', - js: this.app + '/static/js' + templates: `${this.app}/templates`, + css: `${this.app}/static/css`, + sass: `${this.app}/static/sass`, + fonts: `${this.app}/static/fonts`, + images: `${this.app}/static/images`, + js: `${this.app}/static/js`, } -}; +} -var paths = pathsConfig(); +var paths = pathsConfig() //////////////////////////////// - //Tasks// +// Tasks //////////////////////////////// // Styles autoprefixing and minification -gulp.task('styles', function() { - return gulp.src(paths.sass + '/project.scss') +function styles() { + 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({ includePaths: [ - {%- if cookiecutter.custom_bootstrap_compilation == 'y' %} + {% if cookiecutter.custom_bootstrap_compilation == 'y' %} paths.bootstrapSass, - {%- endif %} + {% endif %} paths.sass ] }).on('error', sass.logError)) .pipe(plumber()) // Checks for errors - .pipe(autoprefixer({browsers: ['last 2 versions']})) // Adds vendor prefixes - .pipe(pixrem()) // add fallbacks for rem units - .pipe(gulp.dest(paths.css)) + .pipe(postcss(processCss)) + .pipe(dest(paths.css)) .pipe(rename({ suffix: '.min' })) - .pipe(cssnano()) // Minifies the result - .pipe(gulp.dest(paths.css)); -}); + .pipe(postcss(minifyCss)) // Minifies the result + .pipe(dest(paths.css)) +} // Javascript minification -gulp.task('scripts', function() { - return gulp.src(paths.js + '/project.js') +function scripts() { + return src(`${paths.js}/project.js`) .pipe(plumber()) // Checks for errors .pipe(uglify()) // Minifies the js .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 -gulp.task('vendor-scripts', function() { - return gulp.src(paths.vendorsJs) +function vendorScripts() { + return src(paths.vendorsJs) .pipe(concat('vendors.js')) - .pipe(gulp.dest(paths.js)) + .pipe(dest(paths.js)) .pipe(plumber()) // Checks for errors .pipe(uglify()) // Minifies the js .pipe(rename({ suffix: '.min' })) - .pipe(gulp.dest(paths.js)); -}); -{%- endif %} + .pipe(dest(paths.js)) +} +{% endif %} // Image compression -gulp.task('imgCompression', function(){ - return gulp.src(paths.images + '/*') +function imgCompression() { + return src(`${paths.images}/*`) .pipe(imagemin()) // Compresses PNG, JPEG, GIF and SVG images - .pipe(gulp.dest(paths.images)) -}); + .pipe(dest(paths.images)) +} -{%- if cookiecutter.use_docker == 'n' %} // Run django server -gulp.task('runServer', function(cb) { - var cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'}); +function runServer(cb) { + var cmd = spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'}) cmd.on('close', function(code) { - console.log('runServer exited with code ' + code); - cb(code); - }); -}); -{%- endif %} + console.log('runServer exited with code ' + code) + cb(code) + }) +} // Browser sync server for live reload -gulp.task('browserSync', function() { +function initBrowserSync() { browserSync.init( - [paths.css + "/*.css", paths.js + "*.js", paths.templates + '*.html'], { - {%- if cookiecutter.use_docker == 'n' %} - proxy: "localhost:8000" - {% else %} - proxy: "django:8000", - open: false - {%- endif %} - }); -}); + [ + `${paths.css}/*.css`, + `${paths.js}/*.js`, + `${paths.templates}/*.html` + ], { + proxy: "localhost:8000" + } + ) +} // Watch -gulp.task('watch', function() { - gulp.watch(paths.sass + '/*.scss', ['styles']); - gulp.watch(paths.js + '/*.js', ['scripts']).on("change", reload); - gulp.watch(paths.images + '/*', ['imgCompression']); - gulp.watch(paths.templates + '/**/*.html').on("change", reload); +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) +} -}); +// Generate all assets +const generateAssets = parallel( + styles, + scripts, + {% if cookiecutter.custom_bootstrap_compilation == 'y' %}vendorScripts,{% endif %} + imgCompression +) -// Build task -gulp.task('build', - [ - 'styles', - 'scripts', - {%- if cookiecutter.custom_bootstrap_compilation == 'y' %} - 'vendor-scripts', - {%- endif %} - 'imgCompression' - ], - function () { - console.log('Build complete!') -}); +// Set up dev environment +const dev = parallel( + runServer, + initBrowserSync, + watchPaths +) -// Default task -gulp.task('default', function() { - runSequence( - [ - 'styles', - 'scripts', - {%- if cookiecutter.custom_bootstrap_compilation == 'y' %} - 'vendor-scripts', - {%- endif %} - 'imgCompression' - ], - [ - {%- if cookiecutter.use_docker == 'n' %} - 'runServer', - {%- endif %} - 'browserSync', - 'watch' - ] - ); -}); +exports.default = series(generateAssets, dev) +exports["generate-assets"] = generateAssets +exports["dev"] = dev diff --git a/{{cookiecutter.project_slug}}/package.json b/{{cookiecutter.project_slug}}/package.json index 82d9afa0c..ac0c06d1a 100644 --- a/{{cookiecutter.project_slug}}/package.json +++ b/{{cookiecutter.project_slug}}/package.json @@ -6,32 +6,29 @@ {% if cookiecutter.js_task_runner == 'Gulp' -%} {% if cookiecutter.custom_bootstrap_compilation == 'y' -%} "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", - {% 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", "popper.js": "1.14.3", {% 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 %} }, "engines": { - "node": ">=0.8.0" + "node": ">=8" }, + "browserslist": [ + "last 2 versions" + ], "scripts": { {% if cookiecutter.js_task_runner == 'Gulp' -%} "dev": "gulp", diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt index 93418b5f2..e43b70af6 100644 --- a/{{cookiecutter.project_slug}}/requirements/base.txt +++ b/{{cookiecutter.project_slug}}/requirements/base.txt @@ -1,16 +1,16 @@ -pytz==2018.5 # https://github.com/stub42/pytz -python-slugify==1.2.6 # https://github.com/un33k/python-slugify -Pillow==5.2.0 # https://github.com/python-pillow/Pillow +pytz==2018.9 # https://github.com/stub42/pytz +python-slugify==2.0.1 # https://github.com/un33k/python-slugify +Pillow==5.4.1 # https://github.com/python-pillow/Pillow {%- if cookiecutter.use_compressor == "y" %} rcssmin==1.0.6{% if cookiecutter.windows == 'y' %} --install-option="--without-c-extensions"{% endif %} # https://github.com/ndparker/rcssmin {%- 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' %} -whitenoise==4.1 # https://github.com/evansd/whitenoise +whitenoise==4.1.2 # https://github.com/evansd/whitenoise {%- 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" %} -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' %} flower==0.9.2 # https://github.com/mher/flower {%- endif %} @@ -18,16 +18,16 @@ flower==0.9.2 # https://github.com/mher/flower # Django # ------------------------------------------------------------------------------ -django==2.0.8 # 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-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils -django-allauth==0.37.1 # https://github.com/pennersr/django-allauth +django-allauth==0.38.0 # https://github.com/pennersr/django-allauth django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-crispy-forms {%- if cookiecutter.use_compressor == "y" %} django-compressor==2.2 # https://github.com/django-compressor/django-compressor {%- 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 -djangorestframework==3.8.2 # 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 diff --git a/{{cookiecutter.project_slug}}/requirements/local.txt b/{{cookiecutter.project_slug}}/requirements/local.txt index 1b81011f3..a496135b6 100644 --- a/{{cookiecutter.project_slug}}/requirements/local.txt +++ b/{{cookiecutter.project_slug}}/requirements/local.txt @@ -2,29 +2,29 @@ Werkzeug==0.14.1 # https://github.com/pallets/werkzeug ipdb==0.11 # https://github.com/gotcha/ipdb -Sphinx==1.8.0 # https://github.com/sphinx-doc/sphinx +Sphinx==1.8.4 # https://github.com/sphinx-doc/sphinx {%- if cookiecutter.use_docker == 'y' %} psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- else %} -psycopg2-binary==2.7.5 # https://github.com/psycopg/psycopg2 +psycopg2-binary==2.7.7 # https://github.com/psycopg/psycopg2 {%- endif %} # Testing # ------------------------------------------------------------------------------ -mypy==0.620 # https://github.com/python/mypy -pytest==3.8.0 # https://github.com/pytest-dev/pytest -pytest-sugar==0.9.1 # https://github.com/Frozenball/pytest-sugar +mypy==0.670 # https://github.com/python/mypy +pytest==4.2.0 # https://github.com/pytest-dev/pytest +pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar # Code quality # ------------------------------------------------------------------------------ -flake8==3.5.0 # https://github.com/PyCQA/flake8 -coverage==4.5.1 # https://github.com/nedbat/coveragepy +flake8==3.7.5 # https://github.com/PyCQA/flake8 +coverage==4.5.2 # https://github.com/nedbat/coveragepy # Django # ------------------------------------------------------------------------------ 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-extensions==2.1.2 # https://github.com/django-extensions/django-extensions +django-debug-toolbar==1.11 # https://github.com/jazzband/django-debug-toolbar +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 -pytest-django==3.4.3 # https://github.com/pytest-dev/pytest-django +pytest-django==3.4.7 # https://github.com/pytest-dev/pytest-django diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt index 3a138303c..34877e3fd 100644 --- a/{{cookiecutter.project_slug}}/requirements/production.txt +++ b/{{cookiecutter.project_slug}}/requirements/production.txt @@ -2,16 +2,16 @@ -r ./base.txt -gunicorn==19.8.1 # https://github.com/benoitc/gunicorn +gunicorn==19.9.0 # https://github.com/benoitc/gunicorn psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 {%- if cookiecutter.use_whitenoise == 'n' %} Collectfast==0.6.2 # https://github.com/antonagestam/collectfast {%- endif %} {%- 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 %} # Django # ------------------------------------------------------------------------------ django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages -django-anymail[mailgun]==4.2 # https://github.com/anymail/django-anymail \ No newline at end of file +django-anymail[mailgun]==5.0 # https://github.com/anymail/django-anymail \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py index b3f0a388b..570abc125 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/taskapp/celery.py @@ -11,6 +11,11 @@ if not settings.configured: 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): @@ -18,11 +23,6 @@ class CeleryAppConfig(AppConfig): verbose_name = 'Celery Config' 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()] app.autodiscover_tasks(lambda: installed_apps, force=True) diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html index 6865c929d..4470e955a 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html @@ -36,7 +36,7 @@ -
+